From d7ace517f6b5cbc1ce1298688427d5a1c42c955b Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 17:34:41 +0200
Subject: [PATCH 01/17] docs: add the markdown transforms the Node-RED library
needs to become docs
The library pages were authored as Nunjucks-in-markdown against src/node-red/'s
own shortcodes, so they need more repair than the guides in content-guides/
(authored as MDC) and different repair from the FlowFuse/flowfuse docs.
Reuses joinHtmlBlocks and convertCallouts from docs-markdown rather than
reimplementing them: the library uses the same note/warning shortcodes and the
same column-0 raw-HTML style. Adds what is specific to it.
renderFlow becomes the existing render-flow MDC component, reading its JSON from
a fenced block instead of an inline module script. This has to run before
convertCallouts, which finishes by stripping every remaining Nunjucks tag and
would otherwise delete the flow diagrams and their JSON without a trace. A test
pins that ordering.
Node-RED's own mustaches are the other trap. The pages document {{ msg.payload }}
and guarded it with Nunjucks raw blocks; MDC would read the same braces as a Vue
binding. Each mustache moves into an inline code span, which is how the rest of
the docs already write msg.payload. Fenced blocks are skipped wholesale, so flow
JSON carrying format:"{{msg.payload}}" keeps its exact bytes.
Links to /node-red/ itself are deliberately not rewritten. That page stays on
Eleventy as a marketing page; only the documentation below it moves.
---
nuxt/lib/library-markdown.mjs | 108 +++++++++++++++++++++++++++++
nuxt/lib/library-markdown.test.mjs | 103 +++++++++++++++++++++++++++
2 files changed, 211 insertions(+)
create mode 100644 nuxt/lib/library-markdown.mjs
create mode 100644 nuxt/lib/library-markdown.test.mjs
diff --git a/nuxt/lib/library-markdown.mjs b/nuxt/lib/library-markdown.mjs
new file mode 100644
index 0000000000..afd0c406f5
--- /dev/null
+++ b/nuxt/lib/library-markdown.mjs
@@ -0,0 +1,108 @@
+// Transforms for the Node-RED library pages as they move from Eleventy into the docs
+// content tree.
+//
+// These pages were authored as Nunjucks-in-markdown against `src/node-red/`'s own
+// shortcodes, so they need more repair than the guides in nuxt/content-guides/ (authored
+// as MDC from the start) and different repair from the FlowFuse/flowfuse docs. The
+// overlap with docs-markdown.mjs is deliberate and one-way: `joinHtmlBlocks` and
+// `convertCallouts` are imported from there rather than reimplemented, because the
+// library uses the same `{% note %}` / `{% warning %}` shortcodes and the same
+// column-0 raw-HTML style.
+//
+// ORDER MATTERS. `convertCallouts` finishes by stripping every remaining `{% ... %}`
+// tag, which would silently swallow the 77 `{% renderFlow %}` blocks and every
+// `{% raw %}` guard on the page. Everything that needs to read a Nunjucks tag therefore
+// runs before it. `processLibraryMarkdown` encodes that order; call it rather than
+// composing these by hand.
+//
+// Kept free of Nuxt and filesystem imports so `node --test` can exercise it directly.
+
+import { convertCallouts, joinHtmlBlocks } from './docs-markdown.mjs'
+
+/**
+ * `{% renderFlow %}` becomes the `render-flow` MDC component.
+ *
+ * The Eleventy shortcode took the flow JSON as its paired body and an optional pixel
+ * height as its first argument, then emitted a div plus an inline module script. The Vue
+ * component reads the JSON out of a fenced code block in its slot instead, so the body
+ * moves into a ```json fence. Run this BEFORE convertCallouts.
+ */
+export function convertRenderFlow (content) {
+ return content.replace(
+ /\{%-?\s*renderFlow(?:\s+(\d+))?\s*-?%\}\r?\n([\s\S]*?)\r?\n\{%-?\s*endrenderFlow\s*-?%\}/g,
+ (_, height, body) => {
+ const attrs = height ? `{:height="${height}"}` : ''
+ return `::render-flow${attrs}\n\`\`\`json\n${body.trim()}\n\`\`\`\n::`
+ }
+ )
+}
+
+/**
+ * Resolve the `{{ meta.title }}` interpolation the pages use for their own H1.
+ *
+ * Eleventy rendered frontmatter into the body, so 70 of these pages open with
+ * `# {{meta.title}}` rather than a literal heading. @nuxt/content does not interpolate
+ * frontmatter into markdown, and the docs page component renders the H1 from the body,
+ * so the title has to be written out. Whitespace inside the braces varies in the source.
+ */
+export function resolveTitleInterpolation (content, title) {
+ if (!title) return content
+ return content.replace(/\{\{-?\s*meta\.title\s*-?\}\}/g, title)
+}
+
+/**
+ * Neutralise `{% raw %}` guards and the Node-RED mustaches they protect.
+ *
+ * These pages document Node-RED, whose own templating uses `{{ msg.payload }}`. Under
+ * Eleventy the author wrapped those in `{% raw %}` so Nunjucks left them alone. MDC
+ * treats `{{ }}` in a text node as a Vue binding, so simply dropping the guard would
+ * turn documented syntax into a render error or an empty string. Each mustache is moved
+ * into an inline code span, which MDC does not interpolate, and which is how the rest of
+ * the docs already write `msg.payload`.
+ *
+ * A mustache already inside a code span or a fenced block is left alone: fences are
+ * skipped wholesale, and an existing backtick immediately around the match is honoured.
+ */
+export function protectMustaches (content) {
+ const segments = content.split(/(^```[\s\S]*?^```)/gm)
+
+ return segments.map((segment, i) => {
+ // Odd indices are the captured fenced blocks. Node-RED syntax shown in a fence is
+ // already safe and must keep its exact bytes.
+ if (i % 2 === 1) return segment
+
+ return segment
+ .replace(/\{%-?\s*(?:end)?raw\s*-?%\}\r?\n?/g, '')
+ .replace(/(`?)(\{\{[^{}\n]*\}\})(`?)/g, (whole, before, mustache, after) =>
+ (before && after) ? whole : `\`${mustache}\``)
+ }).join('')
+}
+
+/**
+ * Rewrite the library's own links and asset paths to their new home.
+ *
+ * `/node-red/` itself is deliberately NOT rewritten: it stays on Eleventy as a marketing
+ * page, so a link to it must keep pointing there. Only paths with something after the
+ * prefix move. The FlowFuse node pages split off to their own docs section, so they are
+ * matched first.
+ */
+export function rewriteLibraryPaths (content) {
+ return content
+ .replace(/(["'(])\/node-red\/flowfuse\//g, '$1/docs/flowfuse-nodes/')
+ .replace(/(["'(])\/node-red\/(?=[a-z0-9])/g, '$1/docs/node-red/')
+}
+
+/**
+ * Everything, in the one order that works.
+ *
+ * `title` comes from the page's own frontmatter and is only used to resolve the H1.
+ */
+export function processLibraryMarkdown (content, { title } = {}) {
+ let out = convertRenderFlow(content)
+ out = resolveTitleInterpolation(out, title)
+ out = protectMustaches(out)
+ out = rewriteLibraryPaths(out)
+ out = joinHtmlBlocks(out)
+ // Last: this strips every Nunjucks tag still standing.
+ return convertCallouts(out)
+}
diff --git a/nuxt/lib/library-markdown.test.mjs b/nuxt/lib/library-markdown.test.mjs
new file mode 100644
index 0000000000..31c8d1135e
--- /dev/null
+++ b/nuxt/lib/library-markdown.test.mjs
@@ -0,0 +1,103 @@
+import assert from 'node:assert/strict'
+import test from 'node:test'
+
+import {
+ convertRenderFlow,
+ processLibraryMarkdown,
+ protectMustaches,
+ resolveTitleInterpolation,
+ rewriteLibraryPaths,
+} from './library-markdown.mjs'
+
+test('renderFlow becomes the render-flow component with its height carried over', () => {
+ const out = convertRenderFlow('{% renderFlow 300 %}\n[{"id":"a"}]\n{% endrenderFlow %}')
+
+ assert.equal(out, '::render-flow{:height="300"}\n```json\n[{"id":"a"}]\n```\n::')
+})
+
+test('renderFlow without a height omits the attribute so the component default applies', () => {
+ const out = convertRenderFlow('{% renderFlow %}\n[]\n{% endrenderFlow %}')
+
+ assert.equal(out, '::render-flow\n```json\n[]\n```\n::')
+})
+
+test('several flows on one page each convert', () => {
+ const page = [
+ '{% renderFlow 200 %}', '[1]', '{% endrenderFlow %}',
+ 'prose',
+ '{% renderFlow 400 %}', '[2]', '{% endrenderFlow %}',
+ ].join('\n')
+
+ const out = convertRenderFlow(page)
+
+ assert.equal(out.match(/::render-flow/g).length, 2)
+ assert.match(out, /height="200"/)
+ assert.match(out, /height="400"/)
+})
+
+test('a flow survives the full pipeline, which otherwise strips Nunjucks tags', () => {
+ // The regression this guards: convertCallouts ends by removing every remaining
+ // {% ... %}, so a flow converted too late is silently deleted along with its JSON.
+ const out = processLibraryMarkdown('{% renderFlow 300 %}\n[{"id":"a"}]\n{% endrenderFlow %}')
+
+ assert.match(out, /::render-flow\{:height="300"\}/)
+ assert.match(out, /\[\{"id":"a"\}\]/)
+})
+
+test('the H1 interpolation resolves to the frontmatter title', () => {
+ assert.equal(resolveTitleInterpolation('# {{meta.title}}', 'Using Modbus'), '# Using Modbus')
+ assert.equal(resolveTitleInterpolation('# {{ meta.title }}', 'Using Modbus'), '# Using Modbus')
+})
+
+test('a page with no title leaves the interpolation alone rather than emptying the heading', () => {
+ assert.equal(resolveTitleInterpolation('# {{ meta.title }}'), '# {{ meta.title }}')
+})
+
+test('Node-RED mustaches are moved into code spans so MDC cannot bind them', () => {
+ assert.equal(protectMustaches('set it to {{ msg.payload }} here'),
+ 'set it to `{{ msg.payload }}` here')
+})
+
+test('a mustache already in a code span is not double-wrapped', () => {
+ assert.equal(protectMustaches('use `{{ msg.payload }}` here'), 'use `{{ msg.payload }}` here')
+})
+
+test('raw guards are removed but what they protected is kept', () => {
+ const out = protectMustaches('{% raw %}\n{{ payload }}\n{% endraw %}')
+
+ assert.equal(out, '`{{ payload }}`\n')
+})
+
+test('a fenced block keeps its exact bytes, mustaches and all', () => {
+ const page = ['before {{ a }}', '```json', '{"format":"{{msg.payload}}"}', '```', 'after {{ b }}'].join('\n')
+
+ const out = protectMustaches(page)
+
+ assert.match(out, /\{"format":"\{\{msg\.payload\}\}"\}/)
+ assert.match(out, /before `\{\{ a \}\}`/)
+ assert.match(out, /after `\{\{ b \}\}`/)
+})
+
+test('a flow JSON fence is not mangled by mustache protection', () => {
+ // ui-text nodes carry format:"{{msg.payload}}" inside the flow, so the two transforms
+ // meet on every dashboard example in the library.
+ const out = processLibraryMarkdown(
+ '{% renderFlow 300 %}\n[{"type":"ui-text","format":"{{msg.payload}}"}]\n{% endrenderFlow %}'
+ )
+
+ assert.match(out, /"format":"\{\{msg\.payload\}\}"/)
+ assert.doesNotMatch(out, /`\{\{msg\.payload\}\}`/)
+})
+
+test('library links move to the docs tree', () => {
+ assert.equal(rewriteLibraryPaths('[x](/node-red/protocol/modbus/)'), '[x](/docs/node-red/protocol/modbus/)')
+ assert.equal(rewriteLibraryPaths('src="/node-red/hardware/images/a.png"'), 'src="/docs/node-red/hardware/images/a.png"')
+})
+
+test('the FlowFuse node pages go to their own docs section, not under node-red', () => {
+ assert.equal(rewriteLibraryPaths('[x](/node-red/flowfuse/mcp/)'), '[x](/docs/flowfuse-nodes/mcp/)')
+})
+
+test('a link to /node-red/ itself is left alone, because that page stays on Eleventy', () => {
+ assert.equal(rewriteLibraryPaths('[what is it](/node-red/)'), '[what is it](/node-red/)')
+})
From c34ba12f766f56d2e01f3d8a27883c24f25f8b7e Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 17:36:07 +0200
Subject: [PATCH 02/17] docs: replace the '100+ Tutorials (2026)' page with a
Node-RED section index
/node-red/learn/ was a table of contents wearing a title that had stopped being
true. The count only held if you included the pages that are FlowFuse's own
product reference, and the year was written in December 2025 pointing forward and
never revisited, so the page has advertised '(2026)' with no content change behind
it for most of a year.
It becomes the index of a new 'Node-RED' documentation group instead, ranked last
(navGroupOrder 7, after Contributing) because this is material a reader reaches
for when they want more depth, not a step on the way in. The page now says that
plainly, points at nodered.org as the primary source for the project's own manual,
and names what this section covers that nodered.org does not: per-node reference
on the web, and connecting Node-RED to specific databases, protocols and hardware.
Both callouts route a reader who is actually setting FlowFuse up to
/docs/user/ rather than deeper into Node-RED background.
---
nuxt/content-guides/node-red/README.md | 80 ++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
create mode 100644 nuxt/content-guides/node-red/README.md
diff --git a/nuxt/content-guides/node-red/README.md b/nuxt/content-guides/node-red/README.md
new file mode 100644
index 0000000000..e93360e573
--- /dev/null
+++ b/nuxt/content-guides/node-red/README.md
@@ -0,0 +1,80 @@
+---
+title: "Using Node-RED"
+navTitle: "Using Node-RED"
+navOrder: 1
+navGroup: "Node-RED"
+navGroupOrder: 7
+meta:
+ description: "Reference for working with Node-RED itself: connecting it to databases, protocols, hardware and notification services, and what each core node does."
+ keywords: node-red, node red, core nodes, database, hardware, protocols, industrial iot
+---
+
+# Using Node-RED
+
+Node-RED is the open-source runtime FlowFuse runs, governs and scales. This section is
+reference material about Node-RED itself: how to connect it to a database, speak an
+industrial protocol, run it on a particular piece of hardware, and what each core node
+does.
+
+It is here to answer a question you already have. It is not a path into FlowFuse, and it
+does not need to be read in order. If you are setting FlowFuse up, start with
+[Using FlowFuse](/docs/user/) instead.
+
+::callout{icon="i-lucide-book-open"}
+**The Node-RED project's own documentation is the primary source.** Installing Node-RED,
+the editor, the message model, writing functions and the API reference all live at
+[nodered.org/docs](https://nodered.org/docs/). This section covers the ground that
+documentation does not: per-node reference on the web, and connecting Node-RED to
+specific databases, protocols, hardware and services.
+::
+
+## Getting started
+
+Installing Node-RED, finding your way around the editor, and the basics of shaping a
+message. [Getting started](/docs/node-red/getting-started/)
+
+## Core nodes
+
+What each node in the default palette does, and a worked reason to reach for it.
+[Core nodes](/docs/node-red/core-nodes/)
+
+## Communication protocols
+
+Modbus, OPC UA, MQTT, AMQP, WebSocket and LwM2M, for talking to controllers and edge
+equipment. [Communication protocols](/docs/node-red/protocol/)
+
+## Databases
+
+Reading and writing SQL, NoSQL and time-series databases, one guide per database.
+[Databases](/docs/node-red/database/)
+
+## Integration technologies
+
+Webhooks, REST APIs and GraphQL, for wiring Node-RED to the rest of your systems.
+[Integration technologies](/docs/node-red/integration-technologies/)
+
+## Notification services
+
+Sending alerts by email, Telegram and Discord.
+[Notification services](/docs/node-red/notification/)
+
+## Hardware
+
+Running Node-RED on a Raspberry Pi, a Siemens IoT2050, and other industrial gateways.
+[Hardware](/docs/node-red/hardware/)
+
+## Peripheral devices
+
+Webcams, barcode scanners and other things plugged into the machine Node-RED runs on.
+[Peripheral devices](/docs/node-red/peripheral/)
+
+## Reference
+
+[Terminology](/docs/node-red/terminology/) for the words that come up in Node-RED
+discussions, and [keyboard shortcuts](/docs/node-red/keyboard/) for the editor.
+
+::callout{icon="i-lucide-arrow-right"}
+**Running Node-RED for an organisation?** Access control, version history, deployment
+across environments and remote instance management are what FlowFuse adds on top.
+See [Using FlowFuse](/docs/user/).
+::
From e41f03717b7dafe70677741152a01ac7d3819f5d Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 17:49:00 +0200
Subject: [PATCH 03/17] docs: move the Node-RED library into /docs and off
Eleventy
The library was the last tenant of the old Eleventy documentation portal.
Commit 951aee084 (2024-10-01, "Rename handbook layout to documentation") put
/docs, /handbook and /node-red on one layouts/documentation.njk with one Algolia
index, told apart only by a `nav` key. The handbook left for Nuxt in June and the
docs in July, and the library was left behind on a layout still carrying
`class="handbook ff-prose"`, handbookBreadcrumbs, and a `nav == 'docs'` branch
nothing reached any more. This finishes that migration rather than starting one.
Reference belongs in documentation, and this is reference. /node-red/ itself does
not move: it is a marketing page (layout: page, "What is Node-RED?", FAQ
structured data) and it stays on Eleventy. Everything below it becomes /docs.
The new Node-RED group is ranked last, after Contributing. This is depth a reader
reaches for once they have a question, not a step on the way in, and the nine
header slots that pointed at our own node reference (mirrored verbatim in the
footer, rendered on every page of the site) come out. Those pages are product
reference and move into FlowFuse User Manuals as /docs/flowfuse-nodes/ instead.
The core-node pages were never files. Eleventy paginated them out of
coreNodes.json and each one fetched its help from raw.githubusercontent.com
during the build. They are now generated from that catalogue plus a committed
snapshot of the upstream help, so a deploy never depends on a third-party URL
being reachable, and scripts/refresh_core_node_help.mjs updates the snapshot
deliberately. Generation writes into nuxt/content/docs, which is gitignored and
wiped per sync, so a build never dirties the tree.
Generating instead of scraping surfaced four pages that have been shipping an
empty "Node Documentation" section: coreNodes.json asked for `mqtt-in` and
`mqtt-out` where upstream now says `mqtt in` and `mqtt out`, and both UDP nodes
asked for `upd`, a typo. The old xpath found nothing and rendered the page
anyway. The catalogue is corrected, the lookup tolerates the separator, and a
miss now throws.
Redirects are written one path at a time rather than as a /node-red/** splat,
because a splat would swallow the marketing page too, and would turn every stale
deep link into a 301 towards a 404. A dev-only middleware rule lets those
requests reach Nitro instead of being proxied to an Eleventy tree that no longer
has the pages.
Retires documentation.njk, left-nav.njk, learning-resources-nav.njk,
hardware.njk, docs-banner.njk, core-node-docs.njk and lib/core-node-docs.js.
---
.eleventy.js | 5 -
lib/core-node-docs.js | 27 ---
nuxt/content-guides/flowfuse-nodes/README.md | 24 ++
.../flowfuse-nodes/ai/README.md | 19 ++
.../flowfuse-nodes}/ai/depth-estimation.md | 15 +-
.../ai/image-classification.md | 15 +-
.../flowfuse-nodes}/ai/llm-nodes.md | 16 +-
.../flowfuse-nodes}/ai/object-detection.md | 15 +-
.../content-guides/flowfuse-nodes}/ai/onxx.md | 17 +-
.../flowfuse-nodes/edge/README.md | 40 ++++
.../flowfuse-nodes}/edge/cip-suite.md | 28 +--
.../edge/images/opcua/endpoint-add-new.png | Bin
.../images/opcua/endpoint-subscriptions.png | Bin
.../endpoint-verification-secure-error.png | Bin
...dpoint-verification-user-access-denied.png | Bin
.../images/opcua/endpoint-verification.png | Bin
...ple-all-attributes-on-rfid-scan-result.png | Bin
.../edge/images/opcua/node-palette.png | Bin
.../images/opcua/nodeid-as-browse-path.png | Bin
.../edge/images/opcua/opcu-client2-read.png | Bin
.../images/opcua/opcua-client2-monitor.png | Bin
.../opcua/read-showing-endpoint-field.png | Bin
.../use-node-browser-to-select-node-id.png | Bin
.../edge/images/rtsp/rtsp-config-node.png | Bin
.../edge/images/rtsp/rtsp-edge-catalog.png | Bin
.../flowfuse-nodes}/edge/modbus.md | 88 ++++---
.../flowfuse-nodes}/edge/opcua.md | 159 ++++++++-----
.../flowfuse-nodes}/edge/rtsp.md | 38 ++--
.../flowfuse-nodes/flowfuse-tables/README.md | 12 +-
.../flowfuse-nodes}/flowfuse-tables/query.md | 27 +--
.../flowfuse-nodes/hub/README.md | 33 +--
.../flowfuse-nodes}/hub/redis.md | 30 +--
.../images/ff-expert-at-platform-level.png | Bin
.../images/ff-expert-in-editor.png | Bin
.../images/ff-expert-opened.png | Bin
.../images/ff-expert-opned-platform.png | Bin
.../images/mcp-in-flowfuse.png | Bin
.../flowfuse-nodes/mcp/README.md | 17 +-
.../flowfuse-nodes}/mcp/mcp-prompt.md | 23 +-
.../flowfuse-nodes}/mcp/mcp-resource.md | 15 +-
.../flowfuse-nodes}/mcp/mcp-response.md | 11 +-
.../flowfuse-nodes}/mcp/mcp-tool.md | 15 +-
.../flowfuse-nodes/mqtt/README.md | 16 +-
.../flowfuse-nodes}/mqtt/mqtt-in.md | 11 +-
.../flowfuse-nodes}/mqtt/mqtt-out.md | 9 +-
.../core-nodes/images/batch-example1.png | Bin
.../core-nodes/images/batch-example2.png | Bin
.../core-nodes/images/batch-example3.webm | Bin
.../core-nodes/images/batch-example4.png | Bin
.../core-nodes/images/batch-example5.png | Bin
.../images/change-data-transform.png | Bin
.../core-nodes/images/change-default.png | Bin
.../images/change-message-enrich.png | Bin
.../images/change-message-filter.png | Bin
.../images/change-message-format.png | Bin
.../images/change-rename-property.png | Bin
.../images/comment-node-full-text.png | Bin
.../core-nodes/images/debug-copy-path.webm | Bin
.../core-nodes/images/debug-copy-value.webm | Bin
.../core-nodes/images/debug-examples.png | Bin
.../core-nodes/images/debug-panel.png | Bin
.../core-nodes/images/debug-pin-open.webm | Bin
.../core-nodes/images/delay-node-2.png | Bin
.../images/inject-config-string.png | Bin
.../node-red/core-nodes/images/link-node.png | Bin
.../core-nodes/images/list-nodes-unused.png | Bin
.../core-nodes/images/mqtt-config.png | Bin
.../core-nodes/images/range-node2.png | Bin
.../core-nodes/images/spawn-mode-exec.png | Bin
.../core-nodes/images/status-node-output.png | Bin
.../core-nodes/images/status-node.png | Bin
.../core-nodes/images/switch-example-2.png | Bin
.../images/system-info-installed-nodes.webm | Bin
.../core-nodes/images/wired-up-exec-node.png | Bin
.../node-red/database/README.md | 30 +++
.../node-red/database/dynamodb.md | 41 ++--
.../node-red/database/firebase.md | 23 +-
.../database/images/config-connection.png | Bin
.../database/images/config-database.png | Bin
.../database/images/data_in_influx.webm | Bin
...amodb-data-structure-node-red-flowfuse.png | Bin
.../images/dynamodb-flowfuse-iam-node-red.png | Bin
.../flowfuse-dynamodb-aws-setup-node-red.png | Bin
.../images/influxdb-node-red-cta-1.png | Bin
.../database/images/mysql-node-config.png | Bin
.../database/images/mysql-with-node-red.png | Bin
.../node-red-dynamodb-flow-flowfuse.png | Bin
.../node-red-dynamodb-get-item-flowfuse.png | Bin
.../node-red-dynamodb-query-flowfuse.png | Bin
.../node-red-dynamodb-scan-flowfuse.png | Bin
.../database/images/node-red-dynamodb.png | Bin
.../database/images/node-red-influxdb.png | Bin
.../images/postgresql_with_node-red.png | Bin
...h_node-red_Inventory_management_system.png | Bin
.../postgresql_with_node-red_delete_data.png | Bin
.../postgresql_with_node-red_drop_tables.png | Bin
.../images/postgresql_with_node-red_form1.png | Bin
.../images/postgresql_with_node-red_form2.png | Bin
.../images/postgresql_with_node-red_form3.png | Bin
.../postgresql_with_node-red_insert_data.png | Bin
.../postgresql_with_node-red_pgconfig1.png | Bin
.../postgresql_with_node-red_pgconfig2.png | Bin
.../postgresql_with_node-red_retrive_data.png | Bin
.../postgresql_with_node-red_update_data.png | Bin
.../postgresql_with_nodered_create_table.png | Bin
...resql_with_nodred_environment_variable.png | Bin
..._nodred_environment_variable_ff_editor.png | Bin
.../timescaledb-with-node-red-hypertables.png | Bin
.../images/timescaledb-with-node-red.png | Bin
.../database/images/using-Mongo-with-nr.png | Bin
...-node-red-annotomy-of-mongodb-document.png | Bin
...th-node-red-change-node-to-delete-data.png | Bin
...th-node-red-change-node-to-insert-data.png | Bin
...th-node-red-change-node-to-update-data.png | Bin
...red-crm-system-node-red-dashboard-view.png | Bin
...with-node-red-crm-system-node-red-flow.png | Bin
...mongodb-with-node-red-delete-data-form.png | Bin
...ith-node-red-flowfuse-instance-setting.png | Bin
...g-mongodb-with-node-red-friend-id-node.png | Bin
...mongodb-with-node-red-insert-data-form.png | Bin
...b-with-node-red-mongodb-deleteone-flow.png | Bin
...d-mongodb-deleteone-node-configuration.png | Bin
...ongodb-with-node-red-mongodb-drop-flow.png | Bin
...de-red-mongodb-drop-node-configuration.png | Bin
...ongodb-with-node-red-mongodb-find-flow.png | Bin
...de-red-mongodb-find-node-configuration.png | Bin
...b-with-node-red-mongodb-insertone-flow.png | Bin
...d-mongodb-insertone-node-configuration.png | Bin
...-mongodb-node-connection-configuration.png | Bin
...-red-mongodb-update-node-configuration.png | Bin
...b-with-node-red-mongodb-updateone-flow.png | Bin
...ing-mongodb-with-node-red-table-widget.png | Bin
...mongodb-with-node-red-update-data-form.png | Bin
...db-with-node-red-environment-variables.png | Bin
...imescaledb-with-node-red-inject-node-1.png | Bin
...imescaledb-with-node-red-inject-node-2.png | Bin
...-postgresql-config-node-connection-tab.png | Bin
...ed-postgresql-config-node-security-tab.png | Bin
.../node-red/database/influxdb.md | 29 +--
.../node-red/database/mongodb.md | 13 +-
.../node-red/database/mysql.md | 21 +-
.../node-red/database/postgresql.md | 11 +-
.../node-red/database/redis.md | 21 +-
.../node-red/database/sqlite.md | 15 +-
.../node-red/database/timescaledb.md | 67 ++++--
.../node-red/getting-started/README.md | 22 ++
.../node-red/getting-started/date-and-time.md | 36 +--
.../node-red/getting-started/editor/README.md | 12 +-
.../node-red/getting-started/editor/header.md | 15 +-
.../editor/images/adding-nodes-to-group.webm | Bin
.../editor/images/creating-group.webm | Bin
.../editor/images/deleting-wires.webm | Bin
.../editor/images/deploy-button-active.png | Bin
.../editor/images/deploy-button-inactive.png | Bin
.../editor/images/deploy-button-options.png | Bin
.../detaching-nodes-while-keeping-wires.webm | Bin
.../editor/images/editor-flow-tabs.png | Bin
.../editor/images/editor-workspace.png | Bin
.../images/environment-variables-options.png | Bin
.../editor/images/group-description.png | Bin
.../editor/images/group-level-env.png | Bin
.../editor/images/group-merging-groups.webm | Bin
.../editor/images/group-properties-edit.png | Bin
.../editor/images/group-properties.png | Bin
.../getting-started/editor/images/group.png | Bin
.../images/header-flowfuse-instance.png | Bin
.../getting-started/editor/images/header.png | Bin
.../editor/images/lasso-tool.webm | Bin
.../images/main-menu-arrange-option.png | Bin
.../images/main-menu-configuration-nodes.png | Bin
.../editor/images/main-menu-edit-option.png | Bin
.../editor/images/main-menu-export-local.png | Bin
.../editor/images/main-menu-export-team.png | Bin
.../editor/images/main-menu-export.png | Bin
.../editor/images/main-menu-flows.png | Bin
.../editor/images/main-menu-groups.png | Bin
.../editor/images/main-menu-import.png | Bin
.../images/main-menu-search-flow-option.png | Bin
.../editor/images/main-menu-search-tab.png | Bin
.../editor/images/main-menu-settings.png | Bin
.../editor/images/main-menu-subflows.png | Bin
.../editor/images/main-menu-view-option.png | Bin
.../editor/images/moving-multiple-wires.webm | Bin
.../editor/images/moving-wire.webm | Bin
.../editor/images/mqtt-in-node-status.png | Bin
.../editor/images/node-add-config-node.png | Bin
.../editor/images/node-appearance-tab.png | Bin
.../editor/images/node-buttons.png | Bin
.../editor/images/node-description-tab.png | Bin
.../images/node-edit-properties-tab.png | Bin
.../editor/images/node-enable-option.png | Bin
.../editor/images/node-help.png | Bin
.../editor/images/node-indicating-error.png | Bin
...ating-how-many-nodes-using-config-node.png | Bin
.../node-indicating-undeployed-changes.png | Bin
.../editor/images/node-input-ouput-port.png | Bin
.../editor/images/node-quick-add-dialog.webm | Bin
.../editor/images/node-red-editor-palette.png | Bin
.../editor/images/node-red-editor-window.png | Bin
.../images/node-red-export-local-library.png | Bin
.../editor/images/node-red-node.png | Bin
.../images/node-red-palette-category.png | Bin
.../node-red-palette-collapse-expand.png | Bin
.../images/node-red-palette-export-scope.png | Bin
.../editor/images/node-red-palette-json.png | Bin
.../editor/images/node-red-palette-search.png | Bin
.../editor/images/node-red-palette-toggle.png | Bin
.../node-red-pallete-manger-nodes-option.png | Bin
.../editor/images/node-red-sidebar.png | Bin
.../editor/images/node-wire.png | Bin
...pallete-manger-filter-and-sort-options.png | Bin
.../images/removing-node-from-group.webm | Bin
.../images/removing-nodes-from-group.webm | Bin
.../removing-nodes-while-keeping-wires.webm | Bin
.../images/selecting-connected-nodes.webm | Bin
.../selecting-downward-connected-nodes.webm | Bin
.../editor/images/selecting-flows.webm | Bin
.../selecting-upward-connected-nodes.webm | Bin
.../editor/images/slicing-wires.webm | Bin
.../editor/images/subflow-appearance-tab.png | Bin
.../subflow-converting-nodes-to-subflow.webm | Bin
.../images/subflow-custom-properties.png | Bin
.../editor/images/subflow-delete-option.png | Bin
.../images/subflow-editing-properties.webm | Bin
.../editor/images/subflow-input-output.png | Bin
.../editor/images/subflow-module-tab.png | Bin
.../editor/images/subflow-node.png | Bin
.../images/subflow-property-description.png | Bin
.../editor/images/subflow-status-node.png | Bin
.../editor/images/subflow-ui-preview.png | Bin
.../editor/images/subflow-window.png | Bin
.../editor/images/wiring-nodes-together.webm | Bin
.../editor/images/workspace-add-flow.png | Bin
.../images/workspace-delete-flow-dialog.png | Bin
.../editor/images/workspace-delete-flow.png | Bin
.../images/workspace-disable-enable-flow.png | Bin
.../images/workspace-enable-disable-flow.png | Bin
.../editor/images/workspace-flow-edit.png | Bin
.../images/workspace-navigator-tool.webm | Bin
.../images/workspace-reordering-flow.webm | Bin
.../editor/images/workspace-search-tool.png | Bin
.../editor/images/workspace-view-tool.png | Bin
.../getting-started/editor/palette.md | 11 +-
.../getting-started/editor/sidebar.md | 11 +-
.../getting-started/editor/workspace.md | 53 +++--
.../images/adding-breakpoints.webm | Bin
.../images/breakpoint-debugging.webm | Bin
.../images/breakpoints-section.png | Bin
.../images/debugger-controls.png | Bin
.../images/disable-enable-button.png | Bin
.../images/disabling-breakpoints.webm | Bin
...educing-data-with-node-red-change-node.png | Bin
...ith-node-red-join-node-calculating-avg.png | Bin
...with-node-red-join-node-combining-node.png | Bin
...-reducing-data-with-node-red-sort-node.png | Bin
...educing-data-with-node-red-switch-node.png | Bin
...g-mapping-sorting-reducing-switch-node.png | Bin
...mapping-sorting-reducing-with-node-red.png | Bin
.../images/if-else-with-node-red.png | Bin
.../images/message-section.png | Bin
.../images/mistake-1-solution.webm | Bin
.../getting-started/images/mistake1.webm | Bin
.../images/node-red-data-types.webm | Bin
.../images/node-red-instance-update.png | Bin
.../images/node-red-message-passing.webm | Bin
.../images/proceeding-further-execution.webm | Bin
.../images/removing-breakpoints.webm | Bin
.../images/schedule-update.png | Bin
.../images/update-node-red-cta-1.png | Bin
.../getting-started/library/README.md | 15 +-
.../images/installing-node-red-node.webm | Bin
.../getting-started/node-red-android.md | 10 +-
.../getting-started/node-red-messages.md | 22 +-
.../node-red/getting-started/node-red-port.md | 10 +-
.../getting-started/programming/README.md | 21 ++
.../programming/data-tranformation.md | 43 ++--
.../programming/debugging-flows.md | 29 +--
.../getting-started/programming/if-else.md | 39 ++--
.../getting-started/programming/loop.md | 57 +++--
.../node-red/getting-started/string.md | 87 ++++---
.../getting-started/update-node-red.md | 14 +-
.../node-red/hardware/README.md | 25 ++
.../node-red/hardware/armxy-bl340.md | 116 ++++++++++
.../hardware/images/GRV-R7-MM1001-10.jpeg | Bin
.../node-red/hardware/images/armxy-bl340.jpg | Bin
...onfiguration-dailog-with-one-time-code.png | Bin
...-robustel-eg5120-installation-complete.png | Bin
.../flowfuse-on-robustel-eg5120-node-red.png | Bin
.../images/flowfuse-on-siemens-iot2050.png | Bin
...up-node-red-on-raspberry-pi-debug-node.png | Bin
...spberry-pi-device-configuration-dialog.png | Bin
...up-node-red-on-raspberry-pi-dht-sensor.png | Bin
...p-node-red-on-raspberry-pi-inject-node.png | Bin
...to-setup-node-red-on-raspberry-pi-pins.png | Bin
.../how-to-setup-node-red-on-raspberry-pi.png | Bin
.../hardware/images/raspberry-4-editor.png | Bin
.../hardware/images/raspberry-pi-4-b.png | Bin
.../hardware/images/raspberry-pi-4.png | Bin
.../raspberry-pi-5-config-before-flash.png | Bin
.../images/raspberry-pi-5-device-agent.png | Bin
.../hardware/images/raspberry-pi-5-editor.png | Bin
.../images/raspberry-pi-5-flash-os.png | Bin
.../hardware/images/raspberry-pi-5.png | Bin
.../hardware/images/raspberrypi-5.png | Bin
.../hardware/images/robustel-eg5120.png | Bin
.../hardware/images/siemens-iot-2050.jpg | Bin
.../hardware/images/status-flowfuse.png | Bin
.../hardware/images/systemctl-status.png | Bin
.../opto-22-groove-rio-7-mm2001-10.md | 51 +++--
.../node-red/hardware/raspberry-pi-4.md | 117 ++++++++++
.../node-red/hardware/raspberry-pi-5.md | 117 ++++++++++
.../node-red/hardware/robustel-eg5120.md | 97 ++++++++
.../node-red/hardware/siemens-iot-2050.md | 32 +--
.../integration-technologies/README.md | 22 ++
.../integration-technologies/graphql.md | 19 +-
.../images/change-node-prepare-data-chart.png | Bin
.../change-node-set-downloads-payload.png | Bin
.../images/chart-with-data.png | Bin
.../images/dashboard-config-chart.png | Bin
.../images/dashboard-config-ui-base.png | Bin
.../images/dashboard-config-ui-group.png | Bin
.../images/http-get-npmapi.png | Bin
...-red-change-node-formating-sensor-data.png | Bin
...sing-webhook-with-node-red-change-node.png | Bin
...ing-webhook-with-node-red-change1-node.png | Bin
...ing-webhook-with-node-red-change2-node.png | Bin
...-webhook-with-node-red-dht-sensor-node.png | Bin
.../using-webhook-with-node-red-diagram.png | Bin
.../using-webhook-with-node-red-flow.png | Bin
...ng-webhook-with-node-red-function-node.png | Bin
...ebhook-with-node-red-how-webhook-works.png | Bin
...point-for-receiving-data-from-server-2.png | Bin
...equest-node-sending-request-to-server3.png | Bin
...uled-maintenance-table-dashboard-view.webm | Bin
...hook-with-node-red-sensor2-inject-node.png | Bin
...ebhook-with-node-red-server-1-instance.png | Bin
...ebhook-with-node-red-server-2-instance.png | Bin
...ebhook-with-node-red-server-3-instance.png | Bin
...sing-webhook-with-node-red-switch-node.png | Bin
...-webhook-with-node-red-webhook-trigger.png | Bin
.../images/webhooks-node-red.png | Bin
.../node-red/integration-technologies/rest.md | 57 +++--
.../integration-technologies/webhook.md | 17 +-
.../node-red/keyboard/README.md | 13 +-
.../node-red/notification/README.md | 20 ++
.../node-red/notification/discord.md | 29 +--
.../node-red/notification/email.md | 17 +-
.../discord-with-node-red-bot-reset-token.png | Bin
.../discord-with-node-red-channel-id.png | Bin
...rd-with-node-red-conform-add-to-server.png | Bin
.../discord-with-node-red-create-app.png | Bin
.../discord-with-node-red-developer-mode.png | Bin
.../images/discord-with-node-red-error-1.png | Bin
.../images/discord-with-node-red-error-2.png | Bin
.../images/discord-with-node-red-error-3.png | Bin
...d-with-node-red-new-application-button.png | Bin
.../discord-with-node-red-oauth-copy-url.png | Bin
.../discord-with-node-red-oauth-scope.png | Bin
.../discord-with-node-red-outh-permission.png | Bin
...iscord-with-node-red-privillage-itents.png | Bin
...cord-with-node-red-receiving-messages.webm | Bin
...iscord-with-node-red-select-the-server.png | Bin
...d-with-node-red-sending-msg-to-server.webm | Bin
...ord-with-node-red-sending-msg-to-user.webm | Bin
.../images/discord-with-node-red-user-id.png | Bin
.../notification/images/email_node-red.png | Bin
...-node-red-e-mail-in-node-configuration.png | Bin
...ith-node-red-e-mail-node-configuration.png | Bin
...eiving-email-with-node-red-gmail-inbox.png | Bin
...eiving-email-with-node-red-inject-node.png | Bin
...ving-email-with-node-red-node-red-flow.png | Bin
...node-red_setting_environment_variables.png | Bin
...-telegram-with-node-red-activating-bot.png | Bin
...nding-telegram-with-node-red-botfather.png | Bin
...ing-telegram-with-node-red-change-node.png | Bin
...d-enabling-send-error-to-second-option.png | Bin
.../sending-telegram-with-node-red-flow.png | Bin
...ith-node-red-flowfue-instance-settings.png | Bin
...sending-telegram-with-node-red-getmyid.png | Bin
...h-node-red-telegram-node-configuration.png | Bin
.../notification/images/telegram_node-red.png | Bin
.../node-red/notification/telegram.md | 17 +-
.../node-red/peripheral/README.md | 13 +-
.../node-red/peripheral/ardiuno.md | 15 +-
.../node-red/peripheral/barcodescanner.md | 18 +-
.../node-red/peripheral/esp32.md | 15 +-
.../peripheral/images/barcode-scanner.png | Bin
.../images/usbhid-barcode-node-red.png | Bin
.../images/usbhid-config-node-red.png | Bin
.../images/usbhid-qr-pizza-order.png | Bin
.../images/usbhid-scanned-barcode.png | Bin
.../images/using_webcam_with_node-red.png | Bin
..._node-red_capturing_images_by_control.webm | Bin
...d_capturing_images_by_passing_payload.webm | Bin
...h_node-red_selecting_different_camera.webm | Bin
.../node-red/peripheral/webcam.md | 33 ++-
.../node-red/protocol/README.md | 29 +++
.../content-guides}/node-red/protocol/amqp.md | 45 ++--
.../images/PLC-Information-Model-1.png | Bin
.../images/PLC-Information-Model-2-of-6-1.png | Bin
.../images/PLC-Information-Model-3-of-6-1.png | Bin
.../images/PLC-Information-Model-4-of-6-1.png | Bin
.../images/PLC-Information-Model-5-of-6-1.png | Bin
.../images/PLC-Information-Model-6-of-6-1.png | Bin
.../protocol/images/amqp-with-node-red.png | Bin
.../node-red/protocol/images/cert-list.png | Bin
.../protocol/images/cert-tab-filled.png | Bin
.../protocol/images/change-node-palette.png | Bin
.../images/change-node-properties.png | Bin
.../images/client-cert-path-filled.png | Bin
.../protocol/images/client-cert-path.png | Bin
.../protocol/images/client-testing.webm | Bin
.../protocol/images/compact-server-active.png | Bin
.../compact-server-node-address-space.png | Bin
.../protocol/images/connect-rejected.png | Bin
.../protocol/images/connected-mqtt-node.png | Bin
.../protocol/images/context-data-1.png | Bin
.../protocol/images/context-refresh.png | Bin
.../images/conveyor-context-function.png | Bin
.../images/copied-certs-with-pubkey.png | Bin
.../node-red/protocol/images/copied-certs.png | Bin
.../node-red/protocol/images/copy-certs.png | Bin
.../images/custom-nodes-installed.png | Bin
.../node-red/protocol/images/debug-data.png | Bin
.../node-red/protocol/images/deploy-flow.png | Bin
.../node-red/protocol/images/der-file.png | Bin
.../node-red/protocol/images/direct.webm | Bin
.../node-red/protocol/images/eth-ip-debug.png | Bin
.../protocol/images/eth-ip-endpoint-tags.png | Bin
.../protocol/images/eth-ip-in-palette.png | Bin
.../protocol/images/eth-ip-in-properties.png | Bin
.../protocol/images/ethip-node-connection.png | Bin
.../node-red/protocol/images/example-flow.png | Bin
.../images/executing-command-from-server.webm | Bin
.../node-red/protocol/images/fanout.webm | Bin
.../protocol/images/flow-manage-palette.png | Bin
.../images/flow-with-change-palette.png | Bin
.../images/flow-with-compact-server.png | Bin
.../images/flowforge-nodered-mqtt-hivemq.png | Bin
.../node-red/protocol/images/header.webm | Bin
.../protocol/images/image-20230717-212515.png | Bin
.../protocol/images/image-20230718-155245.png | Bin
.../protocol/images/image-20230718-164326.png | Bin
.../protocol/images/import-context.png | Bin
.../node-red/protocol/images/import.png | Bin
.../images/individual-cert-file-option.png | Bin
.../protocol/images/install-eth-ip-node.png | Bin
.../protocol/images/lwm2m-server-reading.webm | Bin
.../node-red/protocol/images/modbus-1-1.png | Bin
.../node-red/protocol/images/modbus-1-10.png | Bin
.../node-red/protocol/images/modbus-1-11.png | Bin
.../node-red/protocol/images/modbus-1-12.png | Bin
.../node-red/protocol/images/modbus-1-13.png | Bin
.../node-red/protocol/images/modbus-1-14.png | Bin
.../node-red/protocol/images/modbus-1-15.png | Bin
.../node-red/protocol/images/modbus-1-16.png | Bin
.../node-red/protocol/images/modbus-1-2.png | Bin
.../node-red/protocol/images/modbus-1-3.png | Bin
.../node-red/protocol/images/modbus-1-4.png | Bin
.../node-red/protocol/images/modbus-1-5.png | Bin
.../node-red/protocol/images/modbus-1-6.png | Bin
.../node-red/protocol/images/modbus-1-7.png | Bin
.../node-red/protocol/images/modbus-1-8.png | Bin
.../node-red/protocol/images/modbus-1-9.png | Bin
.../node-red/protocol/images/modbus-1.jpg | Bin
.../protocol/images/modbus-node-red-cta-1.png | Bin
.../protocol/images/modbus-server.png | Bin
.../modbus-with-node-red-change-node.png | Bin
...node-red-combine-holding-register-data.png | Bin
...ith-node-red-combine-output-coils-data.png | Bin
.../images/modbus-with-node-red-dashboard.png | Bin
...s-with-node-red-send-holding-registers.png | Bin
.../modbus-with-node-red-send-output-coil.png | Bin
...th-node-red-template-widget-stylesheet.png | Bin
.../images/modbus-with-node-red-text-node.png | Bin
.../protocol/images/mqtt-broker-config.png | Bin
.../images/mqtt-in-config-node-red.png | Bin
.../protocol/images/mqtt-in-config.png | Bin
.../node-red/protocol/images/mqtt-out.png | Bin
.../node-red/protocol/images/msg-payload.png | Bin
.../images/node-red-config-mqtt-server.png | Bin
.../images/opc-client-accept-cert.png | Bin
.../protocol/images/opc-client-connect.png | Bin
.../protocol/images/opc-folder-structure.png | Bin
.../protocol/images/opc-nodes-install.png | Bin
.../node-red/protocol/images/opc-nodes.png | Bin
.../protocol/images/opc-ua-2-title-image.png | Bin
.../reading-data-from-lwm2m-from-server.webm | Bin
.../reading-data-from-server-in-nr.webm | Bin
.../protocol/images/security-tab-default.png | Bin
.../protocol/images/server-testing.webm | Bin
.../node-red/protocol/images/sign&encrypt.png | Bin
.../protocol/images/sine-wave-gen.png | Bin
.../protocol/images/ssl-pub-keygen.png | Bin
.../node-red/protocol/images/topic.webm | Bin
.../images/using-lwm2m-with-node-red.png | Bin
.../protocol/images/websocket-handshake.png | Bin
.../writing-data-to-server-from-nr.webm | Bin
.../protocol/images/writing-in-server.webm | Bin
.../node-red/protocol/lwm2m.md | 26 +--
.../node-red/protocol/modbus.md | 20 +-
.../content-guides}/node-red/protocol/mqtt.md | 10 +-
.../node-red/protocol/opc-ua.md | 32 ++-
.../node-red/protocol/websocket.md | 19 +-
.../node-red/terminology/README.md | 13 +-
.../terminology/images/editor-flow-tabs.png | Bin
.../terminology/images/editor-workspace.png | Bin
.../images/node-input-ouput-port.png | Bin
.../terminology/images/node-palette.png | Bin
.../images/node-red-editor-deploy-button.png | Bin
.../terminology/images/node-red-editor.png | Bin
.../images/node-red-message-object.png | Bin
.../terminology/images/node-red-node.png | Bin
.../images/node-red-palette-manager.png | Bin
.../terminology/images/node-red-subflow.png | Bin
.../node-red/terminology/images/node-wire.png | Bin
nuxt/lib/core-node-help.json | 40 ++++
nuxt/lib/core-nodes-sync.mjs | 214 ++++++++++++++++++
nuxt/lib/core-nodes-sync.test.mjs | 120 ++++++++++
nuxt/modules/docs-source.ts | 14 +-
nuxt/redirects-node-red.ts | 144 ++++++++++++
nuxt/redirects.ts | 3 +
nuxt/server/middleware/legacy.ts | 8 +
scripts/refresh_core_node_help.mjs | 28 +++
scripts/sync_docs.mjs | 8 +
src/_data/chrome.json | 63 ------
src/_data/coreNodes.json | 13 +-
src/_includes/components/docs-banner.njk | 43 ----
src/_includes/core-node-docs.njk | 10 -
src/_includes/layouts/documentation.njk | 59 -----
src/_includes/layouts/hardware.njk | 68 ------
.../layouts/learning-resources-nav.njk | 77 -------
src/_includes/layouts/left-nav.njk | 92 --------
src/node-red/core-nodes/common.njk | 18 --
src/node-red/core-nodes/function.njk | 18 --
src/node-red/core-nodes/index.njk | 25 --
src/node-red/core-nodes/nav.njk | 15 --
src/node-red/core-nodes/network.njk | 18 --
src/node-red/core-nodes/parsers.njk | 18 --
src/node-red/core-nodes/sequence.njk | 18 --
src/node-red/core-nodes/storage.njk | 18 --
src/node-red/database/index.md | 27 ---
src/node-red/flowfuse/ai/index.md | 18 --
src/node-red/flowfuse/edge/index.md | 34 ---
src/node-red/flowfuse/index.md | 18 --
src/node-red/getting-started/index.md | 15 --
.../getting-started/programming/index.md | 20 --
src/node-red/hardware/armxy-bl340.md | 52 -----
src/node-red/hardware/index.md | 21 --
src/node-red/hardware/raspberry-pi-4.md | 55 -----
src/node-red/hardware/raspberry-pi-5.md | 55 -----
src/node-red/hardware/robustel-eg5120.md | 35 ---
.../integration-technologies/index.md | 22 --
src/node-red/learn.md | 63 ------
src/node-red/node-red.json | 9 -
src/node-red/notification/index.md | 19 --
src/node-red/protocol/index.md | 32 ---
557 files changed, 2263 insertions(+), 1941 deletions(-)
delete mode 100644 lib/core-node-docs.js
create mode 100644 nuxt/content-guides/flowfuse-nodes/README.md
create mode 100644 nuxt/content-guides/flowfuse-nodes/ai/README.md
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/ai/depth-estimation.md (98%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/ai/image-classification.md (98%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/ai/llm-nodes.md (98%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/ai/object-detection.md (98%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/ai/onxx.md (98%)
create mode 100644 nuxt/content-guides/flowfuse-nodes/edge/README.md
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/cip-suite.md (95%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/endpoint-add-new.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/endpoint-subscriptions.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/endpoint-verification-secure-error.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/endpoint-verification-user-access-denied.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/endpoint-verification.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/example-all-attributes-on-rfid-scan-result.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/node-palette.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/nodeid-as-browse-path.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/opcu-client2-read.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/opcua-client2-monitor.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/read-showing-endpoint-field.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/opcua/use-node-browser-to-select-node-id.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/rtsp/rtsp-config-node.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/images/rtsp/rtsp-edge-catalog.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/modbus.md (93%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/opcua.md (95%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/edge/rtsp.md (83%)
rename src/node-red/flowfuse/flowfuse-tables/index.md => nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md (62%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/flowfuse-tables/query.md (97%)
rename src/node-red/flowfuse/hub/index.md => nuxt/content-guides/flowfuse-nodes/hub/README.md (56%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/hub/redis.md (98%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/images/ff-expert-at-platform-level.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/images/ff-expert-in-editor.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/images/ff-expert-opened.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/images/ff-expert-opned-platform.png (100%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/images/mcp-in-flowfuse.png (100%)
rename src/node-red/flowfuse/mcp/index.md => nuxt/content-guides/flowfuse-nodes/mcp/README.md (87%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/mcp/mcp-prompt.md (96%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/mcp/mcp-resource.md (99%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/mcp/mcp-response.md (92%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/mcp/mcp-tool.md (98%)
rename src/node-red/flowfuse/mqtt/index.md => nuxt/content-guides/flowfuse-nodes/mqtt/README.md (64%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/mqtt/mqtt-in.md (96%)
rename {src/node-red/flowfuse => nuxt/content-guides/flowfuse-nodes}/mqtt/mqtt-out.md (97%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/batch-example1.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/batch-example2.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/batch-example3.webm (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/batch-example4.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/batch-example5.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/change-data-transform.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/change-default.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/change-message-enrich.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/change-message-filter.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/change-message-format.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/change-rename-property.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/comment-node-full-text.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/debug-copy-path.webm (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/debug-copy-value.webm (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/debug-examples.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/debug-panel.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/debug-pin-open.webm (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/delay-node-2.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/inject-config-string.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/link-node.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/list-nodes-unused.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/mqtt-config.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/range-node2.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/spawn-mode-exec.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/status-node-output.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/status-node.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/switch-example-2.png (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/system-info-installed-nodes.webm (100%)
rename {src => nuxt/content-guides}/node-red/core-nodes/images/wired-up-exec-node.png (100%)
create mode 100644 nuxt/content-guides/node-red/database/README.md
rename {src => nuxt/content-guides}/node-red/database/dynamodb.md (98%)
rename {src => nuxt/content-guides}/node-red/database/firebase.md (99%)
rename {src => nuxt/content-guides}/node-red/database/images/config-connection.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/config-database.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/data_in_influx.webm (100%)
rename {src => nuxt/content-guides}/node-red/database/images/dynamodb-data-structure-node-red-flowfuse.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/dynamodb-flowfuse-iam-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/flowfuse-dynamodb-aws-setup-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/influxdb-node-red-cta-1.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/mysql-node-config.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/mysql-with-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/node-red-dynamodb-flow-flowfuse.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/node-red-dynamodb-get-item-flowfuse.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/node-red-dynamodb-query-flowfuse.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/node-red-dynamodb-scan-flowfuse.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/node-red-dynamodb.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/node-red-influxdb.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_Inventory_management_system.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_delete_data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_drop_tables.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_form1.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_form2.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_form3.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_insert_data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_pgconfig1.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_pgconfig2.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_retrive_data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_node-red_update_data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_nodered_create_table.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_nodred_environment_variable.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/postgresql_with_nodred_environment_variable_ff_editor.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/timescaledb-with-node-red-hypertables.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/timescaledb-with-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-Mongo-with-nr.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-annotomy-of-mongodb-document.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-change-node-to-delete-data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-change-node-to-insert-data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-change-node-to-update-data.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-dashboard-view.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-delete-data-form.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-flowfuse-instance-setting.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-friend-id-node.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-insert-data-form.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-find-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-find-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-node-connection-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-update-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-mongodb-updateone-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-table-widget.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-mongodb-with-node-red-update-data-form.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-timescaledb-with-node-red-environment-variables.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-timescaledb-with-node-red-inject-node-1.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-timescaledb-with-node-red-inject-node-2.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-connection-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-security-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/database/influxdb.md (92%)
rename {src => nuxt/content-guides}/node-red/database/mongodb.md (98%)
rename {src => nuxt/content-guides}/node-red/database/mysql.md (95%)
rename {src => nuxt/content-guides}/node-red/database/postgresql.md (98%)
rename {src => nuxt/content-guides}/node-red/database/redis.md (98%)
rename {src => nuxt/content-guides}/node-red/database/sqlite.md (99%)
rename {src => nuxt/content-guides}/node-red/database/timescaledb.md (97%)
create mode 100644 nuxt/content-guides/node-red/getting-started/README.md
rename {src => nuxt/content-guides}/node-red/getting-started/date-and-time.md (98%)
rename src/node-red/getting-started/editor/index.md => nuxt/content-guides/node-red/getting-started/editor/README.md (84%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/header.md (97%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/adding-nodes-to-group.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/creating-group.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/deleting-wires.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/deploy-button-active.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/deploy-button-inactive.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/deploy-button-options.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/detaching-nodes-while-keeping-wires.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/editor-flow-tabs.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/editor-workspace.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/environment-variables-options.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/group-description.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/group-level-env.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/group-merging-groups.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/group-properties-edit.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/group-properties.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/group.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/header-flowfuse-instance.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/header.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/lasso-tool.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-arrange-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-configuration-nodes.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-edit-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-export-local.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-export-team.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-export.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-flows.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-groups.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-import.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-search-flow-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-search-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-settings.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-subflows.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/main-menu-view-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/moving-multiple-wires.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/moving-wire.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/mqtt-in-node-status.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-add-config-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-appearance-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-buttons.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-description-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-edit-properties-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-enable-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-help.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-indicating-error.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-indicating-how-many-nodes-using-config-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-indicating-undeployed-changes.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-input-ouput-port.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-quick-add-dialog.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-editor-palette.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-editor-window.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-export-local-library.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-palette-category.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-palette-collapse-expand.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-palette-export-scope.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-palette-json.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-palette-search.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-palette-toggle.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-pallete-manger-nodes-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-red-sidebar.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/node-wire.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/pallete-manger-filter-and-sort-options.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/removing-node-from-group.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/removing-nodes-from-group.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/removing-nodes-while-keeping-wires.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/selecting-connected-nodes.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/selecting-downward-connected-nodes.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/selecting-flows.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/selecting-upward-connected-nodes.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/slicing-wires.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-appearance-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-converting-nodes-to-subflow.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-custom-properties.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-delete-option.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-editing-properties.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-input-output.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-module-tab.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-property-description.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-status-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-ui-preview.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/subflow-window.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/wiring-nodes-together.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-add-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-delete-flow-dialog.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-delete-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-disable-enable-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-enable-disable-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-flow-edit.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-navigator-tool.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-reordering-flow.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-search-tool.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/images/workspace-view-tool.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/palette.md (92%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/sidebar.md (98%)
rename {src => nuxt/content-guides}/node-red/getting-started/editor/workspace.md (89%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/adding-breakpoints.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/breakpoint-debugging.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/breakpoints-section.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/debugger-controls.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/disable-enable-button.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/disabling-breakpoints.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-change-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-calculating-avg.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-combining-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-sort-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-switch-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-switch-node.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/if-else-with-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/message-section.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/mistake-1-solution.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/mistake1.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/node-red-data-types.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/node-red-instance-update.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/node-red-message-passing.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/proceeding-further-execution.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/removing-breakpoints.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/schedule-update.png (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/images/update-node-red-cta-1.png (100%)
rename src/node-red/getting-started/library/index.md => nuxt/content-guides/node-red/getting-started/library/README.md (81%)
rename {src => nuxt/content-guides}/node-red/getting-started/library/images/installing-node-red-node.webm (100%)
rename {src => nuxt/content-guides}/node-red/getting-started/node-red-android.md (96%)
rename {src => nuxt/content-guides}/node-red/getting-started/node-red-messages.md (95%)
rename {src => nuxt/content-guides}/node-red/getting-started/node-red-port.md (96%)
create mode 100644 nuxt/content-guides/node-red/getting-started/programming/README.md
rename {src => nuxt/content-guides}/node-red/getting-started/programming/data-tranformation.md (97%)
rename {src => nuxt/content-guides}/node-red/getting-started/programming/debugging-flows.md (90%)
rename {src => nuxt/content-guides}/node-red/getting-started/programming/if-else.md (93%)
rename {src => nuxt/content-guides}/node-red/getting-started/programming/loop.md (96%)
rename {src => nuxt/content-guides}/node-red/getting-started/string.md (94%)
rename {src => nuxt/content-guides}/node-red/getting-started/update-node-red.md (93%)
create mode 100644 nuxt/content-guides/node-red/hardware/README.md
create mode 100644 nuxt/content-guides/node-red/hardware/armxy-bl340.md
rename {src => nuxt/content-guides}/node-red/hardware/images/GRV-R7-MM1001-10.jpeg (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/armxy-bl340.jpg (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/configuration-dailog-with-one-time-code.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/flowfuse-on-robustel-eg5120-installation-complete.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/flowfuse-on-robustel-eg5120-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/flowfuse-on-siemens-iot2050.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-debug-node.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-device-configuration-dialog.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-dht-sensor.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-inject-node.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-pins.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-4-editor.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-4-b.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-4.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-5-config-before-flash.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-5-device-agent.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-5-editor.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-5-flash-os.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberry-pi-5.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/raspberrypi-5.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/robustel-eg5120.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/siemens-iot-2050.jpg (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/status-flowfuse.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/images/systemctl-status.png (100%)
rename {src => nuxt/content-guides}/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md (67%)
create mode 100644 nuxt/content-guides/node-red/hardware/raspberry-pi-4.md
create mode 100644 nuxt/content-guides/node-red/hardware/raspberry-pi-5.md
create mode 100644 nuxt/content-guides/node-red/hardware/robustel-eg5120.md
rename {src => nuxt/content-guides}/node-red/hardware/siemens-iot-2050.md (91%)
create mode 100644 nuxt/content-guides/node-red/integration-technologies/README.md
rename {src => nuxt/content-guides}/node-red/integration-technologies/graphql.md (98%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/change-node-prepare-data-chart.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/change-node-set-downloads-payload.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/chart-with-data.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/dashboard-config-chart.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/dashboard-config-ui-base.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/dashboard-config-ui-group.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/http-get-npmapi.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-change-node-formating-sensor-data.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-change-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-change1-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-change2-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-dht-sensor-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-diagram.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-function-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-how-webhook-works.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-http-in-node-endpoint-for-receiving-data-from-server-2.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-request-node-sending-request-to-server3.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-scheduled-maintenance-table-dashboard-view.webm (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-sensor2-inject-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-server-1-instance.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-server-2-instance.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-server-3-instance.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-switch-node.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/using-webhook-with-node-red-webhook-trigger.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/images/webhooks-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/rest.md (95%)
rename {src => nuxt/content-guides}/node-red/integration-technologies/webhook.md (96%)
rename src/node-red/keyboard/index.md => nuxt/content-guides/node-red/keyboard/README.md (94%)
create mode 100644 nuxt/content-guides/node-red/notification/README.md
rename {src => nuxt/content-guides}/node-red/notification/discord.md (91%)
rename {src => nuxt/content-guides}/node-red/notification/email.md (95%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-bot-reset-token.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-channel-id.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-conform-add-to-server.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-create-app.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-developer-mode.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-error-1.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-error-2.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-error-3.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-new-application-button.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-oauth-copy-url.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-oauth-scope.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-outh-permission.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-privillage-itents.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-receiving-messages.webm (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-select-the-server.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-sending-msg-to-server.webm (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-sending-msg-to-user.webm (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/discord-with-node-red-user-id.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/email_node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-in-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-and-receiving-email-with-node-red-gmail-inbox.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-and-receiving-email-with-node-red-inject-node.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red_setting_environment_variables.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-activating-bot.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-botfather.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-change-node.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-enabling-send-error-to-second-option.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-flowfue-instance-settings.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-getmyid.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/sending-telegram-with-node-red-telegram-node-configuration.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/images/telegram_node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/notification/telegram.md (93%)
rename src/node-red/peripheral/index.md => nuxt/content-guides/node-red/peripheral/README.md (56%)
rename {src => nuxt/content-guides}/node-red/peripheral/ardiuno.md (91%)
rename {src => nuxt/content-guides}/node-red/peripheral/barcodescanner.md (85%)
rename {src => nuxt/content-guides}/node-red/peripheral/esp32.md (97%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/barcode-scanner.png (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/usbhid-barcode-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/usbhid-config-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/usbhid-qr-pizza-order.png (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/usbhid-scanned-barcode.png (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/using_webcam_with_node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_control.webm (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_passing_payload.webm (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/images/using_webcam_with_node-red_selecting_different_camera.webm (100%)
rename {src => nuxt/content-guides}/node-red/peripheral/webcam.md (82%)
create mode 100644 nuxt/content-guides/node-red/protocol/README.md
rename {src => nuxt/content-guides}/node-red/protocol/amqp.md (97%)
rename {src => nuxt/content-guides}/node-red/protocol/images/PLC-Information-Model-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/PLC-Information-Model-2-of-6-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/PLC-Information-Model-3-of-6-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/PLC-Information-Model-4-of-6-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/PLC-Information-Model-5-of-6-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/PLC-Information-Model-6-of-6-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/amqp-with-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/cert-list.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/cert-tab-filled.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/change-node-palette.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/change-node-properties.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/client-cert-path-filled.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/client-cert-path.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/client-testing.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/compact-server-active.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/compact-server-node-address-space.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/connect-rejected.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/connected-mqtt-node.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/context-data-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/context-refresh.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/conveyor-context-function.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/copied-certs-with-pubkey.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/copied-certs.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/copy-certs.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/custom-nodes-installed.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/debug-data.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/deploy-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/der-file.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/direct.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/eth-ip-debug.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/eth-ip-endpoint-tags.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/eth-ip-in-palette.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/eth-ip-in-properties.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/ethip-node-connection.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/example-flow.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/executing-command-from-server.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/fanout.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/flow-manage-palette.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/flow-with-change-palette.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/flow-with-compact-server.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/flowforge-nodered-mqtt-hivemq.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/header.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/image-20230717-212515.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/image-20230718-155245.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/image-20230718-164326.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/import-context.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/import.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/individual-cert-file-option.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/install-eth-ip-node.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/lwm2m-server-reading.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-10.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-11.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-12.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-13.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-14.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-15.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-16.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-2.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-3.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-4.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-5.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-6.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-7.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-8.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1-9.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-1.jpg (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-node-red-cta-1.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-server.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-change-node.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-combine-holding-register-data.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-combine-output-coils-data.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-dashboard.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-send-holding-registers.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-send-output-coil.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-template-widget-stylesheet.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/modbus-with-node-red-text-node.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/mqtt-broker-config.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/mqtt-in-config-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/mqtt-in-config.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/mqtt-out.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/msg-payload.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/node-red-config-mqtt-server.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/opc-client-accept-cert.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/opc-client-connect.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/opc-folder-structure.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/opc-nodes-install.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/opc-nodes.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/opc-ua-2-title-image.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/reading-data-from-lwm2m-from-server.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/reading-data-from-server-in-nr.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/security-tab-default.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/server-testing.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/sign&encrypt.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/sine-wave-gen.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/ssl-pub-keygen.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/topic.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/using-lwm2m-with-node-red.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/websocket-handshake.png (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/writing-data-to-server-from-nr.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/images/writing-in-server.webm (100%)
rename {src => nuxt/content-guides}/node-red/protocol/lwm2m.md (90%)
rename {src => nuxt/content-guides}/node-red/protocol/modbus.md (98%)
rename {src => nuxt/content-guides}/node-red/protocol/mqtt.md (97%)
rename {src => nuxt/content-guides}/node-red/protocol/opc-ua.md (98%)
rename {src => nuxt/content-guides}/node-red/protocol/websocket.md (94%)
rename src/node-red/terminology/index.md => nuxt/content-guides/node-red/terminology/README.md (95%)
rename {src => nuxt/content-guides}/node-red/terminology/images/editor-flow-tabs.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/editor-workspace.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-input-ouput-port.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-palette.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-red-editor-deploy-button.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-red-editor.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-red-message-object.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-red-node.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-red-palette-manager.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-red-subflow.png (100%)
rename {src => nuxt/content-guides}/node-red/terminology/images/node-wire.png (100%)
create mode 100644 nuxt/lib/core-node-help.json
create mode 100644 nuxt/lib/core-nodes-sync.mjs
create mode 100644 nuxt/lib/core-nodes-sync.test.mjs
create mode 100644 nuxt/redirects-node-red.ts
create mode 100644 scripts/refresh_core_node_help.mjs
delete mode 100644 src/_includes/components/docs-banner.njk
delete mode 100644 src/_includes/core-node-docs.njk
delete mode 100644 src/_includes/layouts/documentation.njk
delete mode 100644 src/_includes/layouts/hardware.njk
delete mode 100644 src/_includes/layouts/learning-resources-nav.njk
delete mode 100644 src/_includes/layouts/left-nav.njk
delete mode 100644 src/node-red/core-nodes/common.njk
delete mode 100644 src/node-red/core-nodes/function.njk
delete mode 100644 src/node-red/core-nodes/index.njk
delete mode 100644 src/node-red/core-nodes/nav.njk
delete mode 100644 src/node-red/core-nodes/network.njk
delete mode 100644 src/node-red/core-nodes/parsers.njk
delete mode 100644 src/node-red/core-nodes/sequence.njk
delete mode 100644 src/node-red/core-nodes/storage.njk
delete mode 100644 src/node-red/database/index.md
delete mode 100644 src/node-red/flowfuse/ai/index.md
delete mode 100644 src/node-red/flowfuse/edge/index.md
delete mode 100644 src/node-red/flowfuse/index.md
delete mode 100644 src/node-red/getting-started/index.md
delete mode 100644 src/node-red/getting-started/programming/index.md
delete mode 100644 src/node-red/hardware/armxy-bl340.md
delete mode 100644 src/node-red/hardware/index.md
delete mode 100644 src/node-red/hardware/raspberry-pi-4.md
delete mode 100644 src/node-red/hardware/raspberry-pi-5.md
delete mode 100644 src/node-red/hardware/robustel-eg5120.md
delete mode 100644 src/node-red/integration-technologies/index.md
delete mode 100644 src/node-red/learn.md
delete mode 100644 src/node-red/node-red.json
delete mode 100644 src/node-red/notification/index.md
delete mode 100644 src/node-red/protocol/index.md
diff --git a/.eleventy.js b/.eleventy.js
index 6991429d83..b2c59953f1 100644
--- a/.eleventy.js
+++ b/.eleventy.js
@@ -20,7 +20,6 @@ const pluginTOC = require('eleventy-plugin-toc');
const { decodeHTML } = require('entities');
const imageHandler = require('./lib/image-handler.js')
const site = require("./src/_data/site");
-const coreNodeDoc = require("./lib/core-node-docs.js");
const { isSearchPage, isSearchUrl, extractHeadingRecords } = require("./lib/search-index.js");
const yaml = require("js-yaml");
const eleventyNavigationPlugin = require("@11ty/eleventy-navigation");
@@ -223,10 +222,6 @@ module.exports = function(eleventyConfig) {
return Object.entries(coreNodes).map(([key, nodes]) => ({ key, nodes }));
})
- eleventyConfig.addAsyncShortcode("coreNodeDoc", async function (category, node) {
- return await coreNodeDoc(category, node)
- });
-
eleventyConfig.addFilter("filterNodeCategory", function(nodes, category) {
if (category === "all") {
return nodes;
diff --git a/lib/core-node-docs.js b/lib/core-node-docs.js
deleted file mode 100644
index 1ad3ad08e6..0000000000
--- a/lib/core-node-docs.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const EleventyFetch = require("@11ty/eleventy-fetch");
-const xpath = require('xpath');
-const dom = require('@xmldom/xmldom').DOMParser;
-
-module.exports = async function (cat, node) {
- let page = "";
- try {
- const url = `https://raw.githubusercontent.com/node-red/node-red/master/packages/node_modules/%40node-red/nodes/locales/en-US/${cat}/${node.file}.html`
- let data = await EleventyFetch(url, { type: "text" });
-
- const doc = new dom({
- locator: {},
- errorHandler: {
- warning: function (w) { },
- error: function (e) { },
- fatalError: function (e) { console.error(e) }
- }
- // As there's a few nodes that have multiple docs in one file, we wrap it to make it
- // valid XML
- }).parseFromString("" + data.toString() + "", 'text/xml');
-
- return xpath.select(`/scripts/script[starts-with(@data-help-name, "${node.xpath}")]/*`, doc).join(" ")
- } catch (e) {
- console.error(e);
- return null;
- }
-};
diff --git a/nuxt/content-guides/flowfuse-nodes/README.md b/nuxt/content-guides/flowfuse-nodes/README.md
new file mode 100644
index 0000000000..38c7aebaaf
--- /dev/null
+++ b/nuxt/content-guides/flowfuse-nodes/README.md
@@ -0,0 +1,24 @@
+---
+title: FlowFuse Nodes
+navTitle: FlowFuse Node-RED nodes
+navOrder: 4
+navGroup: FlowFuse User Manuals
+navGroupOrder: 1
+meta:
+ description: An overview of the Node-RED nodes provided by FlowFuse, explaining their purpose, usage, and benefits for building integrations and automations.
+---
+
+# FlowFuse Nodes
+
+In Node-RED on the FlowFuse platform, you have access to additional Node-RED nodes that are provided by FlowFuse. These are documented in the below sections as a reference you can use when building integrations and automations.
+
+## Nodes
+
+The following documents provide details about the FlowFuse nodes:
+
+- [FlowFuse AI Nodes](/docs/flowfuse-nodes/ai/): A set of Node-RED nodes for AI and machine learning, including ONNX model inference and LLM nodes for OpenAI, Anthropic, Google Gemini, and Ollama.
+- [MCP Nodes](/docs/flowfuse-nodes/mcp/): A set of nodes that enable the creation of MCP (Model Context Protocol) servers in your Node-RED flows for AI-integration.
+- [FlowFuse Tables](/docs/flowfuse-nodes/flowfuse-tables/): FlowFuse Tables provides managed databases for Node-RED users, offering built-in nodes to query, insert, and manage data easily within FlowFuse flows.
+- [Edge Certified Nodes](/docs/flowfuse-nodes/edge/): Documentation for FlowFuse Edge Certified Nodes, including nodes for connecting FlowFuse to industrial protocols, PLCs, SCADA systems, and factory-floor equipment.
+- [Hub Certified Nodes](/docs/flowfuse-nodes/hub/): Documentation for FlowFuse Hub Certified Nodes, including nodes for connecting FlowFuse to IT systems, databases, cloud services, APIs, and enterprise applications.
+- [MQTT Nodes](/docs/flowfuse-nodes/mqtt/): MQTT In and Out nodes designed for FlowFuse users with automatic configuration.
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/README.md b/nuxt/content-guides/flowfuse-nodes/ai/README.md
new file mode 100644
index 0000000000..4343869c49
--- /dev/null
+++ b/nuxt/content-guides/flowfuse-nodes/ai/README.md
@@ -0,0 +1,19 @@
+---
+title: FlowFuse AI Nodes
+navTitle: AI
+navOrder: 1
+meta:
+ description: A set of Node-RED nodes for AI and machine learning, including ONNX model inference and LLM nodes for OpenAI, Anthropic, Google Gemini, and Ollama.
+---
+
+# FlowFuse AI Nodes
+
+The **FlowFuse AI** Nodes package adds AI capabilities to Node-RED. It includes nodes for running local ONNX models for image classification, object detection, depth estimation, and custom inference, as well as LLM nodes for sending text prompts to hosted and local large language models from OpenAI, Anthropic, Google Gemini, and Ollama.
+
+## Nodes
+
+- [LLM Nodes](/docs/flowfuse-nodes/ai/llm-nodes/): Send text prompts to hosted and local large language models from OpenAI, Anthropic, Google Gemini, and Ollama directly within Node-RED flows.
+- [Depth Estimation](/docs/flowfuse-nodes/ai/depth-estimation/): The Depth Estimation node estimates the distance of objects in an image and creates a depth map using an ONNX model.
+- [Image Classification](/docs/flowfuse-nodes/ai/image-classification/): Classify images using ONNX models directly in Node-RED. Supports pre-trained and custom models for tasks like labeling, content moderation, and object recognition.
+- [Object Detection](/docs/flowfuse-nodes/ai/object-detection/): The Object Detection node identifies and locates objects within images using ONNX models such as YOLO and DETR, enabling real-time computer vision directly in Node-RED without external AI services.
+- [ONXX](/docs/flowfuse-nodes/ai/onxx/): The ONNX node allows you to perform AI inference directly in Node-RED using ONNX models, supporting image, object, and numeric predictions without external AI services.
diff --git a/src/node-red/flowfuse/ai/depth-estimation.md b/nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md
similarity index 98%
rename from src/node-red/flowfuse/ai/depth-estimation.md
rename to nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md
index 8e68ac88c3..063d6d8ab8 100644
--- a/src/node-red/flowfuse/ai/depth-estimation.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: Depth Estimation
- parent: AI
+title: Depth Estimation
+navTitle: Depth Estimation
+navOrder: 50
meta:
- title: Depth Estimation
description: The Depth Estimation node estimates the distance of objects in an image and creates a depth map using an ONNX model.
---
-# {{ meta.title }}
+# Depth Estimation
The **Depth Estimation** node allows you to estimate the relative distance of objects within an image using an ONNX model. It generates a depth map that represents how far each pixel is from the camera and can optionally create a visual image of the depth map using different color styles.
@@ -96,6 +95,8 @@ You can use either a single value or an array of two values:
## Example Flow
-{% renderFlow 400 %}
+::render-flow{:height="400"}
+```json
[{"id":"5f4317fdaae093fa","type":"image-depth","z":"e1ceeedf31ce1ebd","name":"","property":"image","propertyType":"msg","model":"Xenova/depth-anything-small-hf","modelType":"name","dtype":"fp16","generateImage":"true","generateImageType":"bool","alpha":"alpha","alphaType":"msg","style":"imageStyle","styleType":"msg","x":830,"y":2440,"wires":[["86d79d2fe917c839"]]},{"id":"6c6723ad96d0592f","type":"inject","z":"e1ceeedf31ce1ebd","name":"football (hot A1>0)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Football_in_Bloomington%2C_Indiana%2C_1995.jpg/1920px-Football_in_Bloomington%2C_Indiana%2C_1995.jpg","vt":"str"},{"p":"imageStyle","v":"hot","vt":"str"},{"p":"alpha","v":"[1,0]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":370,"y":2420,"wires":[["91f63424a0d29cbc"]]},{"id":"f0c3d67e58965419","type":"http request","z":"e1ceeedf31ce1ebd","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":730,"y":2380,"wires":[["a18b3a2b995d44a3"]]},{"id":"6dfc1deb6464d48a","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"","width":"300","data":"image","dataType":"msg","active":true,"x":650,"y":2440,"wires":[["5f4317fdaae093fa"]]},{"id":"86d79d2fe917c839","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"payload.image","width":"300","data":"payload.image","dataType":"msg","active":true,"x":1020,"y":2440,"wires":[["15eb62db5c6df5b0"]]},{"id":"15eb62db5c6df5b0","type":"debug","z":"e1ceeedf31ce1ebd","name":"data","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1190,"y":2440,"wires":[]},{"id":"fb988c7f2b5e7ebd","type":"inject","z":"e1ceeedf31ce1ebd","name":"tree (1ch greyscale)","props":[{"p":"url","v":"https://www.jotform.com/blog/wp-content/uploads/2022/02/niko-photos-tGTVxeOr_Rs-unsplash.jpg","vt":"str"},{"p":"imageStyle","v":"greyscale","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":370,"y":2380,"wires":[["91f63424a0d29cbc"]]},{"id":"1276ae55440d8971","type":"inject","z":"e1ceeedf31ce1ebd","name":"bird (viridis)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/3/32/House_sparrow04.jpg","vt":"str"},{"p":"imageStyle","v":"viridis","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":350,"y":2500,"wires":[["91f63424a0d29cbc"]]},{"id":"2d346c4a2bd33b35","type":"inject","z":"e1ceeedf31ce1ebd","name":"cave (density)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/f/f4/Hawaiian_lava_tube.jpg","vt":"str"},{"p":"imageStyle","v":"density","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":350,"y":2580,"wires":[["91f63424a0d29cbc"]]},{"id":"80a435f3656d71c3","type":"inject","z":"e1ceeedf31ce1ebd","name":"octopus (jet A0.9>0.3)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/Octopus2.jpg/1920px-Octopus2.jpg","vt":"str"},{"p":"imageStyle","v":"jet","vt":"str"},{"p":"alpha","v":"[0.5,0.9]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":380,"y":2700,"wires":[["91f63424a0d29cbc"]]},{"id":"f6e800ce3399cb5f","type":"inject","z":"e1ceeedf31ce1ebd","name":"cave (grayscale)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/4/4e/HallOfTheMountainKings.jpg","vt":"str"},{"p":"imageStyle","v":"grayscale","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":360,"y":2620,"wires":[["91f63424a0d29cbc"]]},{"id":"0f4eaaa7bf1dd22e","type":"inject","z":"e1ceeedf31ce1ebd","name":"plane (rainbow)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Spitfire_-_Season_Premiere_Airshow_2018_%28cropped%29.jpg/1920px-Spitfire_-_Season_Premiere_Airshow_2018_%28cropped%29.jpg","vt":"str"},{"p":"colormap","v":"rainbow","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":360,"y":2460,"wires":[["91f63424a0d29cbc"]]},{"id":"2f20edfb8190e918","type":"inject","z":"e1ceeedf31ce1ebd","name":"castle (greys)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/5/50/Bodiam-castle-10My8-1197.jpg","vt":"str"},{"p":"imageStyle","v":"greys","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":350,"y":2540,"wires":[["91f63424a0d29cbc"]]},{"id":"c8f48da588d9c9ce","type":"inject","z":"e1ceeedf31ce1ebd","name":"monkey (rdbu A0>1)","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/4/43/Bonnet_macaque_%28Macaca_radiata%29_Photograph_By_Shantanu_Kuveskar.jpg","vt":"str"},{"p":"imageStyle","v":"rdbu","vt":"str"},{"p":"alpha","v":"[0,1]","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":370,"y":2660,"wires":[["91f63424a0d29cbc"]]},{"id":"814e081d13eb58f6","type":"comment","z":"e1ceeedf31ce1ebd","name":"Image Depth","info":"","x":330,"y":2340,"wires":[]},{"id":"a18b3a2b995d44a3","type":"change","z":"e1ceeedf31ce1ebd","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"image","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":855,"y":2380,"wires":[["6dfc1deb6464d48a"]],"l":false},{"id":"91f63424a0d29cbc","type":"junction","z":"e1ceeedf31ce1ebd","x":580,"y":2380,"wires":[["f0c3d67e58965419"]]},{"id":"2eab771c6086f708","type":"global-config","env":[],"modules":{"@flowfuse-nodes/nr-ai-nodes":"0.1.6","node-red-contrib-image-tools":"2.1.1"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/flowfuse/ai/image-classification.md b/nuxt/content-guides/flowfuse-nodes/ai/image-classification.md
similarity index 98%
rename from src/node-red/flowfuse/ai/image-classification.md
rename to nuxt/content-guides/flowfuse-nodes/ai/image-classification.md
index 87b5c62418..f952019fec 100644
--- a/src/node-red/flowfuse/ai/image-classification.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/image-classification.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: Image Classification
- parent: AI
+title: Image Classification
+navTitle: Image Classification
+navOrder: 50
meta:
- title: Image Classification
description: Classify images using ONNX models directly in Node-RED. Supports pre-trained and custom models for tasks like labeling, content moderation, and object recognition.
---
-# {{ meta.title }}
+# Image Classification
The **Image Classification** node enables you to classify images using **ONNX models** directly within **Node-RED**.
It supports both **pre-trained** and **custom** models, allowing you to identify objects, detect scenes, or categorize images without requiring an external AI service.
@@ -121,6 +120,8 @@ Each object in the output array includes:
## Example Flow
-{% renderFlow 400 %}
+::render-flow{:height="400"}
+```json
[{"id":"80afcb4f0920c6ce","type":"http request","z":"e1ceeedf31ce1ebd","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":670,"y":3160,"wires":[["cef35d49d8f7e529"]]},{"id":"cef35d49d8f7e529","type":"change","z":"e1ceeedf31ce1ebd","name":"topK 3, thres: 5%","rules":[{"t":"move","p":"payload","pt":"msg","to":"image","tot":"msg"},{"t":"set","p":"topK","pt":"msg","to":"3","tot":"num"},{"t":"set","p":"thres","pt":"msg","to":"0.05","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":3160,"wires":[["d76d57912ca9fd3f"]]},{"id":"d76d57912ca9fd3f","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"","width":"224","data":"image","dataType":"msg","active":true,"x":1010,"y":3160,"wires":[["903b8d0c4750e324"]]},{"id":"fb880704ba86d144","type":"debug","z":"e1ceeedf31ce1ebd","name":"class","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload[0].label & \"(\" & $round(payload[0].score * 100,2) & \"%)\"","statusType":"jsonata","x":870,"y":3220,"wires":[]},{"id":"a8e34856bea9f3cd","type":"image-classification","z":"e1ceeedf31ce1ebd","name":"","property":"images","propertyType":"msg","model":"onnx-community/resnet-50-ONNX","modelType":"name","dtype":"fp16","topK":"1","topKType":"num","threshold":"0.1","thresholdType":"num","x":1080,"y":3800,"wires":[["7bd09a7daa0d5dae","0b07230327b5689c"]]},{"id":"903b8d0c4750e324","type":"image-classification","z":"e1ceeedf31ce1ebd","name":"","property":"image","propertyType":"msg","model":"Xenova/vit-base-patch16-224","modelType":"name","dtype":"q8","topK":"topK","topKType":"msg","threshold":"thres","thresholdType":"msg","x":700,"y":3220,"wires":[["fb880704ba86d144"]]},{"id":"e21197eababa2618","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"images[0]","width":"224","data":"images[0]","dataType":"msg","active":true,"x":660,"y":3800,"wires":[["d2b756bb0a700a0e"]]},{"id":"d2b756bb0a700a0e","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"images[1]","width":"224","data":"images[1]","dataType":"msg","active":true,"x":860,"y":3800,"wires":[["a8e34856bea9f3cd"]]},{"id":"b5768dffc25bf899","type":"inject","z":"e1ceeedf31ce1ebd","name":"beer","props":[{"p":"url","v":"https://stoelzle-lausitz.com/cdn/shop/files/stoelzle-lausitz-bierglaeser-glass-mug-full-beer-foam.png","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":450,"y":3200,"wires":[["49f8de9173d09a2f"]]},{"id":"0ff2faa6328f5e8c","type":"inject","z":"e1ceeedf31ce1ebd","name":"wolf","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Eurasian_wolf_2.jpg/1920px-Eurasian_wolf_2.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":450,"y":3160,"wires":[["49f8de9173d09a2f"]]},{"id":"5833b4f0c1928af6","type":"inject","z":"e1ceeedf31ce1ebd","name":"owl","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Bubo_bubo_sibiricus_-_01.JPG/1024px-Bubo_bubo_sibiricus_-_01.JPG","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":450,"y":3240,"wires":[["49f8de9173d09a2f"]]},{"id":"7bd09a7daa0d5dae","type":"debug","z":"e1ceeedf31ce1ebd","name":"class","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload[0][0].label & \"(\" & $round(payload[0][0].score * 100,2) & \"%)\"","statusType":"jsonata","x":1270,"y":3760,"wires":[]},{"id":"0b07230327b5689c","type":"debug","z":"e1ceeedf31ce1ebd","name":"class","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload[1][0].label & \"(\" & $round(payload[1][0].score * 100,2) & \"%)\"","statusType":"jsonata","x":1270,"y":3820,"wires":[]},{"id":"c799ffb88823e69c","type":"comment","z":"e1ceeedf31ce1ebd","name":"Image Classification","info":"","x":470,"y":3060,"wires":[]},{"id":"99eb75b743ba98ea","type":"comment","z":"e1ceeedf31ce1ebd","name":"Batch Image Classification","info":"","x":510,"y":3700,"wires":[]},{"id":"55506dfb9dc40daf","type":"change","z":"e1ceeedf31ce1ebd","name":"move payload images array","rules":[{"t":"set","p":"images","pt":"msg","to":"[]","tot":"json"},{"t":"move","p":"payload","pt":"msg","to":"images[0]","tot":"msg"},{"t":"set","p":"url","pt":"msg","to":"urls[1]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":775,"y":3740,"wires":[["2b8af48bb3f824bc"]],"l":false},{"id":"c9b1c4ff34ec7d02","type":"change","z":"e1ceeedf31ce1ebd","name":"move payload images array","rules":[{"t":"move","p":"payload","pt":"msg","to":"images[1]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":995,"y":3740,"wires":[["e21197eababa2618"]],"l":false},{"id":"c7c8d609c2bfaffc","type":"inject","z":"e1ceeedf31ce1ebd","name":"wolf+clock","props":[{"p":"urls","v":"[]","vt":"json"},{"p":"urls[0]","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Eurasian_wolf_2.jpg/1920px-Eurasian_wolf_2.jpg","vt":"str"},{"p":"urls[1]","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Pendulum_clock_by_Jacob_Kock%2C_antique_furniture_photography%2C_IMG_0931_edit.jpg/250px-Pendulum_clock_by_Jacob_Kock%2C_antique_furniture_photography%2C_IMG_0931_edit.jpg","vt":"str"},{"p":"url","v":"urls[0]","vt":"msg"},{"p":"preprocessorConfigOverrides","v":"{\"size\": {\"width\":224, \"height\":224}}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":460,"y":3740,"wires":[["2dd6833f25e7a905"]]},{"id":"2dd6833f25e7a905","type":"http request","z":"e1ceeedf31ce1ebd","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":670,"y":3740,"wires":[["55506dfb9dc40daf"]]},{"id":"2b8af48bb3f824bc","type":"http request","z":"e1ceeedf31ce1ebd","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":890,"y":3740,"wires":[["c9b1c4ff34ec7d02"]]},{"id":"79f068f1e84fdef2","type":"inject","z":"e1ceeedf31ce1ebd","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":450,"y":3480,"wires":[["c7b482660e93f645"]]},{"id":"c7b482660e93f645","type":"file in","z":"e1ceeedf31ce1ebd","name":"","filename":"dog.jpg","filenameType":"str","format":"","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":660,"y":3480,"wires":[["64b15839a5d33ad4"]]},{"id":"70d9f015166b7f63","type":"comment","z":"e1ceeedf31ce1ebd","name":"Auto preprocessing: Using Image as input","info":"","x":560,"y":3120,"wires":[]},{"id":"0c1b6f94772cf829","type":"inject","z":"e1ceeedf31ce1ebd","name":"plane","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/e/eb/British_Airways_Concorde_G-BOAC_03.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":450,"y":3280,"wires":[["49f8de9173d09a2f"]]},{"id":"584b4e035f0e91dd","type":"comment","z":"e1ceeedf31ce1ebd","name":"Auto preprocessing: Using local file Image as input. \\n NOTE: You will need to add a dog.jpg image to test this","info":"","x":600,"y":3420,"wires":[]},{"id":"25a103fa3e5ced6e","type":"image-classification","z":"e1ceeedf31ce1ebd","name":"","property":"payload","propertyType":"msg","model":"onnx-community/resnet-50-ONNX","modelType":"name","dtype":"fp16","topK":"1","topKType":"num","threshold":"0.5","thresholdType":"num","x":700,"y":3540,"wires":[["7af6dd11b8160481"]]},{"id":"7af6dd11b8160481","type":"debug","z":"e1ceeedf31ce1ebd","name":"class","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload[0].label & \"(\" & $round(payload[0].score * 100,2) & \"%)\"","statusType":"jsonata","x":870,"y":3540,"wires":[]},{"id":"64b15839a5d33ad4","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"","width":"180","data":"payload","dataType":"msg","active":true,"x":1010,"y":3480,"wires":[["25a103fa3e5ced6e"]]},{"id":"49f8de9173d09a2f","type":"junction","z":"e1ceeedf31ce1ebd","x":560,"y":3160,"wires":[["80afcb4f0920c6ce"]]},{"id":"1012c4c8ef915cdd","type":"global-config","env":[],"modules":{"node-red-contrib-image-tools":"2.1.1","@flowfuse-nodes/nr-ai-nodes":"0.1.6","@flowfuse/nr-file-nodes":"0.0.8"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/flowfuse/ai/llm-nodes.md b/nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md
similarity index 98%
rename from src/node-red/flowfuse/ai/llm-nodes.md
rename to nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md
index a6a571d841..c8d93055db 100644
--- a/src/node-red/flowfuse/ai/llm-nodes.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: LLM Nodes
- parent: AI
- order: 5
+title: LLM Nodes
+navTitle: LLM Nodes
+navOrder: 5
meta:
- title: LLM Nodes
description: Send text prompts to hosted and local large language models from OpenAI, Anthropic, Google Gemini, and Ollama directly within Node-RED flows.
---
-# {{ meta.title }}
+# LLM Nodes
The **LLM nodes** send a text prompt to a large language model and return the model's text response. Four provider nodes are included: **OpenAI**, **Anthropic (Claude)**, **Google Gemini**, and **Ollama**. They share a common configuration and message contract so they can be used interchangeably in a flow.
@@ -49,6 +47,8 @@ The node expects a text prompt in the configured input property (default: `msg.p
The following example sends the same prompt to OpenAI, Anthropic, and Gemini in parallel. It expects the relevant API keys to be available as environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and `GEMINI_API_KEY`). Each provider branch runs independently, so the flow works with only one or two keys configured.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"e4180867a7047b1f","type":"group","z":"6ab76a52887948e7","style":{"stroke":"#555555","stroke-opacity":"1","fill":"rgba(255, 255, 255, 0.03)","fill-opacity":"1","label":true,"label-position":"nw","color":"#f0f0f0"},"nodes":["35993459d37b6f93","152c15fad2618e06","d31647cc21cfb203","810f53d0e1fd9948","8fc45bb0b9dd8aea","0268271895881e33","a8ae18854eb8b60f","28e9d111cbe9274f","cdc5f07bfba274e9","8df6bcc3d93baa32","937896ec93065516"],"x":114,"y":99,"w":652,"h":362},{"id":"35993459d37b6f93","type":"ff-ai-openai","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"","apiKeyEnv":"OPENAI_API_KEY","apiKeyEnvType":"env","model":"gpt-4.1-mini","modelType":"str","temperature":"0.7","temperatureType":"num","maxTokens":"1024","maxTokensType":"num","timeoutMs":"30000","timeoutMsType":"num","system":"","systemType":"str","organization":"","project":"","sendClientRequestId":false,"x":410,"y":140,"wires":[["d31647cc21cfb203"]]},{"id":"152c15fad2618e06","type":"inject","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"ISS info","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"what speed does the ISS orbit the earth and how many times does it orbit in 24h?","payloadType":"str","x":210,"y":140,"wires":[["35993459d37b6f93"]]},{"id":"d31647cc21cfb203","type":"debug","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"openai response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":630,"y":140,"wires":[]},{"id":"810f53d0e1fd9948","type":"ff-ai-anthropic","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"","apiKeyEnv":"ANTHROPIC_API_KEY","apiKeyEnvType":"env","model":"claude-haiku-4-5","modelType":"str","temperature":"0.7","temperatureType":"num","maxTokens":"512","maxTokensType":"num","timeoutMs":"30000","timeoutMsType":"num","system":"","systemType":"str","endpoint":"https://api.anthropic.com/v1/messages","anthropicVersion":"2023-06-01","sendClientRequestId":false,"x":420,"y":220,"wires":[["28e9d111cbe9274f"]]},{"id":"8fc45bb0b9dd8aea","type":"inject","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"ISS info","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"what speed does the ISS orbit the earth and how many times does it orbit in 24h?","payloadType":"str","x":210,"y":220,"wires":[["810f53d0e1fd9948"]]},{"id":"0268271895881e33","type":"ff-ai-gemini","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"","apiKeyEnv":"GEMINI_API_KEY","apiKeyEnvType":"env","model":"gemini-3.1-flash-lite","modelType":"str","temperature":"0.7","temperatureType":"num","maxTokens":"1024","maxTokensType":"num","timeoutMs":"30000","timeoutMsType":"num","system":"","systemType":"str","endpointTemplate":"https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:generateContent","sendClientRequestId":false,"x":410,"y":300,"wires":[["cdc5f07bfba274e9"]]},{"id":"a8ae18854eb8b60f","type":"inject","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"ISS info","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"what speed does the ISS orbit the earth and how many times does it orbit in 24h?","payloadType":"str","x":210,"y":300,"wires":[["0268271895881e33"]]},{"id":"28e9d111cbe9274f","type":"debug","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"claude response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":620,"y":220,"wires":[]},{"id":"cdc5f07bfba274e9","type":"debug","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"gemini response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":620,"y":300,"wires":[]},{"id":"8df6bcc3d93baa32","type":"catch","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"","scope":"group","uncaught":false,"x":230,"y":420,"wires":[["937896ec93065516"]]},{"id":"937896ec93065516","type":"debug","z":"6ab76a52887948e7","g":"e4180867a7047b1f","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":590,"y":420,"wires":[]}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/flowfuse/ai/object-detection.md b/nuxt/content-guides/flowfuse-nodes/ai/object-detection.md
similarity index 98%
rename from src/node-red/flowfuse/ai/object-detection.md
rename to nuxt/content-guides/flowfuse-nodes/ai/object-detection.md
index 5f9e647874..eadc2f4697 100644
--- a/src/node-red/flowfuse/ai/object-detection.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/object-detection.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: Object Detection
- parent: AI
+title: Object Detection
+navTitle: Object Detection
+navOrder: 50
meta:
- title: Object Detection
description: The Object Detection node identifies and locates objects within images using ONNX models such as YOLO and DETR, enabling real-time computer vision directly in Node-RED without external AI services.
---
-# {{ meta.title }}
+# Object Detection
The **Object Detection** node enables detection of objects within images using **ONNX models**.
It supports a wide range of architectures, including **DETR** and **YOLO**, and accepts image data in multiple formats such as Buffers, base64 strings, or tensors.
@@ -135,6 +134,8 @@ You can then use a **Function** node for custom post-processing.
## Example Flow
-{% renderFlow %}
+::render-flow
+```json
[{"id":"6a3ca8414acfad34","type":"http request","z":"e1ceeedf31ce1ebd","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":610,"y":1180,"wires":[["f4998c6a9004a6ba"]]},{"id":"f4998c6a9004a6ba","type":"change","z":"e1ceeedf31ce1ebd","name":"","rules":[{"t":"set","p":"image","pt":"msg","to":"payload","tot":"msg","dc":true}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":1240,"wires":[["d902ee154cb6ceac"]]},{"id":"b1a008f95453208e","type":"inject","z":"e1ceeedf31ce1ebd","name":"people on bikes","props":[{"p":"url","v":"https://learnopencv.com/wp-content/uploads/2021/04/vehicle-traffic-object-detection-test-image.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":380,"y":1240,"wires":[["47d587d70bac373c"]]},{"id":"8d18b7c3c2197981","type":"inject","z":"e1ceeedf31ce1ebd","name":"dog bike truck","props":[{"p":"url","v":"https://djl.ai/examples/src/test/resources/dog_bike_car.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":370,"y":1200,"wires":[["47d587d70bac373c"]]},{"id":"86693a849586867a","type":"inject","z":"e1ceeedf31ce1ebd","name":"desk","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Schreibtisch.2.JPG/450px-Schreibtisch.2.JPG","vt":"str"},{"p":"threshold","v":"0.85","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":350,"y":1160,"wires":[["47d587d70bac373c"]]},{"id":"9113dce8475110f5","type":"object-detection","z":"e1ceeedf31ce1ebd","name":"","property":"image","propertyType":"msg","model":"Xenova/detr-resnet-50","modelType":"name","dtype":"fp16","threshold":"threshold","thresholdType":"msg","x":590,"y":1300,"wires":[["2ccb4544d3d5188d"]]},{"id":"2ccb4544d3d5188d","type":"change","z":"e1ceeedf31ce1ebd","name":"","rules":[{"t":"set","p":"result","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"image","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":1300,"wires":[["385ba9c93695c86b"]]},{"id":"a2ef9dc6bf6007e2","type":"debug","z":"e1ceeedf31ce1ebd","name":"debug 15","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"result","targetType":"msg","statusVal":"","statusType":"auto","x":1100,"y":1360,"wires":[]},{"id":"385ba9c93695c86b","type":"function","z":"e1ceeedf31ce1ebd","name":"Pascal VOC to COCO bbox","func":"const annotations = msg.result.slice(0,20)\n// convert Pascal VOC Format bbox to coco format\nconst bboxes = []\nfor (const annotation of annotations) {\n const [ xmin, ymin, xmax, ymax ] = annotation.bbox\n const width = xmax - xmin\n const height = ymax - ymin\n const percent = annotation.score * 100\n bboxes.push({\n label: `${annotation.label} (${percent.toFixed(1)}%)`,\n bbox: [xmin, ymin, width, height]\n })\n}\nmsg.annotations = bboxes\nreturn msg\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1040,"y":1300,"wires":[["da4063f9d3a2548b"]]},{"id":"da0ae2dfd2df248f","type":"inject","z":"e1ceeedf31ce1ebd","name":"football","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Football_in_Bloomington%2C_Indiana%2C_1995.jpg/500px-Football_in_Bloomington%2C_Indiana%2C_1995.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":350,"y":1120,"wires":[["47d587d70bac373c"]]},{"id":"f23ba6823adaab87","type":"comment","z":"e1ceeedf31ce1ebd","name":"Object Detection","info":"","x":360,"y":1080,"wires":[]},{"id":"d902ee154cb6ceac","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"","width":"240","data":"image","dataType":"msg","active":true,"x":350,"y":1300,"wires":[["9113dce8475110f5"]]},{"id":"d75c7f464cca14c1","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"","width":"720","data":"payload","dataType":"msg","active":true,"x":830,"y":1360,"wires":[["a2ef9dc6bf6007e2"]]},{"id":"da4063f9d3a2548b","type":"annotate-image","z":"e1ceeedf31ce1ebd","name":"","fill":"","stroke":"#ffA000","lineWidth":"3","fontSize":24,"fontColor":"#ffA000","x":660,"y":1360,"wires":[["d75c7f464cca14c1"]]},{"id":"47d587d70bac373c","type":"junction","z":"e1ceeedf31ce1ebd","x":500,"y":1180,"wires":[["6a3ca8414acfad34"]]},{"id":"8ee41c61e110e410","type":"global-config","env":[],"modules":{"@flowfuse-nodes/nr-ai-nodes":"0.1.6","node-red-contrib-image-tools":"2.1.1","node-red-node-annotate-image":"0.2.0"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/flowfuse/ai/onxx.md b/nuxt/content-guides/flowfuse-nodes/ai/onxx.md
similarity index 98%
rename from src/node-red/flowfuse/ai/onxx.md
rename to nuxt/content-guides/flowfuse-nodes/ai/onxx.md
index 2a68fe7d13..978a84ed20 100644
--- a/src/node-red/flowfuse/ai/onxx.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/onxx.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: ONXX
- parent: AI
+title: ONXX
+navTitle: ONXX
+navOrder: 50
meta:
- title: ONXX
- description: The ONNX node allows you to perform AI inference directly in Node-RED using ONNX models, supporting image, object, and numeric predictions without external AI services.
+ description: The ONNX node allows you to perform AI inference directly in Node-RED using ONNX models, supporting image, object, and numeric predictions without external AI services.
---
-# {{ meta.title }}
+# ONXX
The **ONNX** node allows you to perform AI inference directly in **Node-RED** using **ONNX models**.
It can run a wide range of pre-trained or custom models, including image classification, object detection, and numeric prediction tasks.
@@ -120,6 +119,8 @@ For batch inputs, use an array of arrays:
## Example Flow
-{% renderFlow %}
+::render-flow
+```json
[{"id":"239b5347fce92ca9","type":"function","z":"e1ceeedf31ce1ebd","name":"load labels","func":"// flag to let the node know we are handling preprocessing\nmsg.noPreprocessorConfig = true\n\nmsg.config = {\n label2id : {\n \"apple\": 0,\n \"kiwi\": 1,\n \"mango\": 2\n },\n id2label : {\n \"0\": \"apple\",\n \"1\": \"kiwi\",\n \"2\": \"mango\"\n }\n}\nreturn msg\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":5860,"wires":[["117470780ea20643"]]},{"id":"117470780ea20643","type":"function","z":"e1ceeedf31ce1ebd","name":"preprocessing","func":"// ImageNet normalization values\nconst IMG_MEAN = [0.485, 0.456, 0.406]\nconst IMG_STD = [0.229, 0.224, 0.225]\n\n// The width and height expected by the model\nconst WIDTH = 224\nconst HEIGHT = 224\n\n/**\n * Load and preprocess image for PyTorch ONNX model\n * @param {Buffer} buffer - an image\n * @returns {Promise} - Tensor ready for ort.run\n */\nasync function preprocessImage(buffer) {\n // Load and resize image\n const resolved = await sharp(buffer)\n .resize(WIDTH, HEIGHT)\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n const { data, info } = resolved; // data = Uint8Array, info = {width, height, channels}\n const { width, height, channels } = info;\n\n if (channels !== 3) {\n throw new Error(`Expected 3 channels (RGB), got ${channels}`);\n }\n\n // Convert to float32 and normalize\n const floatData = new Float32Array(width * height * channels);\n for (let i = 0; i < width * height; i++) {\n for (let c = 0; c < 3; c++) {\n // data is 0..255, convert to 0..1\n const v = data[i * 3 + c] / 255.0;\n floatData[c * width * height + i] = (v - IMG_MEAN[c]) / IMG_STD[c];\n // Notice: channel-first layout (C,H,W)\n }\n }\n\n return floatData;\n}\n\nmsg.payload = {\n type: 'float32',\n dims: [1, 3, HEIGHT, WIDTH], // 1 image, 3 chanels, HEIGHT, WIDTH\n data: await preprocessImage(msg.payload)\n}\n// msg.payload = await preprocessImage(msg.payload)\nreturn msg\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"sharp","module":"sharp"}],"x":1020,"y":5860,"wires":[["36477656b418b6f7"]]},{"id":"36477656b418b6f7","type":"advanced-ai","z":"e1ceeedf31ce1ebd","name":"fruit_classifier","property":"payload","propertyType":"msg","model":"C:/Users/sdmcl/repos/node-red/model_cache/flowfuse/fruit_classifier/onnx/model.onnx","modelType":"path","x":900,"y":5920,"wires":[["aa2b68b2ca30ee34"]]},{"id":"aa2b68b2ca30ee34","type":"function","z":"e1ceeedf31ce1ebd","name":"Sort and Label","func":"/*\nData arrives like so:\nmsg.payload.output.cpuData: {\"0\":1.7446770668029785, 1: -2.4252512454986572, 2: 2.786302089691162}\n*/\nconst cpuData = msg.payload.output.cpuData\nconst labels = []\nconst ids = Object.keys(msg.config.id2label).forEach(id => {\n labels.push(msg.config.id2label[id])\n})\n\n// Convert cpuData to logits array\nconst logits = Object.values(cpuData)\n\n// Softmax function\nfunction softmax(arr) {\n const exp = arr.map(x => Math.exp(x))\n const sum = exp.reduce((a, b) => a + b, 0)\n return exp.map(x => x / sum)\n}\n\nconst probs = softmax(logits)\n\n// Build sorted array of results with labels and probabilities\nconst resultArray = probs\n .map((prob, idx) => ({\n classIndex: idx,\n className: labels[idx] || 'Unknown',\n confidence: prob\n }))\n .filter(item => item.confidence > 0)\n .sort((a, b) => b.confidence - a.confidence)\n\nmsg.payload = resultArray\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1080,"y":5920,"wires":[["f3b605669eb8b00b"]]},{"id":"f3b605669eb8b00b","type":"debug","z":"e1ceeedf31ce1ebd","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload[0].className & \" (\" & $round(payload[0].confidence * 100, 2) & \"%)\"","statusType":"jsonata","x":1070,"y":5980,"wires":[]},{"id":"114028180bd0659b","type":"inject","z":"e1ceeedf31ce1ebd","name":"apple","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Honeycrisp-Apple.jpg/1200px-Honeycrisp-Apple.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":430,"y":5800,"wires":[["bc8648c963842493"]]},{"id":"709d0cacee000fde","type":"inject","z":"e1ceeedf31ce1ebd","name":"apple","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Fuji_apple.jpg/1200px-Fuji_apple.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":430,"y":5840,"wires":[["bc8648c963842493"]]},{"id":"bc8648c963842493","type":"http request","z":"e1ceeedf31ce1ebd","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":630,"y":5800,"wires":[["e6c4bc36b25440e5"]]},{"id":"38ce06ccd2500c74","type":"inject","z":"e1ceeedf31ce1ebd","name":"kiwi","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Kiwi_%28Actinidia_chinensis%29_2_Luc_Viatour.jpg/250px-Kiwi_%28Actinidia_chinensis%29_2_Luc_Viatour.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":430,"y":5880,"wires":[["bc8648c963842493"]]},{"id":"8c2a2e98a21f319b","type":"inject","z":"e1ceeedf31ce1ebd","name":"mango","props":[{"p":"url","v":"https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Mangos_-_single_and_halved.jpg/500px-Mangos_-_single_and_halved.jpg","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":430,"y":5920,"wires":[["bc8648c963842493"]]},{"id":"e6c4bc36b25440e5","type":"image viewer","z":"e1ceeedf31ce1ebd","name":"","width":"224","data":"payload","dataType":"msg","active":true,"x":610,"y":5860,"wires":[["239b5347fce92ca9"]]},{"id":"5711f41ecfe998d6","type":"comment","z":"e1ceeedf31ce1ebd","name":"Using a re-trained resnet model trained to recognise apples, kiwis and mangos \\n See info in the INFO panel on the sidebar","info":"This Node-RED demo flow requires you to have trained a model to recognize fruit types (apple, kiwi, mango) using a labeled image dataset.\n\nThe process involves:\n1. Setting up your Python environment with PyTorch, TorchVision, ONNX, and ONNX Runtime.\n1. Organizing your dataset into train, validation, and test folders for each class.\n1. Using transfer learning with a pre-trained ResNet18 model, fine-tuned on your images.\n1. Training the model and evaluating its accuracy.\n1. Exporting the trained PyTorch model to ONNX format for interoperability.\n1. Optionally, testing the ONNX model with ONNX Runtime to verify predictions.\n\nOnce you have the exported ONNX model (e.g., fruit_classifier.onnx), you can use it with this demo flow for inference.","x":630,"y":5740,"wires":[]},{"id":"dcc5015a105e33bb","type":"global-config","env":[],"modules":{"@flowfuse-nodes/nr-ai-nodes":"0.1.6","node-red-contrib-image-tools":"2.1.1"}}]
-{% endrenderFlow %}
\ No newline at end of file
+```
+::
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/README.md b/nuxt/content-guides/flowfuse-nodes/edge/README.md
new file mode 100644
index 0000000000..c9f63a6b0a
--- /dev/null
+++ b/nuxt/content-guides/flowfuse-nodes/edge/README.md
@@ -0,0 +1,40 @@
+---
+title: Edge Certified Nodes
+navTitle: Edge
+navOrder: 4
+meta:
+ description: Documentation for FlowFuse Edge Certified Nodes, including nodes for connecting FlowFuse to industrial protocols, PLCs, SCADA systems, and factory-floor equipment.
+---
+
+# Edge Certified Nodes
+
+This section contains documentation for **FlowFuse Edge Certified Nodes** that connect your FlowFuse instances to industrial protocols, PLCs, SCADA systems, and factory-floor equipment.
+
+FlowFuse Certified Nodes are packages that FlowFuse has vetted for quality, security, and support, and maintains on an ongoing basis. To learn more about what certification means and how these nodes are delivered, [read the FlowFuse Certified Nodes blog post](/blog/2025/07/certified-nodes-v2/).
+
+
Note
+
+The FlowFuse Edge Certified Nodes catalogue is part of the **FlowFuse Edge** offering. [Contact us](/contact-us/) to get access or to learn more.
+
+
+
+
Note
+
+This section is expanding. We are actively working to bring more Edge Certified Nodes to FlowFuse, and additional documentation will be added here over time.
+
+
+
+## Nodes
+
+This section lists the **Edge Certified Nodes** documented in FlowFuse:
+
+- [RTSP Video Feed](/docs/flowfuse-nodes/edge/rtsp/): Documentation for the FlowFuse RTSP Video Feed node, which connects to an RTSP camera stream and extracts still frames as PNG images for use in flows, dashboards, and local AI models.
+- [CIP Suite, EtherNet/IP Nodes](/docs/flowfuse-nodes/edge/cip-suite/): A suite of nodes for reading, writing, and monitoring data on Rockwell Automation and Allen-Bradley PLCs, and other CIP-capable devices, using the EtherNet/IP protocol.
+- [Modbus](/docs/flowfuse-nodes/edge/modbus/): A FlowFuse-certified package for reading and writing coils and registers over Modbus TCP, Modbus UDP (where supported), and Serial (RTU/ASCII), and for simulating a Modbus server, all from within your flows.
+- [OPC UA for FlowFuse - FlowFuse Certified Node](/docs/flowfuse-nodes/edge/opcua/): Connect a FlowFuse instance to industrial OPC UA servers: read, write, monitor, call methods, browse, read history, work with files, or host your own OPC UA server. A FlowFuse Certified Node.
+
+
Note
+
+Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
+
+
diff --git a/src/node-red/flowfuse/edge/cip-suite.md b/nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md
similarity index 95%
rename from src/node-red/flowfuse/edge/cip-suite.md
rename to nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md
index 59beccf64c..fcdb887c7c 100644
--- a/src/node-red/flowfuse/edge/cip-suite.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: CIP Suite, EtherNet/IP Nodes
- parent: Edge
- order: 2
+title: CIP Suite, EtherNet/IP Nodes
+navTitle: CIP Suite, EtherNet/IP Nodes
+navOrder: 2
meta:
- title: CIP Suite, EtherNet/IP Nodes
description: A suite of nodes for reading, writing, and monitoring data on Rockwell Automation and Allen-Bradley PLCs, and other CIP-capable devices, using the EtherNet/IP protocol.
---
-# {{ meta.title }}
+# CIP Suite, EtherNet/IP Nodes
A suite of nodes for communicating with Rockwell Automation and Allen-Bradley PLCs, and other CIP-capable devices, using the EtherNet/IP (Ethernet Industrial Protocol) protocol.
@@ -18,9 +16,11 @@ The CIP Suite connects FlowFuse flows to industrial control systems across the f
This is a **FlowFuse Certified Node**. Unlike community nodes, which vary in quality and can go unmaintained without warning, FlowFuse vets Certified Nodes for quality, security, and support, and maintains them on an ongoing basis. [Read more about Certified Nodes](/blog/2025/07/certified-nodes-v2/). It is built on the [st-ethernet-ip](https://www.npmjs.com/package/st-ethernet-ip) protocol driver.
-{% note %}
+
Note
+
The CIP Suite is not available by default. It is part of the FlowFuse Edge Certified Nodes catalogue, which is part of the **FlowFuse Edge** offering. Please contact our sales team at [Contact us](/contact-us/) to learn more or to request access.
-{% endnote %}
+
+
## What is EtherNet/IP?
@@ -51,7 +51,7 @@ With the CIP Suite, the flow looks like this:
1. **Connect**: a `cip-endpoint` configuration node holds the shared connection to the PLC, with automatic reconnection.
2. **Subscribe**: a `cip-subscribe` node scans the tags cyclically and emits messages on change, with deadband filtering to suppress noise.
3. **Enrich**: a `function` node attaches a line ID, timestamp, or shift code, and converts raw units into something business-readable.
-4. **Deliver**: the enriched payload is published to an MQTT broker or Unified Namespace, written to a historian, or dropped straight into [FlowFuse Tables](/node-red/flowfuse/flowfuse-tables/).
+4. **Deliver**: the enriched payload is published to an MQTT broker or Unified Namespace, written to a historian, or dropped straight into [FlowFuse Tables](/docs/flowfuse-nodes/flowfuse-tables/).
The result: production counts, machine states, and reject rates are available to dashboards, analytics, and other systems within moments of changing on the floor. No polling scripts, no OPC server to license and maintain, no manual tag mapping in a separate SCADA package.
@@ -66,7 +66,7 @@ The result: production counts, machine states, and reject rates are available to
### Why it scales beyond one PLC
-Only the source and sink nodes are protocol-specific. The transform stages of a flow, such as renaming tags, adding context, and routing data, don't care what protocol the data came from. The same flow pattern you build for one Allen-Bradley line can be reused for a Siemens PLC over [OPC UA](/node-red/flowfuse/edge/opcua/) or a Modbus device just by swapping the protocol nodes, which matters a lot on a mixed-vendor plant floor.
+Only the source and sink nodes are protocol-specific. The transform stages of a flow, such as renaming tags, adding context, and routing data, don't care what protocol the data came from. The same flow pattern you build for one Allen-Bradley line can be reused for a Siemens PLC over [OPC UA](/docs/flowfuse-nodes/edge/opcua/) or a Modbus device just by swapping the protocol nodes, which matters a lot on a mixed-vendor plant floor.
## Requirements
@@ -78,9 +78,11 @@ Only the source and sink nodes are protocol-specific. The transform stages of a
Because this suite is part of the FlowFuse Edge Certified Nodes catalogue, which is part of the **FlowFuse Edge** offering, make sure your account has access before installing. Contact our [sales team](/contact-us/) if you don't.
-{% note %}
+
Note
+
Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
-{% endnote %}
+
+
### Install via the Palette Manager (recommended)
@@ -289,4 +291,4 @@ The `cip-pccc-read` node returns data-file values from SLC 500, MicroLogix, and
### Connection Drops Frequently
- Reduce scan rates to decrease network load.
- Check network stability and switch configuration.
-- Verify the PLC isn't overloaded with connections from other clients.
\ No newline at end of file
+- Verify the PLC isn't overloaded with connections from other clients.
diff --git a/src/node-red/flowfuse/edge/images/opcua/endpoint-add-new.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-add-new.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/endpoint-add-new.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-add-new.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/endpoint-subscriptions.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-subscriptions.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/endpoint-subscriptions.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-subscriptions.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/endpoint-verification-secure-error.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-verification-secure-error.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/endpoint-verification-secure-error.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-verification-secure-error.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/endpoint-verification-user-access-denied.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-verification-user-access-denied.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/endpoint-verification-user-access-denied.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-verification-user-access-denied.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/endpoint-verification.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-verification.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/endpoint-verification.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/endpoint-verification.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/example-all-attributes-on-rfid-scan-result.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/example-all-attributes-on-rfid-scan-result.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/example-all-attributes-on-rfid-scan-result.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/example-all-attributes-on-rfid-scan-result.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/node-palette.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/node-palette.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/node-palette.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/node-palette.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/nodeid-as-browse-path.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/nodeid-as-browse-path.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/nodeid-as-browse-path.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/nodeid-as-browse-path.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/opcu-client2-read.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/opcu-client2-read.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/opcu-client2-read.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/opcu-client2-read.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/opcua-client2-monitor.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/opcua-client2-monitor.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/opcua-client2-monitor.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/opcua-client2-monitor.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/read-showing-endpoint-field.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/read-showing-endpoint-field.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/read-showing-endpoint-field.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/read-showing-endpoint-field.png
diff --git a/src/node-red/flowfuse/edge/images/opcua/use-node-browser-to-select-node-id.png b/nuxt/content-guides/flowfuse-nodes/edge/images/opcua/use-node-browser-to-select-node-id.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/opcua/use-node-browser-to-select-node-id.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/opcua/use-node-browser-to-select-node-id.png
diff --git a/src/node-red/flowfuse/edge/images/rtsp/rtsp-config-node.png b/nuxt/content-guides/flowfuse-nodes/edge/images/rtsp/rtsp-config-node.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/rtsp/rtsp-config-node.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/rtsp/rtsp-config-node.png
diff --git a/src/node-red/flowfuse/edge/images/rtsp/rtsp-edge-catalog.png b/nuxt/content-guides/flowfuse-nodes/edge/images/rtsp/rtsp-edge-catalog.png
similarity index 100%
rename from src/node-red/flowfuse/edge/images/rtsp/rtsp-edge-catalog.png
rename to nuxt/content-guides/flowfuse-nodes/edge/images/rtsp/rtsp-edge-catalog.png
diff --git a/src/node-red/flowfuse/edge/modbus.md b/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
similarity index 93%
rename from src/node-red/flowfuse/edge/modbus.md
rename to nuxt/content-guides/flowfuse-nodes/edge/modbus.md
index fc85dfdfd5..a1ecda2585 100644
--- a/src/node-red/flowfuse/edge/modbus.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: Modbus
- parent: Edge
- order: 3
+title: Modbus
+navTitle: Modbus
+navOrder: 3
meta:
- title: Modbus
description: A FlowFuse-certified package for reading and writing coils and registers over Modbus TCP, Modbus UDP (where supported), and Serial (RTU/ASCII), and for simulating a Modbus server, all from within your flows.
---
-# {{ meta.title }}
+# Modbus
Connect a FlowFuse instance to Modbus TCP and Serial devices — PLCs, sensors, meters, drives, and gateways. Poll or read coils and registers, write setpoints and commands, and simulate a Modbus server for testing, all from within your flows.
@@ -16,9 +14,11 @@ This is a **FlowFuse Certified Node**. Unlike community nodes, which vary in qua
## Get the Certified Node in FlowFuse
-{% note %}
+
Note
+
The Modbus package is not available by default. It is part of the FlowFuse Edge Certified Nodes catalogue, which is part of the **FlowFuse Edge** offering. Please contact our sales team at [Contact us](/contact-us/) to learn more or to request access.
-{% endnote %}
+
+
### Installation steps
@@ -32,9 +32,11 @@ The Modbus package is not available by default. It is part of the FlowFuse Edge
The Modbus nodes then appear in your palette, ready to drag onto the canvas.
-{% note %}
+
Note
+
If your device or hosted instance was already running before Modbus was enabled for your team, it won't show the package in the Install tab search. Restart the instance or device first, then repeat the steps above.
-{% endnote %}
+
+
## What is Modbus?
@@ -46,9 +48,11 @@ Modbus is one of the oldest and most widely supported industrial communication p
Modbus-Client also offers TELNET and C701 modes for TCP-to-serial gateways.
-{% note %}
+
Note
+
Modbus now uses **client** for the device making requests and **server** for the device answering them. Older device manuals and tools use **master** and **slave** for the same two roles — a Modbus master is a client, a Modbus slave is a server. This package follows the newer terminology, so the FlowFuse instance is the client and the PLC, meter, or drive it polls is the server.
-{% endnote %}
+
+
Every Modbus device exposes its data as one of four addressable table types, and every read or write targets one of them:
@@ -59,17 +63,21 @@ Every Modbus device exposes its data as one of four addressable table types, and
| Holding Registers | 3 | 6, 16 | Read/write, 16-bit | `4xxxx` / `40001+` | Setpoints, configuration, read/write process values |
| Input Registers | 4 | — | Read-only, 16-bit | `3xxxx` / `30001+` | Read-only measurements — sensor readings |
-{% note %}
+
Note
+
**Register addressing** Addresses in this package are raw, zero-based protocol addresses. Device manuals often number the same register differently. A manual that lists a holding register as `40001` (or `4x0001`, or `400001` on devices with more than 9,999 registers) is describing the register at address `0`. Other manuals number within the table starting at `1`, so their "register 1" is also address `0`. Check which convention your device's register map uses before entering an address — an off-by-one here is the most common cause of reading the wrong value, and an address one past the end of the table returns Illegal Data Address.
-{% endnote %}
+
+
A device is also identified by a **Unit ID** (also called station address, or slave ID in older documentation), which matters when several logical devices share one connection — for example several RTU devices on the same RS-485 bus, or several logical devices behind one TCP gateway.
On a serial bus, valid device addresses are **1–247**. Address **0 is the broadcast address** — a write sent to unit 0 goes to every device on the bus and none of them reply, so a read addressed to unit 0 will always time out. Addresses 248–255 are reserved. Native Modbus TCP devices often ignore the unit ID altogether (`0`, `1`, and `255` are all common), but it matters as soon as a gateway sits in front of serial devices.
-{% note %}
+
Note
+
Modbus itself moves only raw bits and 16-bit words — the protocol carries no data-type information. A read returns an **array** of booleans (coils/discrete inputs) or **raw 16-bit register values, each `0`–`65535`,** (input/holding registers). Anything larger or more structured — 32-bit integers, floating-point values, scaled measurements — spans multiple registers: the device packs it across them, and you reconstruct it in your flow. See [Decoding register values](#decoding-register-values) for how to do this.
-{% endnote %}
+
+
## Use case
@@ -110,9 +118,11 @@ Each node performs one Modbus operation and reuses the shared connection you con
| Modbus-Server | Runs a buffer-backed Modbus TCP server inside Node-RED, so it responds to reads and writes from an external Modbus client — useful for testing and simulation. |
| Modbus-Queue-Info | Reports (and can reset) the internal request queue depth for a connection, useful for spotting a device that can't keep up with the configured poll rate. |
-{% note %}
+
Note
+
All nodes that share the same Modbus-Client connection share one underlying socket/serial port and are queued through it in order, so a device is never sent two requests at once. A busy queue (see Modbus-Queue-Info) usually means the poll rate is faster than the device can respond to.
-{% endnote %}
+
+
## Configure a Modbus Client Connection
@@ -129,13 +139,17 @@ Every node in this package uses a **Modbus-Client** configuration node to commun
- **Reconnect Timeout** — Default: `2000` ms
5. Click **Done**, then **Deploy** the flow.
-{% note %}
+
Note
+
The connection is shared. Changing its parameters affects every node that uses it, and you must redeploy the flow for connection changes to take effect. To change the endpoint at runtime without redeploying, see [Modbus-Flex-Connector](#modbus-flex-connector).
-{% endnote %}
-{% note %}
+
+
+
Note
+
Keep host/IP addresses and serial port paths in FlowFuse Environment Variables (your instance's **Settings → Environment**) rather than hard-coding them in the connection, so the same flow can be promoted across instances that reach the device differently.
-{% endnote %}
+
+
## Modbus-Read
@@ -155,9 +169,11 @@ Each poll emits a message carrying the raw values on `msg.payload` (an array of
[Modbus-Read] (polls every 1000ms) → [function / buffer-parser] → [Debug]
```
-{% note %}
+
Note
+
Set the poll rate no faster than the device can reliably answer. A rate that outpaces the device causes requests to back up in the queue, check [Modbus-Queue-Info](#modbus-queue-info) if reads start arriving late or with gaps.
-{% endnote %}
+
+
## Modbus-Getter and Modbus-Flex-Getter
@@ -177,9 +193,11 @@ Modbus-Flex-Sequencer reads several configured ranges in order through a single
- **Modbus-Write** — the target table (Coils or Holding Registers), address, and quantity are fixed on the node; the value(s) to write come from the incoming message. Uses write function codes FC 5/6 (single coil/register) and FC 15/16 (multiple).
- **Modbus-Flex-Write** — the table, address, and value(s) are all taken from the incoming message, so a single node can write to different coils/registers depending on what triggers it.
-{% warning %}
+
Warning
+
Writes to production equipment change real-world state. Gate write nodes behind validation or an operator confirmation step before deploying to a live instance.
-{% endwarning %}
+
+
## Modbus-Flex-Fc
@@ -213,17 +231,21 @@ There are a few common approaches:
- **A buffer-parser node** — a dedicated parsing node (such as `node-red-contrib-buffer-parser`) accepts the values array directly, so you don't have to deal with the Buffer question at all. It lets you declare each field's type and endianness in configuration rather than in code, which is convenient when a single response contains many differently-typed fields.
- **The package's built-in IO mapping** — a **Modbus-IO-Config** node holds a JSON file that maps IEC-style addresses (`%QW0`, `%IX8.0`, and so on) to names, where the first character of each name selects the data type (`i` integer, `w` word, `u` unsigned, `b` boolean, `f`/`r` float, and so on). A read node can attach that mapping to its output, and **Modbus-Response-Filter** narrows the result down to the named fields a downstream node needs. This is convenient when your address list originates from a PLC export.
-{% note %}
+
Note
+
Choose the data type and word order that matches how the device packs its data, which is usually documented in the device's Modbus register map. If a decoded value looks wildly wrong (for example a temperature reading in the millions), the most common cause is a word-order mismatch on a multi-register value. Try the other byte/word order before assuming the device or wiring is at fault.
-{% endnote %}
+
+
## Modbus-Response
The Modbus-Response node displays the status and content of a Modbus response in the editor. Placed downstream of a read node, it is an inspection/diagnostic aid that shows what came back, which is helpful while building and debugging a flow.
-{% note %}
+
Note
+
Modbus-Response is for **display and inspection** — it does not convert raw registers into typed values. To turn raw registers into integers, floats, or scaled measurements, decode them in your flow as described in [Decoding register values](#decoding-register-values).
-{% endnote %}
+
+
## Modbus-Server
@@ -234,9 +256,11 @@ This is primarily useful for:
- **Testing flows without hardware** — stand up a server so a Modbus-Read/Write flow can be built and verified before real hardware is available.
- **Local integration testing** — exercise read and write paths end to end against a server running in the same instance.
-{% note %}
+
Note
+
The in-package Modbus-Server is a **buffer-backed server for demos and tests**. It is not intended to replicate a specific device's complete register-map behaviour. For more elaborate simulation needs, a dedicated Modbus simulation tool is a better fit.
-{% endnote %}
+
+
## Modbus-Queue-Info
diff --git a/src/node-red/flowfuse/edge/opcua.md b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
similarity index 95%
rename from src/node-red/flowfuse/edge/opcua.md
rename to nuxt/content-guides/flowfuse-nodes/edge/opcua.md
index 2f4f67751d..72327db9a0 100644
--- a/src/node-red/flowfuse/edge/opcua.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: OPC UA
- parent: Edge
+title: OPC UA for FlowFuse - FlowFuse Certified Node
+navTitle: OPC UA
+navOrder: 50
meta:
- title: "OPC UA for FlowFuse - FlowFuse Certified Node"
- description: "Connect a FlowFuse instance to industrial OPC UA servers: read, write, monitor, call methods, browse, read history, work with files, or host your own OPC UA server. A FlowFuse Certified Node."
+ description: 'Connect a FlowFuse instance to industrial OPC UA servers: read, write, monitor, call methods, browse, read history, work with files, or host your own OPC UA server. A FlowFuse Certified Node.'
---
-# {{meta.title}}
+# OPC UA for FlowFuse - FlowFuse Certified Node
Connect a FlowFuse instance to industrial OPC UA servers. Read and write values, monitor changes in real time, call methods, browse the address space, read history, work with files, or host your own OPC UA server, all from within your flows.
@@ -89,13 +88,17 @@ The OPC UA nodes then appear in your palette, ready to drag onto the canvas.
{data-zoomable}
-{% note %}
+
Note
+
Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
-{% endnote %}
-{% note %}
+
+
+
Note
+
Keep endpoint URLs, usernames, and passwords in FlowFuse Environment Variables (your instance's **Settings → Environment**) rather than hard-coding them in nodes. This keeps credentials out of your flow JSON and lets you promote the same flow across instances.
-{% endnote %}
+
+
## 3. The Node Set
@@ -115,13 +118,17 @@ Each node performs one OPC UA operation and reuses the shared connection you con
| History Read | Retrieve raw, modified, or aggregated historical data over a time range. |
| File Operation | Read, write, append, or size files on servers implementing the FileType interface. |
-{% note %}
+
Note
+
Properties of the input message take precedence over a node's own configuration. Configure a node statically, or drive it dynamically from upstream messages.
-{% endnote %}
-{% note %}
+
+
+
Note
+
Each node reports its state in the editor with a coloured status dot, grey (not connected), blue (operation in progress), green (success), red (failure), and on some nodes (such as History Read) yellow (partial success or quality issues). Watch it alongside the debug sidebar when wiring up a flow.
-{% endnote %}
+
+
## 4. NodeIds and How to Address Data
@@ -145,9 +152,11 @@ Clicking the browse (`...`) button opens the live address space, so you can pick
{data-zoomable}
-{% warning %}
+
Warning
+
Namespace indexes are not guaranteed stable across servers. If you move a flow between servers, prefer browse paths or the namespace alias table over hard-coded numeric namespace indexes.
-{% endwarning %}
+
+
### Browse path syntax
@@ -169,9 +178,11 @@ Every browse path starts with a reference-type separator and then a sequence of
| BadNoMatch | The full path does not exist on the server, or a namespace index is wrong. |
| BadReferenceTypeIdInvalid | A reference type name in the path is unknown or not namespace-qualified. |
-{% note %}
+
Note
+
Browse paths are convenient and portable, but resolution has a cost. For variables you read or write frequently, resolve the path once and use the returned NodeId, or use the verified-browse-path format that caches the NodeId alongside the path.
-{% endnote %}
+
+
## 5. Configure a Connection
@@ -189,9 +200,11 @@ Every OPC UA node depends on a connection defined by the **OPC UA Client** confi
4. Click **Save**, then **Done**.
5. Click **Deploy**, then open the debug sidebar to confirm data flows.
-{% note %}
+
Note
+
The connection is shared. Changing its parameters affects every node that uses it, and you must redeploy the flow for connection changes to take effect.
-{% endnote %}
+
+
### Connection editor areas
@@ -214,17 +227,21 @@ The connection is shared. Changing its parameters affects every node that uses i
{data-zoomable}
-{% warning %}
+
Warning
+
A username and password sent over message security mode `None` travel in cleartext. Combine credentials with at least `Sign` mode.
-{% endwarning %}
+
+
### Certificate trust
Secure connections rely on mutual certificate trust. By default the client side automatically accepts the server's certificate, so the step that usually needs action is the reverse: **the server must trust the client's certificate**. For a `Sign` or `SignAndEncrypt` connection, download the client certificate from the connection editor (in PEM or DER format) and add it to your OPC UA server's trusted list, otherwise the server refuses the connection.
-{% note %}
+
Note
+
Auto-accepting the server certificate is convenient but means the client does not verify the server's identity. To enforce server-certificate validation, set `rejectUnauthorized` to `true` in the connection node's global settings. The client then refuses any server whose certificate is not already in its trusted store, so you must add the server's certificate to the client side first.
-{% endnote %}
+
+
Client and server nodes in your instance share one PKI store. On FlowFuse it lives under `/opcua-for-flow-fuse/PKI`. For trust decisions to survive restarts and redeploys, that directory must be on persistent storage, see [Hosting an OPC UA server](#16.-hosting-an-opc-ua-server) for the storage details, which apply to client connections too.
@@ -249,9 +266,11 @@ The Read node fetches data on demand, it acts only when it receives an input mes
3. `msg.payload`
4. The node's configured NodeId
-{% warning %}
+
Warning
+
A default Inject node sets `msg.payload` to a timestamp, which silently overrides the node's configured NodeId (priority rule 3). To use the node's own configuration, ensure the Inject node does **not** set `msg.nodeId`, `msg.topic`, or `msg.payload`.
-{% endwarning %}
+
+
### Message properties
@@ -372,9 +391,11 @@ A Write returns the original `payload` and `nodeId` plus the result: `statusCode
| BadNotWritable | Variable is read-only. |
| BadConnectionClosed | Connection was lost during the write. |
-{% warning %}
+
Warning
+
Writes to production equipment change real-world state. Gate write nodes behind validation or an operator confirmation step before deploying to a live instance.
-{% endwarning %}
+
+
## 8. Extension Object
@@ -452,9 +473,11 @@ The Monitor node subscribes to variables and emits a message whenever a value ch
- **Notification behaviour**, a monitored item notifies only when the value actually changes (per the OPC UA spec), and once at monitoring start: the initial value is always reported.
- **Resilience**, the Monitor node reconnects automatically after a connection loss and re-establishes its subscription and monitored items, resuming without manual intervention.
-{% note %}
+
Note
+
Each notification carries `msg.sequenceNumber`. Watch it for gaps to detect dropped notifications, for example when a server-side queue overflows under a heavy change rate.
-{% endnote %}
+
+
+
Injecting an empty array releases the monitored items but leaves the now-empty subscription in place on the connection, so it is the right way to pause and resume a stream. To tear the session down completely, disconnect the connection instead.
-{% endnote %}
+
+
### Deadband filtering
@@ -528,9 +553,11 @@ Injecting an empty array releases the monitored items but leaves the now-empty s
| Absolute | Report only when `abs(new − lastReported) > value`. |
| Percent | Report only when the change exceeds a percentage of the variable's EURange. Requires the variable to expose an EURange property. |
-{% note %}
+
Note
+
Deadband compares against the last reported value, not the last sampled value. The initial value is always reported. Use Absolute when a variable has no EURange, Percent silently does nothing without one.
-{% endnote %}
+
+
+
If no events arrive, verify the server supports events (read the object's `EventNotifier` attribute, a non-zero value means it emits events), point the NodeId at an event-generating object (try `i=2253`), and clear the Where Clause. If too many arrive, tighten the Where Clause by type and severity, or point at specific equipment instead of the Server object.
-{% endnote %}
+
+
## 12. Browse
@@ -637,9 +666,11 @@ The output type sets what each leaf variable returns:
- **excludeEmpty**, set `msg.excludeEmpty = true` to drop branches that contain no variables, for cleaner output.
- **Depth**, Explore follows the full hierarchy under the start node (default maximum depth 10). Rather than a depth setting, control scope by choosing a more specific start NodeId: roughly 2–3 levels for one piece of equipment, 4–6 for a production line, 7–10 for a whole plant.
-{% warning %}
+
Warning
+
Starting Explore at the Objects folder (`ns=0;i=85`) or another root node traverses the entire server and can return a very large structure. Start from a specific NodeId (or `/Server/ServerStatus` if you are just getting your bearings) and expand scope deliberately.
-{% endwarning %}
+
+
Example, exploring `/Server/ServerStatus` with output type `NodeId` returns a nested object whose leaves are the NodeIds of each variable:
@@ -663,9 +694,11 @@ Explore a subtree once to discover all its variables, then feed that structure s
[Inject] → [Explore] → [Monitor] → [Debug]
```
-{% note %}
+
Note
+
Address spaces rarely change, so cache the Explore result (with a timestamp) and reuse it rather than re-exploring on every deploy. Re-explore periodically only if your server adds or removes variables at runtime.
-{% endnote %}
+
+
+
Keep time ranges and `numValuesPerNode` bounded, prefer aggregation to thin large ranges for charts, and verify the server supports history before relying on it. For very long ranges, chunk the request into smaller windows.
-{% endnote %}
+
+
## 15. File Operation
@@ -764,23 +799,25 @@ msg.payload = "appended log line\n"; // WriteAppend mode
| BadNodeIdUnknown | File not found, for WriteAppend, create it first with Write. |
| BadNotWritable / BadUserAccessDenied | File is read-only or your account lacks permission. |
-{% note %}
+
Note
+
Use the Browse node to discover available File objects, and the Read node to inspect file metadata such as `Size`, `OpenCount`, and `UserWritable`. Garbled text usually means the wrong encoding, try a different one.
-{% endnote %}
+
+
## 16. Hosting an OPC UA Server
On **self-hosted FlowFuse**, the certified node can run an OPC UA server inside your instance using only Function nodes, no `settings.js` edit, no external module declaration, no extra npm install.
-{% caution %}
Server hosting is **not supported on FlowFuse Cloud**, Cloud exposes HTTP/HTTPS only and cannot expose the arbitrary TCP port (`opc.tcp://`) a server needs. Use a self-hosted FlowFuse instance and ensure the chosen port is reachable through your container and network configuration.
-{% endcaution %}
When the palette loads, it publishes a bootstrap helper in the Node-RED global context. Retrieve it and destructure `{ bootstrap, opcua }`: `bootstrap` carries the server helpers and `opcua` re-exports the full `node-opcua` namespace. This works even with `functionExternalModules: false`.
-{% note %}
+
Note
+
`global.get("sterfive")` below is the literal runtime key the certified node exposes. Keep it exactly as written in your Function nodes, or the code will not find the helper.
-{% endnote %}
+
+
+
Keep the handle in flow context, never in a local `const`/`let`, a Function node's body re-runs on every message. Prefix context keys with `$` (e.g. `$opcuaHandle`) and avoid the bare key `opcua` (it collides with other vendors), dotted keys (Node-RED reads them as nested paths), and colon-separated keys. Put construction-time options (`port`, `nodesets`, security, `users`) only in the Boot node, never in the Update node.
-{% endnote %}
+
+
### Key `bootstrapServer` options
@@ -848,9 +887,11 @@ Keep the handle in flow context, never in a local `const`/`let`, a Function node
| `onPopulate` | Callback run once when a new server is built, add your variables, objects, and methods here. |
| `forceRebuild` | Set `true` to rebuild without a config change (e.g. after editing the trust store). |
-{% note %}
+
Note
+
The server's identity is a config hash of `port`, `endpoint`, `applicationName`, `productUri`, `nodesets`, `securityPolicies`, `securityModes`, `allowAnonymous`, and `users`. Change any of these and the next deploy rebuilds the server (re-running `onPopulate`). `onPopulate` and `forceRebuild` are excluded from the hash, so editing `onPopulate` alone does not trigger a rebuild, use `forceRebuild: true` if you need one.
-{% endnote %}
+
+
### Adding methods
@@ -940,9 +981,11 @@ Beyond roughly five servers per instance, prefer separate processes; the event l
Authentication is declarative through the `users` array. Each entry has a username, a password, and roles. The helper bcrypt-hashes clear-text passwords at boot and maps role names to their NodeIds; a value already in bcrypt form (`$2a$`/`$2b$`/`$2y$` prefix) is passed through verbatim. The `users` array controls only session activation, per-node authorization comes from each variable's access-level attributes combined with role mapping. Set `allowAnonymous: false` to refuse anonymous sessions. The helpers `bootstrap.ensureBcryptHash(plain)` and `bootstrap.isBcryptHash(hash)` are available for tooling.
-{% warning %}
+
Warning
+
If you omit `users`, the helper installs a default test set (`root/secret`, `gdsadmin/admingds`, `user1/password1`, `user2/password2`) intended only for development. Always set your own `users` for any instance reachable beyond your development machine.
-{% endwarning %}
+
+
### Security, certificates, and PKI storage
@@ -950,9 +993,11 @@ Enable secure endpoints with `securityPolicies` and `securityModes`; the server
On FlowFuse, the node stores its PKI (its own certificate, the trusted list, and the rejected list) under `/opcua-for-flow-fuse/PKI`. For this to survive restarts and redeploys, that directory must be on persistent storage: on container-based FlowFuse (Cloud, Kubernetes, Docker) this is the persistent volume mounted at `/data/storage`; on the FlowFuse Device Agent the working directory is on the device's local filesystem. If you re-trust a server's certificate after every deploy, your PKI directory is not landing on persistent storage.
-{% note %}
+
Note
+
Username/password over `MessageSecurityMode.None` travels in cleartext. Combine credentials with at least Sign mode. Disabling `SecurityPolicy.None` entirely can break naive clients that probe without security first.
-{% endnote %}
+
+
### Lifecycle, diagnostics, and stopping
@@ -1000,4 +1045,4 @@ The OPC UA port (default 4840) is configurable per server; update the rule if yo
| Cannot host a server on FlowFuse Cloud | Server hosting requires a self-hosted FlowFuse instance with the OPC UA TCP port exposed. Cloud exposes HTTP/HTTPS only. |
| Re-trusting the server certificate after every deploy | The PKI directory (`/opcua-for-flow-fuse/PKI`) is not on persistent storage. On container-based FlowFuse it must sit under the `/data/storage` persistent volume. Also confirm the certificate is in `trusted`, not `rejected`. |
-For help enabling the OPC UA Certified Node, licensing, or anything else, [contact FlowFuse](/contact-us/).
\ No newline at end of file
+For help enabling the OPC UA Certified Node, licensing, or anything else, [contact FlowFuse](/contact-us/).
diff --git a/src/node-red/flowfuse/edge/rtsp.md b/nuxt/content-guides/flowfuse-nodes/edge/rtsp.md
similarity index 83%
rename from src/node-red/flowfuse/edge/rtsp.md
rename to nuxt/content-guides/flowfuse-nodes/edge/rtsp.md
index 63be992d27..a767e7cc9b 100644
--- a/src/node-red/flowfuse/edge/rtsp.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/rtsp.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: RTSP Video Feed
- parent: Edge
- order: 1
+title: RTSP Video Feed
+navTitle: RTSP Video Feed
+navOrder: 1
meta:
- title: RTSP Video Feed
description: Documentation for the FlowFuse RTSP Video Feed node, which connects to an RTSP camera stream and extracts still frames as PNG images for use in flows, dashboards, and local AI models.
---
-# {{ meta.title }}
+# RTSP Video Feed
The **RTSP Video Feed** node connects to an [RTSP](https://en.wikipedia.org/wiki/Real-Time_Streaming_Protocol) video stream from an IP camera or NVR and extracts still frames as PNG images.
@@ -18,9 +16,11 @@ The node orchestrates `ffmpeg` to acquire and decode the video stream. By handli
Extracted frames can either be emitted as messages into the flow or written directly to disk as a numbered sequence of PNG files. See [Operating modes](#operating-modes) for details.
-{% note %}
+
Note
+
The RTSP Video Feed node is not available by default. It is part of the FlowFuse Edge Certified Nodes catalogue, which is part of the **FlowFuse Edge** offering. Please contact our sales team at [Contact us](/contact-us/) to learn more or to request access.
-{% endnote %}
+
+
## Use case
@@ -28,13 +28,13 @@ Most plants and facilities already have IP cameras on the network, but the foota
### Example: catching mislabeled products on a packaging line
-A camera above a packaging line already streams to the site NVR. Point the RTSP Video Feed node at that same stream, set it to 1 FPS, and wire it to a [FlowFuse AI node](/node-red/flowfuse/ai/) running an image classification model. Each frame is checked for a missing or misprinted label, and when one is detected, the flow raises a dashboard alert and publishes the offending frame over MQTT for the quality team to review. What used to require an operator glancing at a screen, or a dedicated vision system with its own controller and license, becomes a three-node flow on hardware you already own.
+A camera above a packaging line already streams to the site NVR. Point the RTSP Video Feed node at that same stream, set it to 1 FPS, and wire it to a [FlowFuse AI node](/docs/flowfuse-nodes/ai/) running an image classification model. Each frame is checked for a missing or misprinted label, and when one is detected, the flow raises a dashboard alert and publishes the offending frame over MQTT for the quality team to review. What used to require an operator glancing at a screen, or a dedicated vision system with its own controller and license, becomes a three-node flow on hardware you already own.
### Where this shows up in practice
-- **Machine vision**: feed frames to a [FlowFuse AI node](/node-red/flowfuse/ai/) for object detection or image classification, then act on the result, for example counting parts on a line, checking a fill level, or verifying a safety gate is closed.
+- **Machine vision**: feed frames to a [FlowFuse AI node](/docs/flowfuse-nodes/ai/) for object detection or image classification, then act on the result, for example counting parts on a line, checking a fill level, or verifying a safety gate is closed.
- **Remote monitoring**: display the frames live on a [FlowFuse Dashboard](/platform/dashboard/) so an operator can watch a machine or area from anywhere the dashboard is reachable, alongside the process data on the same page.
-- **Event snapshots**: route frames through a `function` or `switch` node and keep only the ones that matter, for example the frame captured the moment a PLC tag or sensor reports a fault, and forward it with an [`mqtt out`](/node-red/flowfuse/mqtt/mqtt-out/) node or attach it to an alert.
+- **Event snapshots**: route frames through a `function` or `switch` node and keep only the ones that matter, for example the frame captured the moment a PLC tag or sensor reports a fault, and forward it with an [`mqtt out`](/docs/flowfuse-nodes/mqtt/mqtt-out/) node or attach it to an alert.
- **Timelapse and archiving**: switch to disk-writing mode and the node writes a numbered sequence of PNG files for later review or timelapse assembly, without adding any messages to the flow.
### Combining video with other plant data
@@ -61,9 +61,11 @@ If a prebuilt binary is not available for your platform, the node falls back to

*Locating and installing the RTSP Video Feed node from the FlowFuse Edge Certified Nodes catalogue.*
-{% note %}
+
Note
+
Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
-{% endnote %}
+
+
## Configuration
@@ -98,11 +100,13 @@ The node emits each captured frame as a message at the configured FPS rate.
| `msg.payload` | Buffer | The captured frame as a PNG image buffer. |
| `msg.topic` | String | The topic configured on the node. |
-The output can be wired to any node that accepts an image buffer, including [FlowFuse Dashboard widgets](/platform/dashboard/), [MQTT out nodes](/node-red/flowfuse/mqtt/mqtt-out/), and [FlowFuse AI nodes](/node-red/flowfuse/ai/).
+The output can be wired to any node that accepts an image buffer, including [FlowFuse Dashboard widgets](/platform/dashboard/), [MQTT out nodes](/docs/flowfuse-nodes/mqtt/mqtt-out/), and [FlowFuse AI nodes](/docs/flowfuse-nodes/ai/).
+
+
Note
-{% note %}
Every captured frame becomes a message in the flow. A high FPS value increases the number and size of messages being processed. Set FPS no higher than your use case requires.
-{% endnote %}
+
+
### Output disabled
@@ -114,4 +118,4 @@ rtsp--.png
If **File path** is left empty, files are written to the OS temp directory (e.g. `/tmp`). On many Linux distributions this is a RAM-backed filesystem, so frames consume memory rather than disk space and are cleared on reboot.
-The node does not delete files written to disk. At a high FPS rate, files will accumulate and eventually fill the available storage. Monitor available disk space when using this mode.
\ No newline at end of file
+The node does not delete files written to disk. At a high FPS rate, files will accumulate and eventually fill the available storage. Monitor available disk space when using this mode.
diff --git a/src/node-red/flowfuse/flowfuse-tables/index.md b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md
similarity index 62%
rename from src/node-red/flowfuse/flowfuse-tables/index.md
rename to nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md
index b60b760931..077010b3bc 100644
--- a/src/node-red/flowfuse/flowfuse-tables/index.md
+++ b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: FlowFuse Tables
- parent: FlowFuse Nodes
- order: 3
+title: FlowFuse Tables
+navTitle: FlowFuse Tables
+navOrder: 3
meta:
- title: FlowFuse Tables
description: FlowFuse Tables provides managed databases for Node-RED users, offering built-in nodes to query, insert, and manage data easily within FlowFuse flows.
---
-# {{ meta.title }}
+# FlowFuse Tables
**FlowFuse Tables** provides managed databases that can be directly accessed from Node-RED flows on the FlowFuse platform.
It simplifies data management and integration by offering purpose-built nodes that allow you to query, insert, and modify data without complex setup.
@@ -17,4 +15,4 @@ It simplifies data management and integration by offering purpose-built nodes th
The following documents describe the nodes available under FlowFuse Tables:
-{% include "navigation-items-list.njk" %}
+- [Query](/docs/flowfuse-nodes/flowfuse-tables/query/): The Query node allows you to run SQL queries against FlowFuse Tables, supporting parameterized queries, Mustache templates, and AI-assisted query generation for seamless database interactions.
diff --git a/src/node-red/flowfuse/flowfuse-tables/query.md b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md
similarity index 97%
rename from src/node-red/flowfuse/flowfuse-tables/query.md
rename to nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md
index 562262de3e..342b75a70b 100644
--- a/src/node-red/flowfuse/flowfuse-tables/query.md
+++ b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: Query
- parent: FlowFuse Tables
+title: Query
+navTitle: Query
+navOrder: 50
meta:
- title: Query
description: The Query node allows you to run SQL queries against FlowFuse Tables, supporting parameterized queries, Mustache templates, and AI-assisted query generation for seamless database interactions.
---
-# {{ meta.title }}
+# Query
The Query node allows you to write and run queries against database tables managed by [FlowFuse Tables](/docs/user/ff-tables/). The node is pre-configured to connect automatically when used within a FlowFuse Node-RED instance.
@@ -33,18 +32,14 @@ SQL queries can be configured directly in the node or passed dynamically via `ms
Pass parameters as an array via `msg.params`:
##### Input Data
-{% raw %}
```javascript
msg.params = [ msg.id ];
```
-{% endraw %}
##### Query defined in the node
-{% raw %}
```sql
SELECT * FROM table WHERE id = $1
```
-{% endraw %}
> Tip: For production environments, it is recommended to use parameterized queries instead. Parameterized queries automatically handle quoting and escaping, making them safer and more reliable.
@@ -53,30 +48,24 @@ SELECT * FROM table WHERE id = $1
Pass parameters as an object via `msg.queryParameters`:
##### Input Data
-{% raw %}
```javascript
msg.queryParameters.id = msg.id;
```
-{% endraw %}
##### Query defined in the node
-{% raw %}
```sql
SELECT * FROM table WHERE id = $id;
```
-{% endraw %}
### Mustache Templates
Reference message properties using Mustache syntax:
##### Query defined in the node
-{% raw %}
```sql
SELECT * FROM table WHERE id = {{{ msg.id }}}
SELECT * FROM table WHERE name = '{{{ msg.name }}}'
```
-{% endraw %}
> Note: Care must be taken to ensure incoming string data is properly escaped (e.g., single quotes must be doubled: `'` to `''`) to prevent syntax errors and SQL injection.
@@ -111,11 +100,11 @@ FlowFuse Tables requires **Enterprise tier** and must be enabled for your team.
## Example Flow
-{% renderFlow 500 %}
-{% raw %}
+::render-flow{:height="500"}
+```json
[{"id":"9cd7498d4f832f1e","type":"group","z":"9db2d9ed7f00b8af","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["f27702ab8c8a58d2","da6e293fa363b172","f74ede3fbcf4ef32","8bfb39f252c16a79","41e1f9e0e97768a8"],"x":34,"y":39,"w":892,"h":142},{"id":"f27702ab8c8a58d2","type":"tables-query","z":"9db2d9ed7f00b8af","g":"9cd7498d4f832f1e","name":"","query":"","split":false,"rowsPerMsg":1,"x":670,"y":140,"wires":[["8bfb39f252c16a79"]]},{"id":"da6e293fa363b172","type":"inject","z":"9db2d9ed7f00b8af","g":"9cd7498d4f832f1e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":140,"wires":[["f74ede3fbcf4ef32"]]},{"id":"f74ede3fbcf4ef32","type":"template","z":"9db2d9ed7f00b8af","g":"9cd7498d4f832f1e","name":"Set Query","field":"query","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"CREATE TABLE \"sensor_data\" (\n \"id\" SERIAL PRIMARY KEY,\n \"sensor_id\" TEXT NOT NULL,\n \"timestamp\" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n \"temperature\" REAL,\n \"unit\" TEXT DEFAULT 'celsius'\n);\n","output":"str","x":300,"y":140,"wires":[["f27702ab8c8a58d2"]]},{"id":"8bfb39f252c16a79","type":"debug","z":"9db2d9ed7f00b8af","g":"9cd7498d4f832f1e","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":830,"y":140,"wires":[]},{"id":"41e1f9e0e97768a8","type":"comment","z":"9db2d9ed7f00b8af","g":"9cd7498d4f832f1e","name":"Pass the query via msg.query","info":"","x":180,"y":80,"wires":[]},{"id":"91fa219afc78e395","type":"group","z":"9db2d9ed7f00b8af","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["aefa193bfd04e5c2","e5bd3fbf9af5e519","6cc5a12665df32cb","1ebe4f1381fe71dd","7a397cd61dd6a210"],"x":34,"y":519,"w":892,"h":142},{"id":"aefa193bfd04e5c2","type":"tables-query","z":"9db2d9ed7f00b8af","g":"91fa219afc78e395","name":"","query":"SELECT * FROM public.sensor_readings WHERE \"temperature\" > {{{msg.temperatureThreshold}}};","split":false,"rowsPerMsg":1,"x":670,"y":620,"wires":[["6cc5a12665df32cb"]]},{"id":"e5bd3fbf9af5e519","type":"inject","z":"9db2d9ed7f00b8af","g":"91fa219afc78e395","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":620,"wires":[["7a397cd61dd6a210"]]},{"id":"6cc5a12665df32cb","type":"debug","z":"9db2d9ed7f00b8af","g":"91fa219afc78e395","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":830,"y":620,"wires":[]},{"id":"1ebe4f1381fe71dd","type":"comment","z":"9db2d9ed7f00b8af","g":"91fa219afc78e395","name":"Using Mustache template","info":"","x":170,"y":560,"wires":[]},{"id":"7a397cd61dd6a210","type":"change","z":"9db2d9ed7f00b8af","g":"91fa219afc78e395","name":"Set temperatureThreshold","rules":[{"t":"set","p":"temperatureThreshold","pt":"msg","to":"20","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":620,"wires":[["aefa193bfd04e5c2"]]},{"id":"e00a02a11a322683","type":"group","z":"9db2d9ed7f00b8af","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["14d47d8931a2855b","58a32e0340ed268d","762caff19e1722b7","997d92441f4d2eeb","ae3e4a6940a8e236"],"x":34,"y":359,"w":892,"h":142},{"id":"14d47d8931a2855b","type":"tables-query","z":"9db2d9ed7f00b8af","g":"e00a02a11a322683","name":"","query":"DELETE FROM \"sensor_data\"\nWHERE \"id\" = $id;\n","split":false,"rowsPerMsg":1,"x":670,"y":460,"wires":[["762caff19e1722b7"]]},{"id":"58a32e0340ed268d","type":"inject","z":"9db2d9ed7f00b8af","g":"e00a02a11a322683","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":460,"wires":[["ae3e4a6940a8e236"]]},{"id":"762caff19e1722b7","type":"debug","z":"9db2d9ed7f00b8af","g":"e00a02a11a322683","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":830,"y":460,"wires":[]},{"id":"997d92441f4d2eeb","type":"comment","z":"9db2d9ed7f00b8af","g":"e00a02a11a322683","name":"Named parameterized query","info":"","x":180,"y":400,"wires":[]},{"id":"ae3e4a6940a8e236","type":"change","z":"9db2d9ed7f00b8af","g":"e00a02a11a322683","name":"Set queryParameters","rules":[{"t":"set","p":"queryParameters","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"queryParameters.id","pt":"msg","to":"3","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":460,"wires":[["14d47d8931a2855b"]]},{"id":"fd2f3651994d44d3","type":"group","z":"9db2d9ed7f00b8af","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["423532459421ddb0","f14ffc1ff606f377","28579ce56dbb5dab","35f9bc3ff82a3789","68d530295082581a","2cb15cbf3a1a3878"],"x":34,"y":199,"w":892,"h":142},{"id":"423532459421ddb0","type":"tables-query","z":"9db2d9ed7f00b8af","g":"fd2f3651994d44d3","name":"","query":"INSERT INTO \"sensor_data\" (\"sensor_id\", \"temperature\", \"unit\")\nVALUES ($1, $2, $3);\n","split":false,"rowsPerMsg":1,"x":670,"y":300,"wires":[["28579ce56dbb5dab"]]},{"id":"f14ffc1ff606f377","type":"inject","z":"9db2d9ed7f00b8af","g":"fd2f3651994d44d3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":300,"wires":[["68d530295082581a"]]},{"id":"28579ce56dbb5dab","type":"debug","z":"9db2d9ed7f00b8af","g":"fd2f3651994d44d3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":830,"y":300,"wires":[]},{"id":"35f9bc3ff82a3789","type":"comment","z":"9db2d9ed7f00b8af","g":"fd2f3651994d44d3","name":"Numeric parameterized query","info":"","x":180,"y":240,"wires":[]},{"id":"68d530295082581a","type":"function","z":"9db2d9ed7f00b8af","g":"fd2f3651994d44d3","name":"Simulate Sensor","func":"// Simulate sensor data and output as an object in msg.payload\n\nlet temperature = (20 + Math.random() * 10).toFixed(2);\nlet sensorIds = [\"sensor_01\", \"sensor_02\", \"sensor_03\"];\nlet sensor_id = sensorIds[Math.floor(Math.random() * sensorIds.length)];\n\nmsg.payload = {\n sensor_id: sensor_id,\n temperature: Number(temperature),\n unit: \"celsius\",\n};\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":300,"wires":[["2cb15cbf3a1a3878"]]},{"id":"2cb15cbf3a1a3878","type":"change","z":"9db2d9ed7f00b8af","g":"fd2f3651994d44d3","name":"Set Params","rules":[{"t":"set","p":"params","pt":"msg","to":"[msg.payload.sensor_id, msg.payload.temperature, msg.payload.unit]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":300,"wires":[["423532459421ddb0"]]},{"id":"bf517ea3d503dd5a","type":"global-config","env":[],"modules":{"@flowfuse/nr-tables-nodes":"0.1.0"}}]
-{% endraw %}
-{% endrenderFlow %}
+```
+::
## Generate Queries with FlowFuse Expert
diff --git a/src/node-red/flowfuse/hub/index.md b/nuxt/content-guides/flowfuse-nodes/hub/README.md
similarity index 56%
rename from src/node-red/flowfuse/hub/index.md
rename to nuxt/content-guides/flowfuse-nodes/hub/README.md
index c2d6d5980d..f59e8d958b 100644
--- a/src/node-red/flowfuse/hub/index.md
+++ b/nuxt/content-guides/flowfuse-nodes/hub/README.md
@@ -1,34 +1,37 @@
---
-eleventyNavigation:
- key: Hub
- parent: FlowFuse Nodes
- order: 5
- title: Hub
+title: Hub Certified Nodes
+navTitle: Hub
+navOrder: 5
meta:
- title: Hub Certified Nodes
- description: Documentation for FlowFuse Hub Certified Nodes, including nodes for connecting FlowFuse to IT systems, databases, cloud services, APIs, and enterprise applications.
+ description: Documentation for FlowFuse Hub Certified Nodes, including nodes for connecting FlowFuse to IT systems, databases, cloud services, APIs, and enterprise applications.
---
-# {{ meta.title }}
+# Hub Certified Nodes
This section contains documentation for **FlowFuse Hub Certified Nodes** that connect your FlowFuse instances to IT systems, databases, cloud services, APIs, and enterprise applications.
FlowFuse Certified Nodes are packages that FlowFuse has vetted for quality, security, and support, and maintains on an ongoing basis. To learn more about what certification means and how these nodes are delivered, [read the FlowFuse Certified Nodes blog post](/blog/2025/07/certified-nodes-v2/).
-{% note %}
+
Note
+
The FlowFuse Hub Certified Nodes catalogue is part of the **FlowFuse Hub** offering. [Contact us](/contact-us/) to get access or to learn more.
-{% endnote %}
-{% note %}
+
+
+
Note
+
This section is expanding. We are actively working to bring more Hub Certified Nodes to FlowFuse, and additional documentation will be added here over time.
-{% endnote %}
+
+
## Nodes
This section lists the **Hub Certified Nodes** documented in FlowFuse:
-{% include "navigation-items-list.njk" %}
+- [Redis](/docs/flowfuse-nodes/hub/redis/): A FlowFuse-certified package that lets you connect to Redis, store and retrieve data, publish and subscribe to messages, execute commands, and integrate Redis into your flows.
+
+
Note
-{% note %}
Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
-{% endnote %}
\ No newline at end of file
+
+
diff --git a/src/node-red/flowfuse/hub/redis.md b/nuxt/content-guides/flowfuse-nodes/hub/redis.md
similarity index 98%
rename from src/node-red/flowfuse/hub/redis.md
rename to nuxt/content-guides/flowfuse-nodes/hub/redis.md
index edf32babd9..983c9358df 100644
--- a/src/node-red/flowfuse/hub/redis.md
+++ b/nuxt/content-guides/flowfuse-nodes/hub/redis.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: Redis
- parent: Hub
- order: 1
+title: Redis
+navTitle: Redis
+navOrder: 1
meta:
- title: Redis
- description: A FlowFuse-certified package that lets you connect to Redis, store and retrieve data, publish and subscribe to messages, execute commands, and integrate Redis into your flows.
+ description: A FlowFuse-certified package that lets you connect to Redis, store and retrieve data, publish and subscribe to messages, execute commands, and integrate Redis into your flows.
---
-# {{ meta.title }}
+# Redis
A FlowFuse-certified package that lets you connect to Redis, store and retrieve data, publish and subscribe to messages, execute commands, and integrate Redis into your flows.
@@ -24,9 +22,11 @@ Built on [ioredis](https://github.com/luin/ioredis), so anything ioredis support
- Connections are pooled per config node, one connection is reused across every node pointed at the same server config, unless a node explicitly requests a dedicated (blocking) connection
- TLS and Redis Cluster support
-{% note %}
+
Note
+
The Redis node is not available by default. It is part of the FlowFuse Hub Certified Nodes catalogue, which is part of the **FlowFuse Hub** offering. Please contact our sales team at [Contact us](/contact-us/) to learn more or to request access.
-{% endnote %}
+
+
## Use case
@@ -47,9 +47,11 @@ Connections are pooled per config node, so pointing many nodes at the same serve
3. Find the **FlowFuse Hub Certified Nodes** collection.
4. Locate `@flowfuse-certified-nodes/redis` and click **Install**.
-{% note %}
+
Note
+
Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
-{% endnote %}
+
+
## Nodes in this package
@@ -230,6 +232,8 @@ Custom/module commands (e.g. RedisJSON, RediSearch) can also be issued via `redi
## Sample flow
-{% renderFlow 300 %}
+::render-flow{:height="300"}
+```json
[{"id":"2c33ebeb73062ab3","type":"group","z":"FFF0000000000001","name":"Your First Redis Flow","style":{"label":true},"nodes":["e5f3326ef736cef6","8486e88d8623b42a","d1ebde22d0e224a8","22db25c60af16ab2","fe2be532c53402b3","ebc1e8fc0f1ba2d6","adb95c63d206c633"],"x":414,"y":1219,"w":712,"h":162},{"id":"e5f3326ef736cef6","type":"redis-command","z":"FFF0000000000001","g":"2c33ebeb73062ab3","server":"e370dc92b39a7ba4","command":"SET","name":"","topic":"mykey","params":"[\"Hello from Node-RED\"]","paramsType":"json","payloadType":"json","block":false,"x":710,"y":1260,"wires":[["8486e88d8623b42a"]]},{"id":"8486e88d8623b42a","type":"debug","z":"FFF0000000000001","g":"2c33ebeb73062ab3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":870,"y":1260,"wires":[]},{"id":"d1ebde22d0e224a8","type":"redis-command","z":"FFF0000000000001","g":"2c33ebeb73062ab3","server":"e370dc92b39a7ba4","command":"GET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":900,"y":1340,"wires":[["22db25c60af16ab2"]]},{"id":"22db25c60af16ab2","type":"debug","z":"FFF0000000000001","g":"2c33ebeb73062ab3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1030,"y":1340,"wires":[]},{"id":"fe2be532c53402b3","type":"change","z":"FFF0000000000001","g":"2c33ebeb73062ab3","name":"Set Key for GET","rules":[{"t":"set","p":"topic","pt":"msg","to":"mykey","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"[]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":700,"y":1340,"wires":[["d1ebde22d0e224a8"]]},{"id":"ebc1e8fc0f1ba2d6","type":"inject","z":"FFF0000000000001","g":"2c33ebeb73062ab3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1260,"wires":[["e5f3326ef736cef6"]]},{"id":"adb95c63d206c633","type":"inject","z":"FFF0000000000001","g":"2c33ebeb73062ab3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1340,"wires":[["fe2be532c53402b3"]]},{"id":"e370dc92b39a7ba4","type":"redis-config","name":"Local","options":"{\"host\":\"localhost\",\"port\":6379,\"db\":0}","cluster":false,"optionsType":"json"},{"id":"5e2842a4730589c0","type":"group","z":"FFF0000000000001","name":"Working with JSON Data","style":{"label":true},"nodes":["6acc07a393e5ee26","3255e8a9122253b7","a3b0b4a74133778e","4faba929a84fb358","e5c359b934174c8e","abf011c2e81960c4","22a2b54caae5869d","b9598f313e2a9fd2","d0051f637fd1621a","9e59f275496d722d"],"x":414,"y":1399,"w":852,"h":162},{"id":"6acc07a393e5ee26","type":"redis-command","z":"FFF0000000000001","g":"5e2842a4730589c0","server":"e370dc92b39a7ba4","command":"SET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":1040,"y":1440,"wires":[["3255e8a9122253b7"]]},{"id":"3255e8a9122253b7","type":"debug","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1170,"y":1440,"wires":[]},{"id":"a3b0b4a74133778e","type":"inject","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1440,"wires":[["d0051f637fd1621a"]]},{"id":"4faba929a84fb358","type":"redis-command","z":"FFF0000000000001","g":"5e2842a4730589c0","server":"e370dc92b39a7ba4","command":"GET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":900,"y":1520,"wires":[["b9598f313e2a9fd2"]]},{"id":"e5c359b934174c8e","type":"debug","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1170,"y":1520,"wires":[]},{"id":"abf011c2e81960c4","type":"change","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"Set Key for GET","rules":[{"t":"set","p":"topic","pt":"msg","to":"sensor:data","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"[]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":700,"y":1520,"wires":[["4faba929a84fb358"]]},{"id":"22a2b54caae5869d","type":"inject","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1520,"wires":[["abf011c2e81960c4"]]},{"id":"b9598f313e2a9fd2","type":"json","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"","property":"payload","action":"","pretty":false,"x":1030,"y":1520,"wires":[["e5c359b934174c8e"]]},{"id":"d0051f637fd1621a","type":"change","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"Set Key","rules":[{"t":"set","p":"topic","pt":"msg","to":"sensor:data","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{\t \"temperature\": 22.5,\t \"humidity\": 65,\t \"timestamp\": $now()\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":1440,"wires":[["9e59f275496d722d"]]},{"id":"9e59f275496d722d","type":"json","z":"FFF0000000000001","g":"5e2842a4730589c0","name":"","property":"payload","action":"","pretty":false,"x":890,"y":1440,"wires":[["6acc07a393e5ee26"]]},{"id":"39de5da95585227f","type":"group","z":"FFF0000000000001","name":"Pub/Sub Messaging","style":{"label":true},"nodes":["7756558fd542d657","bf2dc26107eff967","9856ddb8e7bc28e8","bcd9b22fd67ecf7c","22f0526fab3b589b"],"x":414,"y":1579,"w":632,"h":162},{"id":"7756558fd542d657","type":"inject","z":"FFF0000000000001","g":"39de5da95585227f","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1620,"wires":[["9856ddb8e7bc28e8"]]},{"id":"bf2dc26107eff967","type":"redis-out","z":"FFF0000000000001","g":"39de5da95585227f","server":"e370dc92b39a7ba4","command":"publish","name":"","topic":"alerts:temperature","obj":true,"x":930,"y":1620,"wires":[]},{"id":"9856ddb8e7bc28e8","type":"change","z":"FFF0000000000001","g":"39de5da95585227f","name":"Set Alert Message","rules":[{"t":"set","p":"payload","pt":"msg","to":"ALERT: Temperature critical in Zone A: 85°C - Equipment shutdown initiated","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":1620,"wires":[["bf2dc26107eff967"]]},{"id":"bcd9b22fd67ecf7c","type":"redis-in","z":"FFF0000000000001","g":"39de5da95585227f","server":"e370dc92b39a7ba4","command":"subscribe","name":"","topic":"alerts:temperature","obj":true,"timeout":0,"x":530,"y":1700,"wires":[["22f0526fab3b589b"]]},{"id":"22f0526fab3b589b","type":"debug","z":"FFF0000000000001","g":"39de5da95585227f","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":890,"y":1700,"wires":[]},{"id":"adc4b0df6b0e84e3","type":"group","z":"FFF0000000000001","name":"Using Lua Scripts for Atomic Operations","style":{"label":true},"nodes":["ca8a523710138a11","8804755befa1fbc8","0898e80e28b7d276","0b16690b3c2c10df","9a03ebdea51eefdc","c641d8e54620a73a","7c11a5f1c9716527","a07a025db962be50"],"x":414,"y":1759,"w":1112,"h":142},{"id":"ca8a523710138a11","type":"inject","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1860,"wires":[["8804755befa1fbc8"]]},{"id":"8804755befa1fbc8","type":"function","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","name":"Prepare Lua Script Arguments","func":"// msg.payload format: [keys..., args...]\n// First element(s) are the keys, remaining elements are arguments\nmsg.payload = [\n \"inventory:product:SKU-12345\", // KEYS[1]\n 3 // ARGV[1] - quantity requested\n];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":1860,"wires":[["0898e80e28b7d276"]]},{"id":"0898e80e28b7d276","type":"redis-lua-script","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","server":"e370dc92b39a7ba4","name":"","keyval":"1","func":"local key = KEYS[1]\nlocal requested = tonumber(ARGV[1])\n\nlocal current = tonumber(redis.call('GET', key) or \"0\")\n\nif current >= requested then\n redis.call('DECRBY', key, requested)\n return {1, current - requested}\nelse\n return {0, current}\nend","stored":false,"block":false,"x":1040,"y":1860,"wires":[["0b16690b3c2c10df"]]},{"id":"0b16690b3c2c10df","type":"function","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","name":"Format Lua Script Response","func":"const result = msg.payload;\nconst success = result[0];\nconst remaining = result[1];\n\nif (success === 1) {\n msg.payload = {\n status: \"success\",\n message: `Order processed. Remaining stock: ${remaining}`,\n remaining: remaining\n };\n} else {\n msg.payload = {\n status: \"failed\",\n message: `Insufficient stock. Available: ${remaining}`,\n available: remaining\n };\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1240,"y":1860,"wires":[["9a03ebdea51eefdc"]]},{"id":"9a03ebdea51eefdc","type":"debug","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1430,"y":1860,"wires":[]},{"id":"c641d8e54620a73a","type":"redis-command","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","server":"e370dc92b39a7ba4","command":"SET","name":"","topic":"inventory:product:SKU-12345","params":"[10]","paramsType":"json","payloadType":"json","block":false,"x":780,"y":1800,"wires":[["7c11a5f1c9716527"]]},{"id":"7c11a5f1c9716527","type":"debug","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1030,"y":1800,"wires":[]},{"id":"a07a025db962be50","type":"inject","z":"FFF0000000000001","g":"adc4b0df6b0e84e3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":1800,"wires":[["c641d8e54620a73a"]]},{"id":"01a96fb2044d723f","type":"group","z":"FFF0000000000001","name":"Direct Redis Client Access with redis-instance","style":{"label":true},"nodes":["745f41f352fa2212","a9ec9a8a5a66daa3","e3a5634ef0ebcb78","5192081309e08db9","4f169053bbd7add9","15f0b0462f795828","4a394700c6914961"],"x":414,"y":1919,"w":732,"h":222},{"id":"745f41f352fa2212","type":"redis-instance","z":"FFF0000000000001","g":"01a96fb2044d723f","server":"e370dc92b39a7ba4","name":"","topic":"redis","location":"flow","x":490,"y":1960,"wires":[]},{"id":"a9ec9a8a5a66daa3","type":"inject","z":"FFF0000000000001","g":"01a96fb2044d723f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":520,"y":2040,"wires":[["e3a5634ef0ebcb78"]]},{"id":"e3a5634ef0ebcb78","type":"function","z":"FFF0000000000001","g":"01a96fb2044d723f","name":"Batch Store Sensor Readings (Pipeline)","func":"const redis = flow.get('redis');\n\n// Create a pipeline\nconst pipeline = redis.pipeline();\n\n// Add multiple sensor readings in one batch\nconst sensors = [\n { id: 'temp-01', value: 23.5, unit: 'C' },\n { id: 'temp-02', value: 24.1, unit: 'C' },\n { id: 'humidity-01', value: 65, unit: '%' },\n { id: 'pressure-01', value: 1013, unit: 'hPa' }\n];\n\nsensors.forEach(sensor => {\n const key = `sensor:${sensor.id}:latest`;\n const data = JSON.stringify({\n value: sensor.value,\n unit: sensor.unit,\n timestamp: Date.now()\n });\n pipeline.set(key, data, 'EX', 3600); // Expire in 1 hour\n});\n\n// Execute all commands at once\npipeline.exec((err, results) => {\n if (err) {\n node.error(err, msg);\n return;\n }\n\n msg.payload = {\n message: `Stored ${results.length} sensor readings`,\n results: results\n };\n node.send(msg);\n});","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":2040,"wires":[["5192081309e08db9"]]},{"id":"5192081309e08db9","type":"debug","z":"FFF0000000000001","g":"01a96fb2044d723f","name":"debug 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1040,"y":2040,"wires":[]},{"id":"4f169053bbd7add9","type":"inject","z":"FFF0000000000001","g":"01a96fb2044d723f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":520,"y":2100,"wires":[["15f0b0462f795828"]]},{"id":"15f0b0462f795828","type":"function","z":"FFF0000000000001","g":"01a96fb2044d723f","name":"Scan and List Latest Sensor Keys","func":"const redis = flow.get('redis');\n\nasync function scanKeys() {\n const matchPattern = 'sensor:*:latest';\n const allKeys = [];\n let cursor = '0';\n\n try {\n do {\n // Scan with pattern matching\n const result = await redis.scan(\n cursor,\n 'MATCH', matchPattern,\n 'COUNT', 100\n );\n\n cursor = result[0];\n const keys = result[1];\n allKeys.push(...keys);\n\n } while (cursor !== '0');\n\n msg.payload = {\n pattern: matchPattern,\n count: allKeys.length,\n keys: allKeys\n };\n\n node.send(msg);\n\n } catch (err) {\n node.error(err, msg);\n }\n}\n\nscanKeys();","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":2100,"wires":[["4a394700c6914961"]]},{"id":"4a394700c6914961","type":"debug","z":"FFF0000000000001","g":"01a96fb2044d723f","name":"debug 9","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1040,"y":2100,"wires":[]},{"id":"1a2b3c4d5e6f7081","type":"group","z":"FFF0000000000001","name":"Lists and Queues","style":{"label":true},"nodes":["1a2b3c4d5e6f7001","1a2b3c4d5e6f7002","1a2b3c4d5e6f7003","1a2b3c4d5e6f7005","1a2b3c4d5e6f7006","1a2b3c4d5e6f7007"],"x":414,"y":2159,"w":612,"h":162},{"id":"1a2b3c4d5e6f7001","type":"inject","z":"FFF0000000000001","g":"1a2b3c4d5e6f7081","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":2200,"wires":[["1a2b3c4d5e6f7002"]]},{"id":"1a2b3c4d5e6f7002","type":"change","z":"FFF0000000000001","g":"1a2b3c4d5e6f7081","name":"Prepare Order Payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"Order #1042 - 2x Widget, 1x Gadget","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":700,"y":2200,"wires":[["1a2b3c4d5e6f7003"]]},{"id":"1a2b3c4d5e6f7003","type":"redis-out","z":"FFF0000000000001","g":"1a2b3c4d5e6f7081","server":"e370dc92b39a7ba4","command":"rpush","name":"","topic":"queue:orders","x":930,"y":2200,"wires":[]},{"id":"1a2b3c4d5e6f7005","type":"inject","z":"FFF0000000000001","g":"1a2b3c4d5e6f7081","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":510,"y":2280,"wires":[["1a2b3c4d5e6f7006"]]},{"id":"1a2b3c4d5e6f7006","type":"redis-in","z":"FFF0000000000001","g":"1a2b3c4d5e6f7081","server":"e370dc92b39a7ba4","command":"blpop","name":"","topic":"queue:orders","timeout":0,"x":750,"y":2280,"wires":[["1a2b3c4d5e6f7007"]]},{"id":"1a2b3c4d5e6f7007","type":"debug","z":"FFF0000000000001","g":"1a2b3c4d5e6f7081","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":930,"y":2280,"wires":[]},{"id":"2a2b3c4d5e6f7082","type":"group","z":"FFF0000000000001","name":"Lua Scripting - JSON and Sorted Sets","style":{"label":true},"nodes":["2a2b3c4d5e6f7001","2a2b3c4d5e6f7002","2a2b3c4d5e6f7003","2a2b3c4d5e6f7004","2a2b3c4d5e6f7005","2a2b3c4d5e6f7006","2a2b3c4d5e6f7007","2a2b3c4d5e6f7008","2a2b3c4d5e6f7009","2a2b3c4d5e6f700a","2a2b3c4d5e6f700b","2a2b3c4d5e6f700c","2a2b3c4d5e6f700d","2a2b3c4d5e6f700e","2a2b3c4d5e6f700f","2a2b3c4d5e6f7010","2a2b3c4d5e6f7011","2a2b3c4d5e6f7012","2a2b3c4d5e6f7013","2a2b3c4d5e6f7014"],"x":404,"y":2339,"w":952,"h":322},{"id":"2a2b3c4d5e6f7001","type":"inject","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Add Reading (ZADD)","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":2380,"wires":[["2a2b3c4d5e6f7002"]]},{"id":"2a2b3c4d5e6f7002","type":"function","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Prepare ZADD Args","func":"// msg.payload format: [key, score, JSON-encoded member]\nconst reading = { test: 1, hello: \"world1\", hexstr: \"000102\" };\nmsg.payload = [\n \"device:96a4:1a87:04\", // KEYS[1]\n Date.now(), // ARGV[1] - score\n JSON.stringify(reading) // ARGV[2] - member\n];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":2380,"wires":[["2a2b3c4d5e6f7003"]]},{"id":"2a2b3c4d5e6f7003","type":"redis-lua-script","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","server":"e370dc92b39a7ba4","name":"ZADD with JSON Member","keyval":"1","func":"local foo = redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2])\nreturn foo","stored":false,"block":false,"x":1020,"y":2380,"wires":[["2a2b3c4d5e6f7004"]]},{"id":"2a2b3c4d5e6f7004","type":"debug","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1260,"y":2380,"wires":[]},{"id":"2a2b3c4d5e6f7005","type":"inject","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Decode Member","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":2440,"wires":[["2a2b3c4d5e6f7006"]]},{"id":"2a2b3c4d5e6f7006","type":"function","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Prepare Decode Args","func":"msg.payload = [\n \"device:96a4:1a87:04\",\n \"{\\\"temperature\\\":22.5,\\\"hello\\\":\\\"world\\\"}\"\n];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":2440,"wires":[["2a2b3c4d5e6f7007"]]},{"id":"2a2b3c4d5e6f7007","type":"redis-lua-script","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","server":"e370dc92b39a7ba4","name":"cjson.decode Extract Field","keyval":"1","func":"local foo = cjson.decode(ARGV[1])\nreturn foo.temperature","stored":false,"block":false,"x":1020,"y":2440,"wires":[["2a2b3c4d5e6f7008"]]},{"id":"2a2b3c4d5e6f7008","type":"debug","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1260,"y":2440,"wires":[]},{"id":"2a2b3c4d5e6f7009","type":"inject","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Build JSON (cjson.encode)","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":2500,"wires":[["2a2b3c4d5e6f700a"]]},{"id":"2a2b3c4d5e6f700a","type":"redis-lua-script","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","server":"e370dc92b39a7ba4","name":"cjson.encode Build JSON","keyval":"0","func":"local foo = {}\nfoo.field1 = 1\nfoo.field2 = \"hello world\"\nfoo.field3 = {}\nfoo.field3.name = \"paul\"\nreturn cjson.encode(foo)","stored":false,"block":false,"x":840,"y":2500,"wires":[["2a2b3c4d5e6f700b"]]},{"id":"2a2b3c4d5e6f700b","type":"json","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"","property":"payload","action":"","pretty":false,"x":1020,"y":2500,"wires":[["2a2b3c4d5e6f700c"]]},{"id":"2a2b3c4d5e6f700c","type":"debug","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1260,"y":2500,"wires":[]},{"id":"2a2b3c4d5e6f700d","type":"inject","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Extract Substring","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":2560,"wires":[["2a2b3c4d5e6f700e"]]},{"id":"2a2b3c4d5e6f700e","type":"function","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Prepare string.sub Args","func":"msg.payload = [\"hexstr:060708\"];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":2560,"wires":[["2a2b3c4d5e6f700f"]]},{"id":"2a2b3c4d5e6f700f","type":"redis-lua-script","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","server":"e370dc92b39a7ba4","name":"string.sub Extract Substring","keyval":"0","func":"local foo = string.sub(ARGV[1], -6, -1)\nreturn foo","stored":false,"block":false,"x":1020,"y":2560,"wires":[["2a2b3c4d5e6f7010"]]},{"id":"2a2b3c4d5e6f7010","type":"debug","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1260,"y":2560,"wires":[]},{"id":"2a2b3c4d5e6f7011","type":"inject","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Query Sorted Set (ZRANGE)","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":2620,"wires":[["2a2b3c4d5e6f7012"]]},{"id":"2a2b3c4d5e6f7012","type":"function","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Prepare ZRANGE Args","func":"msg.payload = [\"device:96a4:1a87:04\"];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":800,"y":2620,"wires":[["2a2b3c4d5e6f7013"]]},{"id":"2a2b3c4d5e6f7013","type":"redis-lua-script","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","server":"e370dc92b39a7ba4","name":"ZRANGE Query Members","keyval":"1","func":"return redis.call('ZRANGE', KEYS[1], 0, -1)","stored":false,"block":false,"x":1020,"y":2620,"wires":[["2a2b3c4d5e6f7014"]]},{"id":"2a2b3c4d5e6f7014","type":"debug","z":"FFF0000000000001","g":"2a2b3c4d5e6f7082","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1260,"y":2620,"wires":[]},{"id":"0b0e24520432625b","type":"global-config","env":[],"modules":{"@flowfuse-certified-nodes/redis":"1.0.0"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/flowfuse/images/ff-expert-at-platform-level.png b/nuxt/content-guides/flowfuse-nodes/images/ff-expert-at-platform-level.png
similarity index 100%
rename from src/node-red/flowfuse/images/ff-expert-at-platform-level.png
rename to nuxt/content-guides/flowfuse-nodes/images/ff-expert-at-platform-level.png
diff --git a/src/node-red/flowfuse/images/ff-expert-in-editor.png b/nuxt/content-guides/flowfuse-nodes/images/ff-expert-in-editor.png
similarity index 100%
rename from src/node-red/flowfuse/images/ff-expert-in-editor.png
rename to nuxt/content-guides/flowfuse-nodes/images/ff-expert-in-editor.png
diff --git a/src/node-red/flowfuse/images/ff-expert-opened.png b/nuxt/content-guides/flowfuse-nodes/images/ff-expert-opened.png
similarity index 100%
rename from src/node-red/flowfuse/images/ff-expert-opened.png
rename to nuxt/content-guides/flowfuse-nodes/images/ff-expert-opened.png
diff --git a/src/node-red/flowfuse/images/ff-expert-opned-platform.png b/nuxt/content-guides/flowfuse-nodes/images/ff-expert-opned-platform.png
similarity index 100%
rename from src/node-red/flowfuse/images/ff-expert-opned-platform.png
rename to nuxt/content-guides/flowfuse-nodes/images/ff-expert-opned-platform.png
diff --git a/src/node-red/flowfuse/images/mcp-in-flowfuse.png b/nuxt/content-guides/flowfuse-nodes/images/mcp-in-flowfuse.png
similarity index 100%
rename from src/node-red/flowfuse/images/mcp-in-flowfuse.png
rename to nuxt/content-guides/flowfuse-nodes/images/mcp-in-flowfuse.png
diff --git a/src/node-red/flowfuse/mcp/index.md b/nuxt/content-guides/flowfuse-nodes/mcp/README.md
similarity index 87%
rename from src/node-red/flowfuse/mcp/index.md
rename to nuxt/content-guides/flowfuse-nodes/mcp/README.md
index 9dc8fec3db..141b4a7e87 100644
--- a/src/node-red/flowfuse/mcp/index.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/README.md
@@ -1,14 +1,12 @@
---
-eleventyNavigation:
- key: MCP
- parent: FlowFuse Nodes
- order: 2
+title: MCP Nodes
+navTitle: MCP
+navOrder: 2
meta:
- title: MCP Nodes
description: A set of nodes that enable the creation of MCP (Model Context Protocol) servers in your Node-RED flows for AI-integration.
---
-# {{ meta.title }}
+# MCP Nodes
This document lists and explains the **MCP nodes** available in FlowFuse. MCP (Model Context Protocol) nodes extend Node-RED to integrate AI models, tools, and resources through the Model Context Protocol framework. Each node helps you connect, configure, and manage AI interactions directly from Node-RED.
@@ -119,6 +117,9 @@ FlowFuse Expert enforces access control based on the annotations configured in y
This section lists the **MCP nodes** available in FlowFuse:
-{% include "navigation-items-list.njk" %}
+- [MCP Prompt](/docs/flowfuse-nodes/mcp/mcp-prompt/): The MCP Prompt node allows you to create pre-configured prompt templates that users can easily invoke from their MCP client.
+- [MCP Resource](/docs/flowfuse-nodes/mcp/mcp-resource/): The MCP Resource node allows you to expose read-only data that AI assistants can access for context.
+- [MCP Response](/docs/flowfuse-nodes/mcp/mcp-response/): Sends responses back to the MCP client for tools and resources.
+- [MCP Tool](/docs/flowfuse-nodes/mcp/mcp-tool/): MCP Tool node allows you to create custom tools that FlowFuse Expert can invoke to perform specific tasks.
-Each listed node provides a unique capability within the MCP ecosystem, from registering tools and resources to managing prompts and responses. Use these nodes to create MCP Servers, provide tools, resources, and prompts to MCP clients, and standardize AI workflows in your Node-RED projects.
\ No newline at end of file
+Each listed node provides a unique capability within the MCP ecosystem, from registering tools and resources to managing prompts and responses. Use these nodes to create MCP Servers, provide tools, resources, and prompts to MCP clients, and standardize AI workflows in your Node-RED projects.
diff --git a/src/node-red/flowfuse/mcp/mcp-prompt.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md
similarity index 96%
rename from src/node-red/flowfuse/mcp/mcp-prompt.md
rename to nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md
index 49abe13a9b..ba3175ef3a 100644
--- a/src/node-red/flowfuse/mcp/mcp-prompt.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: MCP Prompt
- parent: MCP
+title: MCP Prompt
+navTitle: MCP Prompt
+navOrder: 50
meta:
- title: MCP Prompt
description: The MCP Prompt node allows you to create pre-configured prompt templates that users can easily invoke from their MCP client.
---
-# {{ meta.title }}
+# MCP Prompt
The MCP Prompt node allows you to create pre-configured prompt templates that users can easily invoke from their MCP client. These templates can include variable placeholders that users fill in, making it simple to create consistent, well-structured prompts for common tasks. Unlike tools and resources, prompts don't require flows or response nodes - they simply register the template with the MCP server.
@@ -54,9 +53,7 @@ Detailed description of what this prompt does and when to use it.
`string`, *Required*
-{% raw %}
Define your prompt text using **double curly braces `{{ }}`** for variables.
-{% endraw %}
### Arguments
@@ -67,30 +64,25 @@ JSON schema defining the template variables that users can customize. This follo
### Basic Example
-{% raw %}
```
Hello {{ name }}! Welcome to {{ location }}.
```
-{% endraw %}
**Variables:** `name`, `location`
### Complex Example
-{% raw %}
```
You are a holiday planning agent.
You should provide information about {{ location }}.
You should also provide rough budget ideas for visiting this place in {{ time_of_year }} for {{ duration }} days.
Please breakdown rough ideas for hotels and local tourist hot spots to visit.
```
-{% endraw %}
**Variables:** `location`, `time_of_year`, `duration`
### Multi-Section Example
-{% raw %}
```
# Code Review Request
@@ -108,7 +100,6 @@ Focus on:
Provide feedback on code quality, potential bugs, and suggestions for improvement.
```
-{% endraw %}
**Variables:** `filename`, `language`, `code`, `focus_area_1`, `focus_area_2`, `focus_area_3`
@@ -192,6 +183,8 @@ Provide feedback on code quality, potential bugs, and suggestions for improvemen
## Example Flow
-{% renderFlow 300 %}
+::render-flow{:height="300"}
+```json
[{"id":"1dd56abc53f50de0","type":"group","z":"FFF0000000000001","name":"MCP Prompts","style":{"label":true},"nodes":["cc2bedb852d8cea4","241e6041470d735c"],"x":714,"y":819,"w":212,"h":142},{"id":"cc2bedb852d8cea4","type":"mcp-prompt","z":"FFF0000000000001","g":"1dd56abc53f50de0","name":"","server":"28907ed9ddcdd4b9","promptId":"uppercase","title":"Uppercase","description":"This prompt will return the provided content in all uppercase characters","template":"Please return the following in uppercase: {{message}}","arguments":"{\n \"type\": \"object\",\n \"properties\": {\n \"message\": {\n \"type\": \"string\",\n \"description\": \"The text provided by the user\"\n }\n },\n \"required\": [\"message\"]\n}","x":800,"y":920,"wires":[]},{"id":"241e6041470d735c","type":"mcp-prompt","z":"FFF0000000000001","g":"1dd56abc53f50de0","name":"","server":"28907ed9ddcdd4b9","promptId":"holiday_planner","title":"Holiday Planning and Budgeting Agent","description":"This prompt can assist users with creating budgets for their desired holiday destinations. ","template":"You are a holiday planning agent.\nYou should provide information about {{ location }}.\nYou should also provide rough budget ideas for visiting this place in {{ time_of_year }} for {{ duration }} days.\nPlease breakdown rough ideas for hotels and local tourist hot spots to visit.","arguments":"{\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"A country, city or town somewhere in the world.\",\n \"minLength\": 1\n },\n \"time_of_year\": {\n \"type\": \"string\",\n \"description\": \"This can be a specific date, season or month. Used to provide context to the agent as to when in the year the user wants to take their holiday.\",\n \"minLength\": 1\n },\n \"duration\": {\n \"type\": \"number\",\n \"default\": 7,\n \"description\": \"The number of days\"\n }\n },\n \"required\": [\"location\", \"time_of_year\"]\n}","x":820,"y":860,"wires":[]},{"id":"28907ed9ddcdd4b9","type":"mcp-server","name":"My Node-RED MCP Server","protocol":"http","path":"/mcp"},{"id":"89a436fa564e3d58","type":"global-config","env":[],"modules":{"@flowfuse-nodes/nr-mcp-server-nodes":"0.1.2"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/flowfuse/mcp/mcp-resource.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md
similarity index 99%
rename from src/node-red/flowfuse/mcp/mcp-resource.md
rename to nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md
index 5006b1d5da..0d81c31244 100644
--- a/src/node-red/flowfuse/mcp/mcp-resource.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: MCP Resource
- parent: MCP
+title: MCP Resource
+navTitle: MCP Resource
+navOrder: 50
meta:
- title: MCP Resource
description: The MCP Resource node allows you to expose read-only data that AI assistants can access for context.
---
-# {{ meta.title }}
+# MCP Resource
The MCP Resource node allows you to expose read-only data that AI assistants can access for context. Resources are designed to provide information without performing actions or causing side effects. Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information
@@ -168,6 +167,8 @@ msg.payload = {
## Example Flow
-{% renderFlow %}
+::render-flow
+```json
[{"id":"00744c2e2a560c36","type":"group","z":"e1ceeedf31ce1ebd","name":"MCP Resources","style":{"label":true},"nodes":["555a96b221f8e2bf","bb258a21622c01b9","066f6c58bc45d617","70e0b93eb88d63d6","5651b0896965ae6a","6bcaef4ed1ab82ae","3d6e0d7e21b44845","350db73b93a26f8d","fac46d1d55d9ca22","84ebc9b12d948173","9d4edda03c19b553","d3aa7bfa90c9e24e","d6f015cb6debe35b"],"x":274,"y":1739,"w":1002,"h":282},{"id":"555a96b221f8e2bf","type":"mcp-resource","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"","server":"28907ed9ddcdd4b9","resourceUri":"local://books/{genre}","resourceId":"my_books","title":"Books Array","description":"JSON Array of books filtered by genre.","mimeType":"application/json","x":400,"y":1920,"wires":[["70e0b93eb88d63d6"]]},{"id":"bb258a21622c01b9","type":"mcp-response","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"","x":1130,"y":1900,"wires":[]},{"id":"066f6c58bc45d617","type":"json","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"To JSON","property":"payload","action":"str","pretty":false,"x":870,"y":1920,"wires":[["350db73b93a26f8d"]]},{"id":"70e0b93eb88d63d6","type":"template","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"library","field":"library","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"author\": \"Harper Lee\",\n \"title\": \"To Kill a Mockingbird\",\n \"genre\": \"Fiction\",\n \"year\": 1960\n },\n {\n \"author\": \"J.K. Rowling\",\n \"title\": \"Harry Potter and the Sorcerer's Stone\",\n \"genre\": \"Fantasy\",\n \"year\": 1997\n },\n {\n \"author\": \"George Orwell\",\n \"title\": \"1984\",\n \"genre\": \"Dystopian\",\n \"year\": 1949\n },\n {\n \"author\": \"Jane Austen\",\n \"title\": \"Pride and Prejudice\",\n \"genre\": \"Romance\",\n \"year\": 1813\n },\n {\n \"author\": \"F. Scott Fitzgerald\",\n \"title\": \"The Great Gatsby\",\n \"genre\": \"Classic\",\n \"year\": 1925\n },\n {\n \"author\": \"Toni Morrison\",\n \"title\": \"Beloved\",\n \"genre\": \"Historical Fiction\",\n \"year\": 1987\n },\n {\n \"author\": \"Stephen King\",\n \"title\": \"The Shining\",\n \"genre\": \"Horror\",\n \"year\": 1977\n },\n {\n \"author\": \"Agatha Christie\",\n \"title\": \"Murder on the Orient Express\",\n \"genre\": \"Mystery\",\n \"year\": 1934\n },\n {\n \"author\": \"Gabriel Garcia Marquez\",\n \"title\": \"One Hundred Years of Solitude\",\n \"genre\": \"Magical Realism\",\n \"year\": 1967\n },\n {\n \"author\": \"Mark Twain\",\n \"title\": \"Adventures of Huckleberry Finn\",\n \"genre\": \"Adventure\",\n \"year\": 1884\n },\n {\n \"author\": \"J.R.R. Tolkien\",\n \"title\": \"The Lord of the Rings\",\n \"genre\": \"Fantasy\",\n \"year\": 1954\n },\n {\n \"author\": \"Ernest Hemingway\",\n \"title\": \"The Old Man and the Sea\",\n \"genre\": \"Literary Fiction\",\n \"year\": 1952\n },\n {\n \"author\": \"Charlotte Bronte\",\n \"title\": \"Jane Eyre\",\n \"genre\": \"Gothic\",\n \"year\": 1847\n },\n {\n \"author\": \"Leo Tolstoy\",\n \"title\": \"War and Peace\",\n \"genre\": \"Historical Fiction\",\n \"year\": 1869\n },\n {\n \"author\": \"Emily Bronte\",\n \"title\": \"Wuthering Heights\",\n \"genre\": \"Gothic Romance\",\n \"year\": 1847\n },\n {\n \"author\": \"Ray Bradbury\",\n \"title\": \"Fahrenheit 451\",\n \"genre\": \"Science Fiction\",\n \"year\": 1953\n },\n {\n \"author\": \"Arthur Conan Doyle\",\n \"title\": \"The Adventures of Sherlock Holmes\",\n \"genre\": \"Mystery\",\n \"year\": 1892\n },\n {\n \"author\": \"Margaret Atwood\",\n \"title\": \"The Handmaid's Tale\",\n \"genre\": \"Dystopian\",\n \"year\": 1985\n },\n {\n \"author\": \"Herman Melville\",\n \"title\": \"Moby Dick\",\n \"genre\": \"Adventure\",\n \"year\": 1851\n },\n {\n \"author\": \"Kazuo Ishiguro\",\n \"title\": \"Never Let Me Go\",\n \"genre\": \"Science Fiction\",\n \"year\": 2005\n }\n]","output":"json","x":580,"y":1920,"wires":[["5651b0896965ae6a"]]},{"id":"5651b0896965ae6a","type":"function","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"filter","func":"const books = msg.library\nconst genre = msg.payload.genre\n\nif (!Array.isArray(books)) {\n throw new Error('Payload is not an array of books')\n}\n\nif (typeof genre !== 'string') {\n throw new Error('Genre must be a string')\n}\n\nfunction looseCompareGenre(genre, search) {\n return genre.toLowerCase().includes(search.toLowerCase())\n}\nconst filteredBooks = books.filter(book => looseCompareGenre(book.genre, genre))\n\nmsg.payload = filteredBooks\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":720,"y":1920,"wires":[["066f6c58bc45d617"]]},{"id":"6bcaef4ed1ab82ae","type":"catch","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"","scope":"group","uncaught":false,"x":860,"y":1980,"wires":[["350db73b93a26f8d"]]},{"id":"3d6e0d7e21b44845","type":"mcp-resource","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"","server":"28907ed9ddcdd4b9","resourceUri":"db://recipes","resourceId":"recipes","title":"Recipes Array","description":"JSON Array of all recipes","mimeType":"application/json","x":380,"y":1820,"wires":[["84ebc9b12d948173"]]},{"id":"350db73b93a26f8d","type":"junction","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","x":1010,"y":1880,"wires":[["bb258a21622c01b9","fac46d1d55d9ca22"]]},{"id":"fac46d1d55d9ca22","type":"debug","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"resource response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1140,"y":1860,"wires":[]},{"id":"84ebc9b12d948173","type":"template","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"db query for recipes","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"{\n \"recipes\": [\n {\n \"name\": \"Spaghetti Aglio e Olio\",\n \"ingredients\": [\n \"Spaghetti\",\n \"Garlic\",\n \"Olive Oil\",\n \"Red Pepper Flakes\",\n \"Parsley\"\n ],\n \"method\": \"Cook spaghetti, sauté garlic in olive oil, add red pepper flakes, toss with cooked spaghetti, garnish with parsley.\",\n \"wine_paring\": \"Pinot Grigio\"\n },\n {\n \"name\": \"Chicken Alfredo Pasta\",\n \"ingredients\": [\n \"Chicken Breast\",\n \"Fettuccine Pasta\",\n \"Heavy Cream\",\n \"Parmesan Cheese\",\n \"Garlic\",\n \"Butter\"\n ],\n \"method\": \"Cook chicken, cook pasta, make alfredo sauce with cream, parmesan, garlic, and butter, combine all.\",\n \"wine_paring\": \"Chardonnay\"\n },\n {\n \"name\": \"Caprese Salad\",\n \"ingredients\": [\n \"Tomatoes\",\n \"Fresh Mozzarella\",\n \"Basil\",\n \"Olive Oil\",\n \"Balsamic Vinegar\",\n \"Salt\",\n \"Pepper\"\n ],\n \"method\": \"Slice tomatoes and mozzarella, layer with basil, drizzle with olive oil and balsamic vinegar, season with salt and pepper.\",\n \"wine_paring\": \"Chianti\"\n },\n {\n \"name\": \"Beef Tacos\",\n \"ingredients\": [\n \"Ground Beef\",\n \"Taco Seasoning\",\n \"Tortillas\",\n \"Lettuce\",\n \"Tomatoes\",\n \"Cheese\",\n \"Sour Cream\"\n ],\n \"method\": \"Cook beef with taco seasoning, assemble tacos with beef, lettuce, tomatoes, cheese, and sour cream.\",\n \"wine_paring\": null\n },\n {\n \"name\": \"Vegetable Stir Fry\",\n \"ingredients\": [\n \"Mixed Vegetables\",\n \"Soy Sauce\",\n \"Garlic\",\n \"Ginger\",\n \"Sesame Oil\",\n \"Rice\"\n ],\n \"method\": \"Stir fry vegetables with soy sauce, garlic, and ginger, finish with sesame oil, serve over rice.\",\n \"wine_paring\": \"Riesling\"\n },\n {\n \"name\": \"Margherita Pizza\",\n \"ingredients\": [\n \"Pizza Dough\",\n \"Tomato Sauce\",\n \"Fresh Mozzarella\",\n \"Basil\",\n \"Olive Oil\"\n ],\n \"method\": \"Top pizza dough with sauce, mozzarella, and basil, drizzle with olive oil, bake until crust is golden.\",\n \"wine_paring\": \"Merlot\"\n },\n {\n \"name\": \"Grilled Salmon\",\n \"ingredients\": [\n \"Salmon Fillet\",\n \"Lemon\",\n \"Garlic\",\n \"Dill\",\n \"Olive Oil\"\n ],\n \"method\": \"Marinate salmon with lemon, garlic, dill, and olive oil, grill until cooked through.\",\n \"wine_paring\": \"Sauvignon Blanc\"\n },\n {\n \"name\": \"Pasta Primavera\",\n \"ingredients\": [\n \"Pasta\",\n \"Assorted Vegetables\",\n \"Cream Sauce\",\n \"Garlic\",\n \"Parmesan Cheese\"\n ],\n \"method\": \"Cook pasta, sauté vegetables, add cream sauce, garlic, and parmesan, toss with cooked pasta.\",\n \"wine_paring\": \"Chardonnay\"\n },\n {\n \"name\": \"Chicken Caesar Salad\",\n \"ingredients\": [\n \"Chicken Breast\",\n \"Romaine Lettuce\",\n \"Caesar Dressing\",\n \"Croutons\",\n \"Parmesan Cheese\"\n ],\n \"method\": \"Grill chicken, chop lettuce, toss with dressing, croutons, and parmesan, top with grilled chicken.\",\n \"wine_paring\": \"Sauvignon Blanc\"\n },\n {\n \"name\": \"Chocolate Chip Cookies\",\n \"ingredients\": [\n \"Flour\",\n \"Butter\",\n \"Sugar\",\n \"Eggs\",\n \"Chocolate Chips\",\n \"Vanilla Extract\",\n \"Baking Soda\"\n ],\n \"method\": \"Cream butter and sugar, add eggs and vanilla, mix in dry ingredients and chocolate chips, bake until golden.\",\n \"wine_paring\": null\n }\n ]\n}","output":"str","x":630,"y":1820,"wires":[["d6f015cb6debe35b"]]},{"id":"9d4edda03c19b553","type":"comment","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"Status Resource Example","info":"","x":420,"y":1780,"wires":[]},{"id":"d3aa7bfa90c9e24e","type":"comment","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","name":"Dynamic Resource Example","info":"","x":430,"y":1880,"wires":[]},{"id":"d6f015cb6debe35b","type":"junction","z":"e1ceeedf31ce1ebd","g":"00744c2e2a560c36","x":930,"y":1820,"wires":[["350db73b93a26f8d"]]},{"id":"28907ed9ddcdd4b9","type":"mcp-server","name":"My Node-RED MCP Server","protocol":"http","path":"/mcp"},{"id":"3d12514448ee3580","type":"global-config","env":[],"modules":{"@flowfuse-nodes/nr-mcp-server-nodes":"0.1.1"}}]
-{% endrenderFlow %}
\ No newline at end of file
+```
+::
diff --git a/src/node-red/flowfuse/mcp/mcp-response.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md
similarity index 92%
rename from src/node-red/flowfuse/mcp/mcp-response.md
rename to nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md
index a0d5444238..7d5a3e691a 100644
--- a/src/node-red/flowfuse/mcp/mcp-response.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: MCP Responses
- parent: MCP
+title: MCP Response
+navTitle: MCP Responses
+navOrder: 50
meta:
- title: MCP Response
description: Sends responses back to the MCP client for tools and resources.
---
-# {{ meta.title }}
+# MCP Response
Sends responses back to the MCP client for tools and resources. This node should be the final node in any flow that begins with an MCP Tool or MCP Resource node.
@@ -40,4 +39,4 @@ The node accepts `msg.payload` containing the data to return to the MCP client.
[MCP Tool/Resource] → [Your Processing Nodes] → [MCP Response]
```
-The MCP Response node completes the request cycle by sending your processed data back to the AI assistant that made the request.
\ No newline at end of file
+The MCP Response node completes the request cycle by sending your processed data back to the AI assistant that made the request.
diff --git a/src/node-red/flowfuse/mcp/mcp-tool.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md
similarity index 98%
rename from src/node-red/flowfuse/mcp/mcp-tool.md
rename to nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md
index 06af642559..73d366c76b 100644
--- a/src/node-red/flowfuse/mcp/mcp-tool.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: MCP Tool
- parent: MCP
+title: MCP Tool
+navTitle: MCP Tool
+navOrder: 50
meta:
- title: MCP Tool
description: MCP Tool node allows you to create custom tools that FlowFuse Expert can invoke to perform specific tasks.
---
-# {{ meta.title }}
+# MCP Tool
MCP Tool node allows you to create custom tools that FlowFuse Expert can invoke to perform specific tasks. These tools can do anything a Node-RED flow can do - from querying databases and calling APIs to controlling IoT devices and processing data. The FlowFuse Expert decides when to call your tool based on the description and input schema you provide.
@@ -159,6 +158,8 @@ You can then use these values in subsequent nodes to perform your tool's logic.
## Example Flow
-{% renderFlow %}
+::render-flow
+```json
[{"id":"4076896ebd9fb8b4","type":"group","z":"e1ceeedf31ce1ebd","name":"MCP Tools","style":{"label":true},"nodes":["670d69227fd02715","d473b3e38011a7d1","8bb8104fab25a772","b4398fb68fb3d363","561f103a1ac605c4","d31cc0acdb813863","3c23f963f4982c1a","644e76704eb56f41","30ead11f52f1bf19","8b5faff4ad12d406","079eacf59ffc7032"],"x":254,"y":1399,"w":892,"h":282},{"id":"670d69227fd02715","type":"mcp-tool","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","server":"28907ed9ddcdd4b9","toolName":"greeting","title":"","description":"Greet person by name","inputSchema":"{\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"The name to greet\",\n \"minLength\": 1\n }\n },\n \"required\": [\"name\"]\n}","x":340,"y":1480,"wires":[["8bb8104fab25a772"]]},{"id":"d473b3e38011a7d1","type":"change","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"This response is defined in a Node-RED change node. Hi \" & payload.name","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":1480,"wires":[["079eacf59ffc7032"]]},{"id":"8bb8104fab25a772","type":"delay","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":540,"y":1480,"wires":[["d473b3e38011a7d1"]]},{"id":"b4398fb68fb3d363","type":"mcp-tool","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","server":"28907ed9ddcdd4b9","toolName":"get_iss_position","title":"Get ISS Position","description":"Retrieves the latitude and longitude of the Internanational Space Station","inputSchema":"{}","x":360,"y":1580,"wires":[["561f103a1ac605c4"]]},{"id":"561f103a1ac605c4","type":"http request","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://api.open-notify.org/iss-now.json","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":730,"y":1580,"wires":[["079eacf59ffc7032"]]},{"id":"d31cc0acdb813863","type":"catch","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","scope":"group","uncaught":false,"x":730,"y":1640,"wires":[["079eacf59ffc7032"]]},{"id":"3c23f963f4982c1a","type":"debug","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"tool response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1020,"y":1520,"wires":[]},{"id":"644e76704eb56f41","type":"mcp-response","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"","x":1020,"y":1560,"wires":[]},{"id":"30ead11f52f1bf19","type":"comment","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"Simple Greeting Tool","info":"","x":370,"y":1440,"wires":[]},{"id":"8b5faff4ad12d406","type":"comment","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","name":"Get IIS Position Tool","info":"","x":370,"y":1540,"wires":[]},{"id":"079eacf59ffc7032","type":"junction","z":"e1ceeedf31ce1ebd","g":"4076896ebd9fb8b4","x":880,"y":1540,"wires":[["644e76704eb56f41","3c23f963f4982c1a"]]},{"id":"28907ed9ddcdd4b9","type":"mcp-server","name":"My Node-RED MCP Server","protocol":"http","path":"/mcp"},{"id":"c05fb032a62e8357","type":"global-config","env":[],"modules":{"@flowfuse-nodes/nr-mcp-server-nodes":"0.1.1"}}]
-{% endrenderFlow %}
\ No newline at end of file
+```
+::
diff --git a/src/node-red/flowfuse/mqtt/index.md b/nuxt/content-guides/flowfuse-nodes/mqtt/README.md
similarity index 64%
rename from src/node-red/flowfuse/mqtt/index.md
rename to nuxt/content-guides/flowfuse-nodes/mqtt/README.md
index db2b9535c8..998cf4d690 100644
--- a/src/node-red/flowfuse/mqtt/index.md
+++ b/nuxt/content-guides/flowfuse-nodes/mqtt/README.md
@@ -1,15 +1,12 @@
---
-eleventyNavigation:
- key: FlowFuse MQTT Nodes
- parent: FlowFuse Nodes
- order: 5
- title: MQTT
+title: MQTT Nodes
+navTitle: FlowFuse MQTT Nodes
+navOrder: 5
meta:
- title: MQTT Nodes
description: MQTT In and Out nodes designed for FlowFuse users with automatic configuration.
---
-# {{ meta.title }}
+# MQTT Nodes
This document lists and explains the **MQTT nodes** available in FlowFuse. These nodes are enhanced versions of the standard **MQTT In** and **MQTT Out** nodes in Node-RED, designed for FlowFuse users.
@@ -19,6 +16,7 @@ They are tightly integrated with the [FlowFuse MQTT Broker Service](/docs/user/t
This section lists the document of **MQTT nodes** available in FlowFuse:
-{% include "navigation-items-list.njk" %}
+- [MQTT In](/docs/flowfuse-nodes/mqtt/mqtt-in/): Enhanced MQTT In node for FlowFuse with automatic broker setup, dynamic subscriptions, wildcard topic support, and full MQTT v5 compatibility.
+- [MQTT Out](/docs/flowfuse-nodes/mqtt/mqtt-out/): Enhanced MQTT Out node for FlowFuse with automatic broker setup, dynamic topic control, and full MQTT v5 support.
-Each node extends standard MQTT functionality with automatic configuration and full MQTT v5 support, making it easier to build reliable and real-time communication flows inside FlowFuse.
\ No newline at end of file
+Each node extends standard MQTT functionality with automatic configuration and full MQTT v5 support, making it easier to build reliable and real-time communication flows inside FlowFuse.
diff --git a/src/node-red/flowfuse/mqtt/mqtt-in.md b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md
similarity index 96%
rename from src/node-red/flowfuse/mqtt/mqtt-in.md
rename to nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md
index 78938bb8b7..6eaba1f802 100644
--- a/src/node-red/flowfuse/mqtt/mqtt-in.md
+++ b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: MQTT In
- parent: FlowFuse MQTT Nodes
+title: MQTT In
+navTitle: MQTT In
+navOrder: 50
meta:
- title: MQTT In
description: Enhanced MQTT In node for FlowFuse with automatic broker setup, dynamic subscriptions, wildcard topic support, and full MQTT v5 compatibility.
---
-# {{ meta.title }}
+# MQTT In
This is an enhanced version of the standard MQTT In node, designed exclusively for FlowFuse users. The node features automatic configuration upon deployment. The [MQTT broker client](/docs/user/teambroker/) is created automatically alongside the node configuration when added to the canvas.
@@ -80,4 +79,4 @@ MQTT supports two wildcard characters for flexible topic matching:
## Version Support
-This node fully supports MQTT version 5 features including response topics, correlation data, content types, user properties, message expiry intervals, and topic aliases. It maintains backward compatibility with earlier MQTT versions.
\ No newline at end of file
+This node fully supports MQTT version 5 features including response topics, correlation data, content types, user properties, message expiry intervals, and topic aliases. It maintains backward compatibility with earlier MQTT versions.
diff --git a/src/node-red/flowfuse/mqtt/mqtt-out.md b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md
similarity index 97%
rename from src/node-red/flowfuse/mqtt/mqtt-out.md
rename to nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md
index 8501166bf3..728af1bc78 100644
--- a/src/node-red/flowfuse/mqtt/mqtt-out.md
+++ b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: MQTT Out
- parent: FlowFuse MQTT Nodes
+title: MQTT Out
+navTitle: MQTT Out
+navOrder: 50
meta:
- title: MQTT Out
description: Enhanced MQTT Out node for FlowFuse with automatic broker setup, dynamic topic control, and full MQTT v5 support.
---
-# {{ meta.title }}
+# MQTT Out
This is an enhanced version of the standard MQTT Out node, designed exclusively for FlowFuse users. The node features automatic configuration upon deployment when using within Flowfuse instance. The [MQTT broker](/docs/user/teambroker/) client is created automatically alongside the node configuration when added to the canvas.
diff --git a/src/node-red/core-nodes/images/batch-example1.png b/nuxt/content-guides/node-red/core-nodes/images/batch-example1.png
similarity index 100%
rename from src/node-red/core-nodes/images/batch-example1.png
rename to nuxt/content-guides/node-red/core-nodes/images/batch-example1.png
diff --git a/src/node-red/core-nodes/images/batch-example2.png b/nuxt/content-guides/node-red/core-nodes/images/batch-example2.png
similarity index 100%
rename from src/node-red/core-nodes/images/batch-example2.png
rename to nuxt/content-guides/node-red/core-nodes/images/batch-example2.png
diff --git a/src/node-red/core-nodes/images/batch-example3.webm b/nuxt/content-guides/node-red/core-nodes/images/batch-example3.webm
similarity index 100%
rename from src/node-red/core-nodes/images/batch-example3.webm
rename to nuxt/content-guides/node-red/core-nodes/images/batch-example3.webm
diff --git a/src/node-red/core-nodes/images/batch-example4.png b/nuxt/content-guides/node-red/core-nodes/images/batch-example4.png
similarity index 100%
rename from src/node-red/core-nodes/images/batch-example4.png
rename to nuxt/content-guides/node-red/core-nodes/images/batch-example4.png
diff --git a/src/node-red/core-nodes/images/batch-example5.png b/nuxt/content-guides/node-red/core-nodes/images/batch-example5.png
similarity index 100%
rename from src/node-red/core-nodes/images/batch-example5.png
rename to nuxt/content-guides/node-red/core-nodes/images/batch-example5.png
diff --git a/src/node-red/core-nodes/images/change-data-transform.png b/nuxt/content-guides/node-red/core-nodes/images/change-data-transform.png
similarity index 100%
rename from src/node-red/core-nodes/images/change-data-transform.png
rename to nuxt/content-guides/node-red/core-nodes/images/change-data-transform.png
diff --git a/src/node-red/core-nodes/images/change-default.png b/nuxt/content-guides/node-red/core-nodes/images/change-default.png
similarity index 100%
rename from src/node-red/core-nodes/images/change-default.png
rename to nuxt/content-guides/node-red/core-nodes/images/change-default.png
diff --git a/src/node-red/core-nodes/images/change-message-enrich.png b/nuxt/content-guides/node-red/core-nodes/images/change-message-enrich.png
similarity index 100%
rename from src/node-red/core-nodes/images/change-message-enrich.png
rename to nuxt/content-guides/node-red/core-nodes/images/change-message-enrich.png
diff --git a/src/node-red/core-nodes/images/change-message-filter.png b/nuxt/content-guides/node-red/core-nodes/images/change-message-filter.png
similarity index 100%
rename from src/node-red/core-nodes/images/change-message-filter.png
rename to nuxt/content-guides/node-red/core-nodes/images/change-message-filter.png
diff --git a/src/node-red/core-nodes/images/change-message-format.png b/nuxt/content-guides/node-red/core-nodes/images/change-message-format.png
similarity index 100%
rename from src/node-red/core-nodes/images/change-message-format.png
rename to nuxt/content-guides/node-red/core-nodes/images/change-message-format.png
diff --git a/src/node-red/core-nodes/images/change-rename-property.png b/nuxt/content-guides/node-red/core-nodes/images/change-rename-property.png
similarity index 100%
rename from src/node-red/core-nodes/images/change-rename-property.png
rename to nuxt/content-guides/node-red/core-nodes/images/change-rename-property.png
diff --git a/src/node-red/core-nodes/images/comment-node-full-text.png b/nuxt/content-guides/node-red/core-nodes/images/comment-node-full-text.png
similarity index 100%
rename from src/node-red/core-nodes/images/comment-node-full-text.png
rename to nuxt/content-guides/node-red/core-nodes/images/comment-node-full-text.png
diff --git a/src/node-red/core-nodes/images/debug-copy-path.webm b/nuxt/content-guides/node-red/core-nodes/images/debug-copy-path.webm
similarity index 100%
rename from src/node-red/core-nodes/images/debug-copy-path.webm
rename to nuxt/content-guides/node-red/core-nodes/images/debug-copy-path.webm
diff --git a/src/node-red/core-nodes/images/debug-copy-value.webm b/nuxt/content-guides/node-red/core-nodes/images/debug-copy-value.webm
similarity index 100%
rename from src/node-red/core-nodes/images/debug-copy-value.webm
rename to nuxt/content-guides/node-red/core-nodes/images/debug-copy-value.webm
diff --git a/src/node-red/core-nodes/images/debug-examples.png b/nuxt/content-guides/node-red/core-nodes/images/debug-examples.png
similarity index 100%
rename from src/node-red/core-nodes/images/debug-examples.png
rename to nuxt/content-guides/node-red/core-nodes/images/debug-examples.png
diff --git a/src/node-red/core-nodes/images/debug-panel.png b/nuxt/content-guides/node-red/core-nodes/images/debug-panel.png
similarity index 100%
rename from src/node-red/core-nodes/images/debug-panel.png
rename to nuxt/content-guides/node-red/core-nodes/images/debug-panel.png
diff --git a/src/node-red/core-nodes/images/debug-pin-open.webm b/nuxt/content-guides/node-red/core-nodes/images/debug-pin-open.webm
similarity index 100%
rename from src/node-red/core-nodes/images/debug-pin-open.webm
rename to nuxt/content-guides/node-red/core-nodes/images/debug-pin-open.webm
diff --git a/src/node-red/core-nodes/images/delay-node-2.png b/nuxt/content-guides/node-red/core-nodes/images/delay-node-2.png
similarity index 100%
rename from src/node-red/core-nodes/images/delay-node-2.png
rename to nuxt/content-guides/node-red/core-nodes/images/delay-node-2.png
diff --git a/src/node-red/core-nodes/images/inject-config-string.png b/nuxt/content-guides/node-red/core-nodes/images/inject-config-string.png
similarity index 100%
rename from src/node-red/core-nodes/images/inject-config-string.png
rename to nuxt/content-guides/node-red/core-nodes/images/inject-config-string.png
diff --git a/src/node-red/core-nodes/images/link-node.png b/nuxt/content-guides/node-red/core-nodes/images/link-node.png
similarity index 100%
rename from src/node-red/core-nodes/images/link-node.png
rename to nuxt/content-guides/node-red/core-nodes/images/link-node.png
diff --git a/src/node-red/core-nodes/images/list-nodes-unused.png b/nuxt/content-guides/node-red/core-nodes/images/list-nodes-unused.png
similarity index 100%
rename from src/node-red/core-nodes/images/list-nodes-unused.png
rename to nuxt/content-guides/node-red/core-nodes/images/list-nodes-unused.png
diff --git a/src/node-red/core-nodes/images/mqtt-config.png b/nuxt/content-guides/node-red/core-nodes/images/mqtt-config.png
similarity index 100%
rename from src/node-red/core-nodes/images/mqtt-config.png
rename to nuxt/content-guides/node-red/core-nodes/images/mqtt-config.png
diff --git a/src/node-red/core-nodes/images/range-node2.png b/nuxt/content-guides/node-red/core-nodes/images/range-node2.png
similarity index 100%
rename from src/node-red/core-nodes/images/range-node2.png
rename to nuxt/content-guides/node-red/core-nodes/images/range-node2.png
diff --git a/src/node-red/core-nodes/images/spawn-mode-exec.png b/nuxt/content-guides/node-red/core-nodes/images/spawn-mode-exec.png
similarity index 100%
rename from src/node-red/core-nodes/images/spawn-mode-exec.png
rename to nuxt/content-guides/node-red/core-nodes/images/spawn-mode-exec.png
diff --git a/src/node-red/core-nodes/images/status-node-output.png b/nuxt/content-guides/node-red/core-nodes/images/status-node-output.png
similarity index 100%
rename from src/node-red/core-nodes/images/status-node-output.png
rename to nuxt/content-guides/node-red/core-nodes/images/status-node-output.png
diff --git a/src/node-red/core-nodes/images/status-node.png b/nuxt/content-guides/node-red/core-nodes/images/status-node.png
similarity index 100%
rename from src/node-red/core-nodes/images/status-node.png
rename to nuxt/content-guides/node-red/core-nodes/images/status-node.png
diff --git a/src/node-red/core-nodes/images/switch-example-2.png b/nuxt/content-guides/node-red/core-nodes/images/switch-example-2.png
similarity index 100%
rename from src/node-red/core-nodes/images/switch-example-2.png
rename to nuxt/content-guides/node-red/core-nodes/images/switch-example-2.png
diff --git a/src/node-red/core-nodes/images/system-info-installed-nodes.webm b/nuxt/content-guides/node-red/core-nodes/images/system-info-installed-nodes.webm
similarity index 100%
rename from src/node-red/core-nodes/images/system-info-installed-nodes.webm
rename to nuxt/content-guides/node-red/core-nodes/images/system-info-installed-nodes.webm
diff --git a/src/node-red/core-nodes/images/wired-up-exec-node.png b/nuxt/content-guides/node-red/core-nodes/images/wired-up-exec-node.png
similarity index 100%
rename from src/node-red/core-nodes/images/wired-up-exec-node.png
rename to nuxt/content-guides/node-red/core-nodes/images/wired-up-exec-node.png
diff --git a/nuxt/content-guides/node-red/database/README.md b/nuxt/content-guides/node-red/database/README.md
new file mode 100644
index 0000000000..1e0d7ee805
--- /dev/null
+++ b/nuxt/content-guides/node-red/database/README.md
@@ -0,0 +1,30 @@
+---
+title: Node-RED Database Integration Guides
+navTitle: Databases
+navOrder: 5
+meta:
+ description: Explore database integration guides for Node-RED, including PostgreSQL, MongoDB, InfluxDB, DynamoDB, and TimescaleDB
+ keywords: node-red, databases, integration, PostgreSQL, MongoDB, InfluxDB, DynamoDB, TimescaleDB
+---
+
+# Node-RED Database Integration Guides
+
+Node-RED is highly versatile and can be set up to work with a variety of databases, whether it is SQL (e.g., PostgreSQL, MySQL), NoSQL (e.g., MongoDB), or time-series databases (e.g., InfluxDB). This flexibility allows you to store and manage IoT data effectively, enabling the creation of interactive and data-driven applications for IoT environments.
+
+## Resources
+
+Here are some resources to help you get started with Node-RED on diffrent types of databases. Each guide provides step-by-step instructions to help you get started, along with advanced techniques for optimizing performance and handling complex data operations.:
+
+- [Using DynamoDB with Node-RED (2026 Updated)](/docs/node-red/database/dynamodb/): Get started with AWS' NoSQL database DynamoDB with Node-RED
+- [Using Firebase with Node-RED (2026 Updated)](/docs/node-red/database/firebase/): Learn how to integrate Cloud Firestore with Node-RED to build real-time event-driven applications. This guide covers Firestore setup, reading, writing, and listening to data using Node-RED.
+- [Using InfluxDB with Node-RED (2026 Updated)](/docs/node-red/database/influxdb/): Node-RED has great support for InfluxDB. In this guide, we'll explain how to get your data flowing into one of the most popular time-series databases.
+- [Using MongoDB With Node-RED (2026 Updated)](/docs/node-red/database/mongodb/): Learn how to seamlessly integrate MongoDB, a NoSQL database, into your Node-RED applications with this step-by-step documentation.
+- [Using MySQL with Node-RED (2026 Updated)](/docs/node-red/database/mysql/): Learn how to seamlessly integrate MySQL with Node-RED for efficient data management and application development.
+- [Using PostgreSQL with Node-RED (2026 Updated)](/docs/node-red/database/postgresql/): Learn how to seamlessly integrate PostgreSQL with Node-RED for efficient data management and application development.
+- [Using Redis with Node-RED (2026 Updated)](/docs/node-red/database/redis/): Learn how to integrate Redis with Node-RED for fast data storage, pub/sub messaging, JSON handling, Lua scripting, and advanced Redis operations in Node-RED flows.
+- [Using SQLite with Node-RED (2026 Updated)](/docs/node-red/database/sqlite/): Learn how to seamlessly integrate SQLite with Node-RED for efficient data management and application development.
+- [Using TimescaleDB with Node-RED (2026 Updated)](/docs/node-red/database/timescaledb/): Learn how to integrate TimescaleDB with Node-RED for storing and managing time-series data efficiently.
+
+::callout{icon="i-lucide-badge-check"}
+**A certified node for these databases.** The guides in this section use community packages. One database here is also covered by a FlowFuse certified node, maintained for production use: [redis](/integrations/?certified=1)
+::
diff --git a/src/node-red/database/dynamodb.md b/nuxt/content-guides/node-red/database/dynamodb.md
similarity index 98%
rename from src/node-red/database/dynamodb.md
rename to nuxt/content-guides/node-red/database/dynamodb.md
index 297bfd82dc..f3bf0e7811 100644
--- a/src/node-red/database/dynamodb.md
+++ b/nuxt/content-guides/node-red/database/dynamodb.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: DynamoDB
- parent: Database
+title: Using DynamoDB with Node-RED (2026 Updated)
+navTitle: DynamoDB
+navOrder: 50
meta:
- title: Using DynamoDB with Node-RED (2026 Updated)
description: Get started with AWS' NoSQL database DynamoDB with Node-RED
keywords: node-red, flowfuse, integration, dynamodb, aws, nosql
-image: /node-red/core-nodes/images/node-red-dynamodb.png
+image: /docs/node-red/core-nodes/images/node-red-dynamodb.png
---
-# {{ meta.title }}
+# Using DynamoDB with Node-RED (2026 Updated)
Amazon's DynamoDB is a fully managed NoSQL database service known for its fast and predictable performance and scalable design. This makes it suitable for applications needing low-latency responses. In this documentation, we’ll look at how to set up and use DynamoDB, configure the necessary IAM roles, and apply Node-RED flows to store and retrieve data effectively.
@@ -69,9 +68,11 @@ Now, let's dive into some practical examples using Node-RED to manage a customer
This first flow will generate data and send it to DynamoDB via the PutItem operation.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"10b0b970739d616f","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"","operation":"PutItem","Statements":"","RequestItems":"","TableName":"FlowFuse-Demo","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":820,"y":240,"wires":[["0157a98fa69fe514"],["dc92470ddaa225bf"]]},{"id":"functionNode","type":"function","z":"37b2bc0d1ebdc616","name":"Set Value","func":"msg.Item = {\n \"source\": { \"S\": msg.source },\n \"time\": { \"N\": String(msg.payload) },\n \"temp\": {\"S\": String(msg.datagen.temp) },\n \"email\": {\"S\": msg.datagen.email},\n \"name\": {\"S\": msg.datagen.name},\n \"work\": {\"S\": msg.datagen.work},\n \"address\": {\"S\": msg.datagen.address},\n \"country\": {\"S\": msg.datagen.country},\n};\n\nmsg.TableName = \"FlowFuse-Email\";\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":240,"wires":[["10b0b970739d616f"]]},{"id":"0157a98fa69fe514","type":"debug","z":"37b2bc0d1ebdc616","name":"Store Customer Info","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1060,"y":220,"wires":[]},{"id":"dc92470ddaa225bf","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 28","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1020,"y":260,"wires":[]},{"id":"45d46649bae57332","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"source","v":"FF_DEVICE_NAME","vt":"env"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":240,"wires":[["1160ce65b26429f3"]]},{"id":"1160ce65b26429f3","type":"data-generator","z":"37b2bc0d1ebdc616","name":"","field":"datagen","fieldType":"msg","syntax":"json","template":"{\n \"name\": \"{{firstName}} {{lastName}}\",\n \"work\": \"{{company}}\",\n \"email\": \"{{email}}\",\n \"address\": \"{{int 1 100}} {{street}}\",\n \"country\": \"{{country}}\",\n \"temp\": {{float 30 36}}\n}","x":440,"y":240,"wires":[["functionNode"]]}]
-{% endrenderFlow %}
+```
+::
1. Configure the AWS DyanamoDB node by **editing** the node.
2. Click the **pencil** to create a new config.
@@ -89,9 +90,11 @@ This first flow will generate data and send it to DynamoDB via the PutItem opera
The GetItem operation fetches data, which you can then display using debug nodes. To retrieve specific customer information based on their email import this flow:
-{% renderFlow %}
+::render-flow
+```json
[{"id":"f7e069b78d2ef5c5","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":340,"wires":[["07caf16318aea2f6"]]},{"id":"07caf16318aea2f6","type":"function","z":"37b2bc0d1ebdc616","name":"Get Specific Customer Info","func":"msg = {\n TableName: \"FlowFuse-Email\",\n Key: {\n \"email\": { \"S\": \"florance.shelly@cirpria.xyz\" } //put email you want to search here\n }\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":340,"wires":[["3485e6cddd28c3eb"]]},{"id":"3485e6cddd28c3eb","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"","operation":"GetItem","Statements":"","RequestItems":"","TableName":"","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":760,"y":340,"wires":[["0c0b39c735d71d77"],["ae673a52c1c7a123"]]},{"id":"0c0b39c735d71d77","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 30","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1000,"y":320,"wires":[]},{"id":"ae673a52c1c7a123","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 31","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1000,"y":360,"wires":[]}]
-{% endrenderFlow %}
+```
+::
{data-zoomable}
@@ -101,9 +104,11 @@ The function node specifies the **TableName** and **Partition key** to search ag
When you need to find items under specific criteria, set up your query with partition keys and conditions. The Query operation allows you to efficiently retrieve data without scanning the entire contents of a particular partition key. If a key has significant about of data, but only need one particular value. This would then be the ideal path.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"99a876a56a78ae25","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":420,"wires":[["d59f213bc3f6712a"]]},{"id":"d59f213bc3f6712a","type":"function","z":"37b2bc0d1ebdc616","name":"Returns only the attriubutes desired","func":"msg.TableName = \"FlowFuse-Email\";\nmsg.KeyConditionExpression = \"email = :email\";\nmsg.ExpressionAttributeValues = {\n \":email\": { \"S\": \"florance.shelly@cirpria.xyz\" }\n};\nmsg.ProjectionExpression = \"#n, #t\";\nmsg.ExpressionAttributeNames = {\n \"#t\": \"temp\",\n \"#n\": \"name\"\n} // Only retrieve these attributes\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":420,"wires":[["e178ffb9a87226a5"]]},{"id":"e178ffb9a87226a5","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"","operation":"Query","Statements":"","RequestItems":"","TableName":"","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":730,"y":420,"wires":[["b2e6d9d9dcf9dcd3"],["b2595472f1dc6570"]]},{"id":"b2e6d9d9dcf9dcd3","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 32","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":400,"wires":[]},{"id":"b2595472f1dc6570","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 33","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":440,"wires":[]}]
-{% endrenderFlow %}
+```
+::
In this example we are retrieving only **name** and **temp** from the matching partition key match.
@@ -113,14 +118,18 @@ In this example we are retrieving only **name** and **temp** from the matching p
While scanning is available, it should be used sparingly due to its high demand on resources, especially in large databases. Use it when necessary prioritize using query for regular operations.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"671cf37c1ae941d2","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"TableName","v":"FlowFuse-Email","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":540,"wires":[["8cb14f9530eeb524"]]},{"id":"8cb14f9530eeb524","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"","operation":"Scan","Statements":"","RequestItems":"","TableName":"","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":470,"y":540,"wires":[["86e3a35428218d91"],["5c6e85699cc783fc"]]},{"id":"86e3a35428218d91","type":"debug","z":"37b2bc0d1ebdc616","name":"Get Values From Partition Key","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":730,"y":520,"wires":[]},{"id":"5c6e85699cc783fc","type":"debug","z":"37b2bc0d1ebdc616","name":"Should be used sparingly for Large databases","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":560,"wires":[]}]
-{% endrenderFlow %}
+```
+::
{data-zoomable}
## Full Flow
-{% renderFlow 400 %}
+::render-flow{:height="400"}
+```json
[{"id":"10b0b970739d616f","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"75ba91a6ba62cece","operation":"PutItem","Statements":"","RequestItems":"","TableName":"FlowFuse-Email","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":820,"y":220,"wires":[["0157a98fa69fe514"],["dc92470ddaa225bf"]]},{"id":"functionNode","type":"function","z":"37b2bc0d1ebdc616","name":"Set Value","func":"msg.Item = {\n \"source\": { \"S\": msg.source },\n \"time\": { \"N\": String(msg.payload) },\n \"temp\": {\"S\": String(msg.datagen.temp) },\n \"email\": {\"S\": msg.datagen.email},\n \"name\": {\"S\": msg.datagen.name},\n \"work\": {\"S\": msg.datagen.work},\n \"address\": {\"S\": msg.datagen.address},\n \"country\": {\"S\": msg.datagen.country},\n};\n\nmsg.TableName = \"FlowFuse-Email\";\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":220,"wires":[["10b0b970739d616f"]]},{"id":"0157a98fa69fe514","type":"debug","z":"37b2bc0d1ebdc616","name":"Store Customer Info","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1060,"y":200,"wires":[]},{"id":"dc92470ddaa225bf","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 28","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1020,"y":240,"wires":[]},{"id":"45d46649bae57332","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"source","v":"FF_DEVICE_NAME","vt":"env"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":220,"wires":[["1160ce65b26429f3"]]},{"id":"f7e069b78d2ef5c5","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":320,"wires":[["07caf16318aea2f6"]]},{"id":"07caf16318aea2f6","type":"function","z":"37b2bc0d1ebdc616","name":"Get Specific Customer Info","func":"msg = {\n TableName: \"FlowFuse-Email\",\n Key: {\n \"email\": { \"S\": \"florance.shelly@cirpria.xyz\" } //put email you want to search here\n }\n};\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":320,"wires":[["3485e6cddd28c3eb"]]},{"id":"3485e6cddd28c3eb","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"75ba91a6ba62cece","operation":"GetItem","Statements":"","RequestItems":"","TableName":"","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":760,"y":320,"wires":[["0c0b39c735d71d77"],["ae673a52c1c7a123"]]},{"id":"0c0b39c735d71d77","type":"debug","z":"37b2bc0d1ebdc616","name":"Get Customer Info","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1030,"y":300,"wires":[]},{"id":"ae673a52c1c7a123","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 31","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1000,"y":340,"wires":[]},{"id":"99a876a56a78ae25","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":420,"wires":[["d59f213bc3f6712a"]]},{"id":"d59f213bc3f6712a","type":"function","z":"37b2bc0d1ebdc616","name":"Returns only the attriubutes desired","func":"msg.TableName = \"FlowFuse-Email\";\nmsg.KeyConditionExpression = \"email = :email\";\nmsg.ExpressionAttributeValues = {\n \":email\": { \"S\": \"florance.shelly@cirpria.xyz\" }\n};\nmsg.ProjectionExpression = \"#n, #t\";\nmsg.ExpressionAttributeNames = {\n \"#t\": \"temp\",\n \"#n\": \"name\"\n} // Only retrieve these attributes\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":420,"wires":[["e178ffb9a87226a5"]]},{"id":"e178ffb9a87226a5","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"75ba91a6ba62cece","operation":"Query","Statements":"","RequestItems":"","TableName":"","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":730,"y":420,"wires":[["b2e6d9d9dcf9dcd3"],["b2595472f1dc6570"]]},{"id":"b2e6d9d9dcf9dcd3","type":"debug","z":"37b2bc0d1ebdc616","name":"Get Specific Customer Info","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":400,"wires":[]},{"id":"b2595472f1dc6570","type":"debug","z":"37b2bc0d1ebdc616","name":"debug 33","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":440,"wires":[]},{"id":"1160ce65b26429f3","type":"data-generator","z":"37b2bc0d1ebdc616","name":"","field":"datagen","fieldType":"msg","syntax":"json","template":"{\n \"name\": \"{{firstName}} {{lastName}}\",\n \"work\": \"{{company}}\",\n \"email\": \"{{email}}\",\n \"address\": \"{{int 1 100}} {{street}}\",\n \"country\": \"{{country}}\",\n \"temp\": {{float 30 36}}\n}","x":440,"y":220,"wires":[["functionNode"]]},{"id":"671cf37c1ae941d2","type":"inject","z":"37b2bc0d1ebdc616","name":"","props":[{"p":"payload"},{"p":"TableName","v":"FlowFuse-Email","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":540,"wires":[["8cb14f9530eeb524"]]},{"id":"8cb14f9530eeb524","type":"AWS DynamoDB","z":"37b2bc0d1ebdc616","aws":"75ba91a6ba62cece","operation":"Scan","Statements":"","RequestItems":"","TableName":"","BackupName":"","GlobalTableName":"","ReplicationGroup":"","AttributeDefinitions":"","KeySchema":"","BackupArn":"","Key":"","ExportArn":"","Statement":"","TransactStatements":"","TableArn":"","S3Bucket":"","ResourceArn":"","Item":"","TargetTableName":"","Tags":"","TransactItems":"","TagKeys":"","PointInTimeRecoverySpecification":"","ContributorInsightsAction":"","ReplicaUpdates":"","TimeToLiveSpecification":"","name":"","x":470,"y":540,"wires":[["86e3a35428218d91"],["5c6e85699cc783fc"]]},{"id":"86e3a35428218d91","type":"debug","z":"37b2bc0d1ebdc616","name":"Get Values From Partition Key","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":730,"y":520,"wires":[]},{"id":"5c6e85699cc783fc","type":"debug","z":"37b2bc0d1ebdc616","name":"Should be used sparingly for Large databases","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":560,"wires":[]},{"id":"75ba91a6ba62cece","type":"amazon config","name":"AWS-gdziuba","region":"us-east-1","proxyRequired":false,"proxy":""}]
-{% endrenderFlow %}
\ No newline at end of file
+```
+::
diff --git a/src/node-red/database/firebase.md b/nuxt/content-guides/node-red/database/firebase.md
similarity index 99%
rename from src/node-red/database/firebase.md
rename to nuxt/content-guides/node-red/database/firebase.md
index 6edd848246..656db4f9b1 100644
--- a/src/node-red/database/firebase.md
+++ b/nuxt/content-guides/node-red/database/firebase.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: Firebase
- parent: Database
+title: Using Firebase with Node-RED (2026 Updated)
+navTitle: Firebase
+navOrder: 50
meta:
- title: Using Firebase with Node-RED (2026 Updated)
description: Learn how to integrate Cloud Firestore with Node-RED to build real-time event-driven applications. This guide covers Firestore setup, reading, writing, and listening to data using Node-RED.
keywords: node-red, flowfuse, integration, firebase, cloud firestore, google firebase
---
-# {{meta.title}}
+# Using Firebase with Node-RED (2026 Updated)
Firebase provides two database options: Realtime Database (RTDB) and Cloud Firestore. This guide focuses on Cloud Firestore, Firebase's newer, more flexible document database with better performance, richer queries, and multi-regional support.
@@ -170,20 +169,16 @@ This capability is particularly useful for building live dashboards, sending not
The flow below demonstrates all the concepts covered in this guide. You can explore and modify it as needed.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"57c1f30f8a825e5c","type":"group","z":"b5ce73e91740e4b2","name":"","style":{"label":true,"stroke":"#7fb7df"},"nodes":["5792767043952f56","15d852f4a29abec1","16c12e22e1f3b257","c4d08c57eec14060","f69b62a66076edf1"],"x":114,"y":279,"w":972,"h":122},{"id":"5792767043952f56","type":"inject","z":"b5ce73e91740e4b2","g":"57c1f30f8a825e5c","name":"Send Timestamp","props":[{"p":"payload.timestamp","v":"","vt":"date"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":240,"y":360,"wires":[["c4d08c57eec14060"]]},{"id":"15d852f4a29abec1","type":"debug","z":"b5ce73e91740e4b2","g":"57c1f30f8a825e5c","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":360,"wires":[]},{"id":"16c12e22e1f3b257","type":"comment","z":"b5ce73e91740e4b2","g":"57c1f30f8a825e5c","name":"Set Timestamp to \"timestampOverwritten\"","info":"","x":320,"y":320,"wires":[]},{"id":"c4d08c57eec14060","type":"firestore-out","z":"b5ce73e91740e4b2","g":"57c1f30f8a825e5c","name":"Overwrite Timestamp","database":"e8796a1869e179bc","collection":"demo","collectionType":"str","document":"timestampOverwritten","documentType":"str","queryMethod":"set","queryOptions":{"merge":false},"x":540,"y":360,"wires":[]},{"id":"f69b62a66076edf1","type":"firestore-in","z":"b5ce73e91740e4b2","g":"57c1f30f8a825e5c","name":"Timestamp Changes","database":"e8796a1869e179bc","collection":"","collectionType":"str","collectionGroup":"","collectionGroupType":"str","constraints":{},"document":"demo/timestampOverwritten","documentType":"str","filter":"none","inputs":0,"passThrough":false,"x":770,"y":360,"wires":[["15d852f4a29abec1"]]},{"id":"e8796a1869e179bc","type":"firebase-config","name":"My Database","authType":"email","claims":{},"createUser":false,"status":{"firestore":false,"storage":false},"useClaims":false},{"id":"8f193a9c1fc939fb","type":"group","z":"b5ce73e91740e4b2","name":"","style":{"stroke":"#c8e7a7","label":true},"nodes":["7376db537268899b","bd18e498f7c61507","19355c55dc280ad7","0ef7c0721cf81927","29aaf3383098e09e","735b562a594841f3","6a90881898ed3551","ee3a2b0bc367a47e","ca1a112e5c6cbdb2","9acbf29beeba99c3","ce937eb6b8c8ca65","16d258ae4b97ca34","78d3b0d5f0f884f4","cf5f66733f714098","fe80dd5b71c8eebe","a8a4da4c647877d1","4d5563941ff8ff6e","108e033753b35f5a","6e2869b197f278f5","d8baeef2707a77b5","1b5c69ac26a6eed7","c87c1434f562e22c","770a532dd82c2c5d","1addc1cfbb75e991"],"x":114,"y":439,"w":972,"h":582},{"id":"7376db537268899b","type":"inject","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Add Alan","props":[{"p":"payload"},{"p":"user","v":"alanisawesome","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"date_of_birth\":\"June 23, 1912\",\"full_name\":\"Alan Turing\",\"nickname\":\"Alan The Machine\"}","payloadType":"json","x":220,"y":520,"wires":[["a8a4da4c647877d1"]]},{"id":"bd18e498f7c61507","type":"inject","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Add Steve","props":[{"p":"payload"},{"p":"user","v":"steveisapple","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"full_name\":\"Steve Jobs\",\"nickname\":\"Steve The King\",\"hobby\":\"Computer\"}","payloadType":"json","x":220,"y":580,"wires":[["a8a4da4c647877d1"]]},{"id":"19355c55dc280ad7","type":"inject","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Modify Alan Nickname","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"nickname\":\"Alan is Genius\"}","payloadType":"json","x":260,"y":700,"wires":[["108e033753b35f5a"]]},{"id":"0ef7c0721cf81927","type":"inject","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Remove Steve","props":[{"p":"user","v":"steveisapple","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":240,"y":860,"wires":[["d8baeef2707a77b5"]]},{"id":"29aaf3383098e09e","type":"debug","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.changes","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":540,"wires":[]},{"id":"735b562a594841f3","type":"debug","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.changes","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":700,"wires":[]},{"id":"6a90881898ed3551","type":"debug","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.changes","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":860,"wires":[]},{"id":"ee3a2b0bc367a47e","type":"debug","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":980,"y":980,"wires":[]},{"id":"ca1a112e5c6cbdb2","type":"inject","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Get All Users","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":230,"y":980,"wires":[["770a532dd82c2c5d"]]},{"id":"9acbf29beeba99c3","type":"debug","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"debug 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.docs","targetType":"msg","statusVal":"","statusType":"auto","x":560,"y":980,"wires":[]},{"id":"ce937eb6b8c8ca65","type":"comment","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Add Alan to \"users\"","info":"","x":250,"y":480,"wires":[]},{"id":"16d258ae4b97ca34","type":"comment","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Modify the Alan's Nickname","info":"","x":280,"y":660,"wires":[]},{"id":"78d3b0d5f0f884f4","type":"comment","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Remove Steve from \"users\"","info":"","x":280,"y":820,"wires":[]},{"id":"cf5f66733f714098","type":"comment","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Get All Users from \"users\"","info":"","x":270,"y":940,"wires":[]},{"id":"fe80dd5b71c8eebe","type":"comment","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Print All Users Changes","info":"","x":780,"y":940,"wires":[]},{"id":"a8a4da4c647877d1","type":"firestore-out","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Add User","database":"e8796a1869e179bc","collection":"users","collectionType":"str","document":"user","documentType":"msg","queryMethod":"set","queryOptions":{"merge":false},"x":400,"y":540,"wires":[]},{"id":"4d5563941ff8ff6e","type":"firestore-in","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"User added","database":"e8796a1869e179bc","collection":"users","collectionType":"str","collectionGroup":"","collectionGroupType":"str","constraints":{},"document":"","documentType":"str","filter":"added","inputs":0,"passThrough":false,"x":750,"y":540,"wires":[["29aaf3383098e09e"]]},{"id":"108e033753b35f5a","type":"firestore-out","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Update User Nickname","database":"e8796a1869e179bc","collection":"users","collectionType":"str","document":"alanisawesome","documentType":"str","queryMethod":"update","queryOptions":{"merge":true},"x":530,"y":700,"wires":[]},{"id":"6e2869b197f278f5","type":"firestore-in","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"User Modified","database":"e8796a1869e179bc","collection":"users","collectionType":"str","collectionGroup":"","collectionGroupType":"str","constraints":{},"document":"","documentType":"str","filter":"modified","inputs":0,"passThrough":false,"x":750,"y":700,"wires":[["735b562a594841f3"]]},{"id":"d8baeef2707a77b5","type":"firestore-out","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Remove User","database":"e8796a1869e179bc","collection":"users","collectionType":"str","document":"user","documentType":"msg","queryMethod":"delete","queryOptions":{"merge":false},"x":440,"y":860,"wires":[]},{"id":"1b5c69ac26a6eed7","type":"firestore-in","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"User Removed","database":"e8796a1869e179bc","collection":"users","collectionType":"str","collectionGroup":"","collectionGroupType":"str","constraints":{},"document":"","documentType":"str","filter":"removed","inputs":0,"passThrough":false,"x":760,"y":860,"wires":[["6a90881898ed3551"]]},{"id":"c87c1434f562e22c","type":"firestore-in","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"All Users Changes","database":"e8796a1869e179bc","collection":"users","collectionType":"str","collectionGroup":"","collectionGroupType":"str","constraints":{},"document":"","documentType":"str","filter":"none","inputs":0,"passThrough":false,"x":770,"y":980,"wires":[["ee3a2b0bc367a47e"]]},{"id":"770a532dd82c2c5d","type":"firestore-get","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Get Users","database":"e8796a1869e179bc","collection":"users","collectionType":"str","collectionGroup":"","collectionGroupType":"str","constraints":{},"document":"","documentType":"str","passThrough":false,"x":400,"y":980,"wires":[["9acbf29beeba99c3"]]},{"id":"1addc1cfbb75e991","type":"inject","z":"b5ce73e91740e4b2","g":"8f193a9c1fc939fb","name":"Remove Alan Nickname","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"nickname\":\"DELETE\"}","payloadType":"json","x":260,"y":740,"wires":[["108e033753b35f5a"]]},{"id":"3b1dbdd5845f591a","type":"global-config","env":[],"modules":{"@gogovega/node-red-contrib-cloud-firestore":"0.2.0"}}]
-{% endrenderFlow %}
+```
+::
-
### Try FlowFuse's Built-In Database Service
-
[FlowFuse now includes a fully integrated database service that makes connecting and querying your data effortless](/blog/2025/08/getting-started-with-flowfuse-tables/). With the FlowFuse Query Node, you do not need to configure the connection manually, the node sets itself up automatically.
-
Even better, the [FlowFuse Expert allows you to query your tables using natural language](/blog/2025/09/ai-assistant-flowfuse-tables/). Simply type your request, and it will generate the correct SQL for you based on your table.
-
Deploy, manage, scale, and secure your Node-RED applications with FlowFuse, and take full control of your industrial workflows and data.
-
[**Start with FlowFuse today**](https://app.flowfuse.com/)
-
-
\ No newline at end of file
+
diff --git a/src/node-red/database/images/config-connection.png b/nuxt/content-guides/node-red/database/images/config-connection.png
similarity index 100%
rename from src/node-red/database/images/config-connection.png
rename to nuxt/content-guides/node-red/database/images/config-connection.png
diff --git a/src/node-red/database/images/config-database.png b/nuxt/content-guides/node-red/database/images/config-database.png
similarity index 100%
rename from src/node-red/database/images/config-database.png
rename to nuxt/content-guides/node-red/database/images/config-database.png
diff --git a/src/node-red/database/images/data_in_influx.webm b/nuxt/content-guides/node-red/database/images/data_in_influx.webm
similarity index 100%
rename from src/node-red/database/images/data_in_influx.webm
rename to nuxt/content-guides/node-red/database/images/data_in_influx.webm
diff --git a/src/node-red/database/images/dynamodb-data-structure-node-red-flowfuse.png b/nuxt/content-guides/node-red/database/images/dynamodb-data-structure-node-red-flowfuse.png
similarity index 100%
rename from src/node-red/database/images/dynamodb-data-structure-node-red-flowfuse.png
rename to nuxt/content-guides/node-red/database/images/dynamodb-data-structure-node-red-flowfuse.png
diff --git a/src/node-red/database/images/dynamodb-flowfuse-iam-node-red.png b/nuxt/content-guides/node-red/database/images/dynamodb-flowfuse-iam-node-red.png
similarity index 100%
rename from src/node-red/database/images/dynamodb-flowfuse-iam-node-red.png
rename to nuxt/content-guides/node-red/database/images/dynamodb-flowfuse-iam-node-red.png
diff --git a/src/node-red/database/images/flowfuse-dynamodb-aws-setup-node-red.png b/nuxt/content-guides/node-red/database/images/flowfuse-dynamodb-aws-setup-node-red.png
similarity index 100%
rename from src/node-red/database/images/flowfuse-dynamodb-aws-setup-node-red.png
rename to nuxt/content-guides/node-red/database/images/flowfuse-dynamodb-aws-setup-node-red.png
diff --git a/src/node-red/database/images/influxdb-node-red-cta-1.png b/nuxt/content-guides/node-red/database/images/influxdb-node-red-cta-1.png
similarity index 100%
rename from src/node-red/database/images/influxdb-node-red-cta-1.png
rename to nuxt/content-guides/node-red/database/images/influxdb-node-red-cta-1.png
diff --git a/src/node-red/database/images/mysql-node-config.png b/nuxt/content-guides/node-red/database/images/mysql-node-config.png
similarity index 100%
rename from src/node-red/database/images/mysql-node-config.png
rename to nuxt/content-guides/node-red/database/images/mysql-node-config.png
diff --git a/src/node-red/database/images/mysql-with-node-red.png b/nuxt/content-guides/node-red/database/images/mysql-with-node-red.png
similarity index 100%
rename from src/node-red/database/images/mysql-with-node-red.png
rename to nuxt/content-guides/node-red/database/images/mysql-with-node-red.png
diff --git a/src/node-red/database/images/node-red-dynamodb-flow-flowfuse.png b/nuxt/content-guides/node-red/database/images/node-red-dynamodb-flow-flowfuse.png
similarity index 100%
rename from src/node-red/database/images/node-red-dynamodb-flow-flowfuse.png
rename to nuxt/content-guides/node-red/database/images/node-red-dynamodb-flow-flowfuse.png
diff --git a/src/node-red/database/images/node-red-dynamodb-get-item-flowfuse.png b/nuxt/content-guides/node-red/database/images/node-red-dynamodb-get-item-flowfuse.png
similarity index 100%
rename from src/node-red/database/images/node-red-dynamodb-get-item-flowfuse.png
rename to nuxt/content-guides/node-red/database/images/node-red-dynamodb-get-item-flowfuse.png
diff --git a/src/node-red/database/images/node-red-dynamodb-query-flowfuse.png b/nuxt/content-guides/node-red/database/images/node-red-dynamodb-query-flowfuse.png
similarity index 100%
rename from src/node-red/database/images/node-red-dynamodb-query-flowfuse.png
rename to nuxt/content-guides/node-red/database/images/node-red-dynamodb-query-flowfuse.png
diff --git a/src/node-red/database/images/node-red-dynamodb-scan-flowfuse.png b/nuxt/content-guides/node-red/database/images/node-red-dynamodb-scan-flowfuse.png
similarity index 100%
rename from src/node-red/database/images/node-red-dynamodb-scan-flowfuse.png
rename to nuxt/content-guides/node-red/database/images/node-red-dynamodb-scan-flowfuse.png
diff --git a/src/node-red/database/images/node-red-dynamodb.png b/nuxt/content-guides/node-red/database/images/node-red-dynamodb.png
similarity index 100%
rename from src/node-red/database/images/node-red-dynamodb.png
rename to nuxt/content-guides/node-red/database/images/node-red-dynamodb.png
diff --git a/src/node-red/database/images/node-red-influxdb.png b/nuxt/content-guides/node-red/database/images/node-red-influxdb.png
similarity index 100%
rename from src/node-red/database/images/node-red-influxdb.png
rename to nuxt/content-guides/node-red/database/images/node-red-influxdb.png
diff --git a/src/node-red/database/images/postgresql_with_node-red.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_Inventory_management_system.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_Inventory_management_system.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_Inventory_management_system.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_Inventory_management_system.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_delete_data.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_delete_data.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_delete_data.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_delete_data.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_drop_tables.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_drop_tables.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_drop_tables.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_drop_tables.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_form1.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_form1.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_form1.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_form1.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_form2.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_form2.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_form2.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_form2.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_form3.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_form3.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_form3.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_form3.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_insert_data.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_insert_data.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_insert_data.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_insert_data.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_pgconfig1.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_pgconfig1.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_pgconfig1.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_pgconfig1.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_pgconfig2.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_pgconfig2.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_pgconfig2.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_pgconfig2.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_retrive_data.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_retrive_data.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_retrive_data.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_retrive_data.png
diff --git a/src/node-red/database/images/postgresql_with_node-red_update_data.png b/nuxt/content-guides/node-red/database/images/postgresql_with_node-red_update_data.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_node-red_update_data.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_node-red_update_data.png
diff --git a/src/node-red/database/images/postgresql_with_nodered_create_table.png b/nuxt/content-guides/node-red/database/images/postgresql_with_nodered_create_table.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_nodered_create_table.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_nodered_create_table.png
diff --git a/src/node-red/database/images/postgresql_with_nodred_environment_variable.png b/nuxt/content-guides/node-red/database/images/postgresql_with_nodred_environment_variable.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_nodred_environment_variable.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_nodred_environment_variable.png
diff --git a/src/node-red/database/images/postgresql_with_nodred_environment_variable_ff_editor.png b/nuxt/content-guides/node-red/database/images/postgresql_with_nodred_environment_variable_ff_editor.png
similarity index 100%
rename from src/node-red/database/images/postgresql_with_nodred_environment_variable_ff_editor.png
rename to nuxt/content-guides/node-red/database/images/postgresql_with_nodred_environment_variable_ff_editor.png
diff --git a/src/node-red/database/images/timescaledb-with-node-red-hypertables.png b/nuxt/content-guides/node-red/database/images/timescaledb-with-node-red-hypertables.png
similarity index 100%
rename from src/node-red/database/images/timescaledb-with-node-red-hypertables.png
rename to nuxt/content-guides/node-red/database/images/timescaledb-with-node-red-hypertables.png
diff --git a/src/node-red/database/images/timescaledb-with-node-red.png b/nuxt/content-guides/node-red/database/images/timescaledb-with-node-red.png
similarity index 100%
rename from src/node-red/database/images/timescaledb-with-node-red.png
rename to nuxt/content-guides/node-red/database/images/timescaledb-with-node-red.png
diff --git a/src/node-red/database/images/using-Mongo-with-nr.png b/nuxt/content-guides/node-red/database/images/using-Mongo-with-nr.png
similarity index 100%
rename from src/node-red/database/images/using-Mongo-with-nr.png
rename to nuxt/content-guides/node-red/database/images/using-Mongo-with-nr.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-annotomy-of-mongodb-document.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-annotomy-of-mongodb-document.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-annotomy-of-mongodb-document.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-annotomy-of-mongodb-document.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-change-node-to-delete-data.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-change-node-to-delete-data.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-change-node-to-delete-data.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-change-node-to-delete-data.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-change-node-to-insert-data.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-change-node-to-insert-data.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-change-node-to-insert-data.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-change-node-to-insert-data.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-change-node-to-update-data.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-change-node-to-update-data.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-change-node-to-update-data.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-change-node-to-update-data.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-dashboard-view.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-dashboard-view.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-dashboard-view.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-dashboard-view.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-flow.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-flow.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-flow.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-crm-system-node-red-flow.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-delete-data-form.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-delete-data-form.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-delete-data-form.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-delete-data-form.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-flowfuse-instance-setting.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-flowfuse-instance-setting.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-flowfuse-instance-setting.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-flowfuse-instance-setting.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-friend-id-node.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-friend-id-node.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-friend-id-node.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-friend-id-node.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-insert-data-form.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-insert-data-form.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-insert-data-form.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-insert-data-form.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-flow.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-flow.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-flow.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-flow.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-node-configuration.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-node-configuration.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-node-configuration.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-deleteone-node-configuration.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-flow.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-flow.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-flow.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-flow.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-node-configuration.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-node-configuration.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-node-configuration.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-drop-node-configuration.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-find-flow.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-find-flow.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-find-flow.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-find-flow.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-find-node-configuration.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-find-node-configuration.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-find-node-configuration.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-find-node-configuration.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-flow.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-flow.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-flow.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-flow.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-node-configuration.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-node-configuration.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-node-configuration.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-insertone-node-configuration.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-node-connection-configuration.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-node-connection-configuration.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-node-connection-configuration.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-node-connection-configuration.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-update-node-configuration.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-update-node-configuration.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-update-node-configuration.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-update-node-configuration.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-mongodb-updateone-flow.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-updateone-flow.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-mongodb-updateone-flow.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-mongodb-updateone-flow.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-table-widget.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-table-widget.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-table-widget.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-table-widget.png
diff --git a/src/node-red/database/images/using-mongodb-with-node-red-update-data-form.png b/nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-update-data-form.png
similarity index 100%
rename from src/node-red/database/images/using-mongodb-with-node-red-update-data-form.png
rename to nuxt/content-guides/node-red/database/images/using-mongodb-with-node-red-update-data-form.png
diff --git a/src/node-red/database/images/using-timescaledb-with-node-red-environment-variables.png b/nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-environment-variables.png
similarity index 100%
rename from src/node-red/database/images/using-timescaledb-with-node-red-environment-variables.png
rename to nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-environment-variables.png
diff --git a/src/node-red/database/images/using-timescaledb-with-node-red-inject-node-1.png b/nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-inject-node-1.png
similarity index 100%
rename from src/node-red/database/images/using-timescaledb-with-node-red-inject-node-1.png
rename to nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-inject-node-1.png
diff --git a/src/node-red/database/images/using-timescaledb-with-node-red-inject-node-2.png b/nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-inject-node-2.png
similarity index 100%
rename from src/node-red/database/images/using-timescaledb-with-node-red-inject-node-2.png
rename to nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-inject-node-2.png
diff --git a/src/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-connection-tab.png b/nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-connection-tab.png
similarity index 100%
rename from src/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-connection-tab.png
rename to nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-connection-tab.png
diff --git a/src/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-security-tab.png b/nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-security-tab.png
similarity index 100%
rename from src/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-security-tab.png
rename to nuxt/content-guides/node-red/database/images/using-timescaledb-with-node-red-postgresql-config-node-security-tab.png
diff --git a/src/node-red/database/influxdb.md b/nuxt/content-guides/node-red/database/influxdb.md
similarity index 92%
rename from src/node-red/database/influxdb.md
rename to nuxt/content-guides/node-red/database/influxdb.md
index cb4fe02795..4931a4f8bb 100644
--- a/src/node-red/database/influxdb.md
+++ b/nuxt/content-guides/node-red/database/influxdb.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: InfluxDB
- parent: Database
+title: Using InfluxDB with Node-RED (2026 Updated)
+navTitle: InfluxDB
+navOrder: 50
meta:
- title: Using InfluxDB with Node-RED (2026 Updated)
description: Node-RED has great support for InfluxDB. In this guide, we'll explain how to get your data flowing into one of the most popular time-series databases.
keywords: node-red, databases, integration, influxdb, time-series database
-image: /node-red/core-nodes/images/node-red-influxdb.png
+image: /docs/node-red/core-nodes/images/node-red-influxdb.png
---
-# {{ meta.title }}
+# Using InfluxDB with Node-RED (2026 Updated)
InfluxDB is a time series database that is commonly used for storing and analysing IoT data. Node-RED is a visual programming tool that makes it easy to connect different data sources and create flows that automate tasks.
@@ -18,7 +17,7 @@ In this documentation, we will show you how to write data to InfluxDB from a Nod
-
+
## Step 1: Install the InfluxDB Node-RED package
@@ -53,9 +52,11 @@ The fields are the individual pieces of data that you are writing. The tags are
You can import the flow below into Node-RED to see an example of a payload which will write all the required values to create a data point in InfluxDB:
-{% renderFlow %}
+::render-flow
+```json
[{"id":"cb3b0ecc762dbf93","type":"inject","z":"4542482476b9c71d","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"time\":1688718546,\"temperature\":24},{\"device\":\"dQBgXeWLRE\",\"deviceType\":\"Pi4\",\"deviceName\":\"demo-pi-rob\"}]","payloadType":"json","x":450,"y":420,"wires":[["87166c0dafdeea33"]]},{"id":"87166c0dafdeea33","type":"debug","z":"4542482476b9c71d","name":"debug 31","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":600,"y":420,"wires":[]}]
-{% endrenderFlow %}
+```
+::
In this example, the time & temperature fields are hard coded, you will need to overwrite the values stored in ```payload[0].time``` & ```payload[0].temperature``` with real data if you were to connect this flow to a real IOT thermometer.
@@ -83,9 +84,11 @@ This is an example valid payload:
```
You can import a demo, including the demo payload flow using the code below:
-{% renderFlow %}
+::render-flow
+```json
[{"id":"ecbb02face30cbcd","type":"influxdb out","z":"4542482476b9c71d","influxdb":"1c1a5edef41716e3","name":"InfluxDB","measurement":"temperature","precision":"","retentionPolicy":"","database":"database","precisionV18FluxV20":"s","retentionPolicyV18Flux":"","org":"organization","bucket":"my_data","x":360,"y":220,"wires":[]},{"id":"de83c2b49ba249fd","type":"inject","z":"4542482476b9c71d","name":"","props":[{"p":"measurement","v":"temperature","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"time\":1688987984,\"temperature\":24},{\"device\":\"dQBgXeWLRE\",\"deviceType\":\"Pi4\",\"deviceName\":\"demo-pi-rob\"}]","payloadType":"json","x":190,"y":160,"wires":[["aad6353f2f00333e","ecbb02face30cbcd"]]},{"id":"aad6353f2f00333e","type":"debug","z":"4542482476b9c71d","name":"debug 31","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":360,"y":160,"wires":[]},{"id":"1c1a5edef41716e3","type":"influxdb","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"my_data","name":"","usetls":false,"tls":"","influxdbVersion":"2.0","url":"https://localhost","rejectUnauthorized":true}]
-{% endrenderFlow %}
+```
+::
Bear in mind that you will need to edit the server and database details in your influxdb node for this demo to work.
@@ -93,7 +96,7 @@ Bear in mind that you will need to edit the server and database details in your
You should now be ready to test your flow is writing data to InfluxDB correctly. There is no output in Node-RED to confirm you data was written, so you will need to check directly on InfluxDB.
-
+
Great, our data has arrived correctly and is ready to be used.
@@ -103,4 +106,4 @@ Great, our data has arrived correctly and is ready to be used.
1. Set the correct measurement name. The measurement name is the name of the table in InfluxDB where the data will be stored. It is important to choose a meaningful measurement name that will help you to easily identify the data later.
1. Set the correct tags and fields. Tags are used to identify the data points, while fields are used to store the actual data values. It is important to set the correct tags and fields for your data so that you can easily query and analyse it later.
1. Set the correct timestamp. The timestamp is the time at which the data point was recorded. It is important to set the correct timestamp so that you can track the evolution of your data over time.
-1. Use the correct precision. The precision is the number of decimal places that are stored for each data value. It is important to use the correct precision so that your data is easy to use.
\ No newline at end of file
+1. Use the correct precision. The precision is the number of decimal places that are stored for each data value. It is important to use the correct precision so that your data is easy to use.
diff --git a/src/node-red/database/mongodb.md b/nuxt/content-guides/node-red/database/mongodb.md
similarity index 98%
rename from src/node-red/database/mongodb.md
rename to nuxt/content-guides/node-red/database/mongodb.md
index 158fbf5c89..d556c90640 100644
--- a/src/node-red/database/mongodb.md
+++ b/nuxt/content-guides/node-red/database/mongodb.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: MongoDB
- parent: Database
+title: Using MongoDB With Node-RED (2026 Updated)
+navTitle: MongoDB
+navOrder: 50
meta:
- title: Using MongoDB With Node-RED (2026 Updated)
description: Learn how to seamlessly integrate MongoDB, a NoSQL database, into your Node-RED applications with this step-by-step documentation.
keywords: node-red, databases, integration, mongodb, no-sql database
-image: /node-red/core-nodes/images/using-Mongo-with-nr.png
+image: /docs/node-red/core-nodes/images/using-Mongo-with-nr.png
---
-# {{ meta.title }}
+# Using MongoDB With Node-RED (2026 Updated)
This guide provides implementation procedures for integrating MongoDB with Node-RED. It covers configuration requirements, operational patterns, and a complete implementation example using a customer relationship management system.
@@ -290,4 +289,4 @@ Connect a debug node to the output of any MongoDB4 node to monitor operation res
3. Use inject nodes for retrieval and drop operations
4. Use the forms for create, update, and delete operations
-{data-zoomable}
\ No newline at end of file
+{data-zoomable}
diff --git a/src/node-red/database/mysql.md b/nuxt/content-guides/node-red/database/mysql.md
similarity index 95%
rename from src/node-red/database/mysql.md
rename to nuxt/content-guides/node-red/database/mysql.md
index 569ca8cda1..30f30658bf 100644
--- a/src/node-red/database/mysql.md
+++ b/nuxt/content-guides/node-red/database/mysql.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: MySQL
- parent: Database
+title: Using MySQL with Node-RED (2026 Updated)
+navTitle: MySQL
+navOrder: 50
meta:
- title: Using MySQL with Node-RED (2026 Updated)
description: Learn how to seamlessly integrate MySQL with Node-RED for efficient data management and application development.
keywords: nodered mysql, node red mysql, node-red-node-mysql, mysql nodered, mysql node red
-image: /node-red/database/images/timescaledb-with-node-red.png
+image: /docs/node-red/database/images/timescaledb-with-node-red.png
---
-# {{ meta.title }}
+# Using MySQL with Node-RED (2026 Updated)
When discussing popular and widely used databases, MySQL inevitably stands out. This is especially evident within the Node-RED community, where the MySQL contrib node has the highest number of downloads among all database contrib nodes. However, popularity often brings its own set of challenges. We've prepared this comprehensive guide to help our Node-RED community members navigate these challenges. It covers all aspects of using MySQL with Node-RED, including an overview of what MySQL is, the differences between PostgreSQL and MySQL, when to choose one over the other, essential MySQL operations, and more.
@@ -31,7 +30,7 @@ When discussing popular and widely used databases, MySQL inevitably stands out.
## Choosing Between PostgreSQL and MySQL
-Deciding between [PostgreSQL](/node-red/database/postgresql/) and MySQL depends on your project's needs and what each database system does best. PostgreSQL is ideal for big projects requiring complex data handling, reliability, and often updated data. It works well in environments where keeping data consistent is crucial. PostgreSQL's advanced features, like materialized views and support for writing procedures in different languages beyond SQL make it great for managing sophisticated data needs.
+Deciding between [PostgreSQL](/docs/node-red/database/postgresql/) and MySQL depends on your project's needs and what each database system does best. PostgreSQL is ideal for big projects requiring complex data handling, reliability, and often updated data. It works well in environments where keeping data consistent is crucial. PostgreSQL's advanced features, like materialized views and support for writing procedures in different languages beyond SQL make it great for managing sophisticated data needs.
On the other hand, MySQL is excellent for projects that prioritize fast data reading and are easy to set up and use. It's commonly used for smaller projects, quick prototypes, or applications where quick deployment is critical. MySQL offers flexibility with different storage options, like InnoDB for transactions and MyISAM for handling lots of reads simultaneously, making it versatile depending on your workload.
@@ -185,9 +184,11 @@ DROP TABLE IF EXISTS weather_data;
Below is the complete flow covering all the operations discussed throughout this blog.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"01aeec8769243693","type":"mysql","z":"a9e5683585deb91e","mydb":"bceed1e54606b872","name":"MySQL","x":980,"y":1660,"wires":[["e9ae7f0a3fdbfbc5"]]},{"id":"e9ae7f0a3fdbfbc5","type":"debug","z":"a9e5683585deb91e","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1160,"y":1660,"wires":[]},{"id":"faf229c38d675b2b","type":"inject","z":"a9e5683585deb91e","name":"Create the table","props":[],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","x":600,"y":1660,"wires":[["b407dbb1901749bf"]]},{"id":"b407dbb1901749bf","type":"template","z":"a9e5683585deb91e","name":"","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"CREATE TABLE IF NOT EXISTS weather_data (\n id INT AUTO_INCREMENT PRIMARY KEY,\n location VARCHAR(100) NOT NULL,\n date DATETIME NOT NULL,\n tem DECIMAL(5, 2) NOT NULL\n);\n","output":"str","x":820,"y":1660,"wires":[["01aeec8769243693"]]},{"id":"cfc26503f0812a88","type":"mysql","z":"a9e5683585deb91e","mydb":"bceed1e54606b872","name":"MySQL","x":980,"y":1760,"wires":[["236d40b1edb920d2"]]},{"id":"236d40b1edb920d2","type":"debug","z":"a9e5683585deb91e","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1160,"y":1760,"wires":[]},{"id":"25790e7a3aef22bf","type":"inject","z":"a9e5683585deb91e","name":"Insert the data into the table","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"$random() * 100","payloadType":"jsonata","x":560,"y":1760,"wires":[["7a798b3539da9547"]]},{"id":"7a798b3539da9547","type":"template","z":"a9e5683585deb91e","name":"","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"INSERT INTO weather_data (location, date, tem)\nVALUES ('New York', CURTIME(), {{ payload }});","output":"str","x":820,"y":1760,"wires":[["cfc26503f0812a88"]]},{"id":"0bbb4d17defe3067","type":"mysql","z":"a9e5683585deb91e","mydb":"bceed1e54606b872","name":"MySQL","x":980,"y":1860,"wires":[["ef2de8cb0673de47"]]},{"id":"ef2de8cb0673de47","type":"debug","z":"a9e5683585deb91e","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1180,"y":1860,"wires":[]},{"id":"027c2c3801bf71a3","type":"inject","z":"a9e5683585deb91e","name":"Retrieve data from table","props":[],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","x":580,"y":1860,"wires":[["079031046156b6a0"]]},{"id":"079031046156b6a0","type":"template","z":"a9e5683585deb91e","name":"","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"SELECT * FROM weather_data;\n","output":"str","x":820,"y":1860,"wires":[["0bbb4d17defe3067"]]},{"id":"eb38532de73213cf","type":"mysql","z":"a9e5683585deb91e","mydb":"bceed1e54606b872","name":"MySQL","x":980,"y":2080,"wires":[["281838d687cea172"]]},{"id":"281838d687cea172","type":"debug","z":"a9e5683585deb91e","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1180,"y":2080,"wires":[]},{"id":"d4dfbbac635e8a3f","type":"inject","z":"a9e5683585deb91e","name":"Drop the table if it exist","props":[],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","x":580,"y":2080,"wires":[["4442ad0bbc50a256"]]},{"id":"4442ad0bbc50a256","type":"template","z":"a9e5683585deb91e","name":"","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"DROP TABLE IF EXISTS weather_data;\n","output":"str","x":820,"y":2080,"wires":[["eb38532de73213cf"]]},{"id":"668ed2021704c543","type":"mysql","z":"a9e5683585deb91e","mydb":"bceed1e54606b872","name":"MySQL","x":980,"y":1940,"wires":[["666152e3362a25d5"]]},{"id":"666152e3362a25d5","type":"debug","z":"a9e5683585deb91e","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1180,"y":1940,"wires":[]},{"id":"fbcbad8039d05e8e","type":"inject","z":"a9e5683585deb91e","name":"Delete data from the table","props":[],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","x":570,"y":1940,"wires":[["aa929706ed813892"]]},{"id":"aa929706ed813892","type":"template","z":"a9e5683585deb91e","name":"","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"DELETE FROM weather_data\nWHERE tem > 15\nAND location = 'New York';","output":"str","x":820,"y":1940,"wires":[["668ed2021704c543"]]},{"id":"9c2befca464db660","type":"mysql","z":"a9e5683585deb91e","mydb":"bceed1e54606b872","name":"MySQL","x":980,"y":2020,"wires":[["d28cb3472c5fc357"]]},{"id":"d28cb3472c5fc357","type":"debug","z":"a9e5683585deb91e","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1180,"y":2020,"wires":[]},{"id":"49297922b9784f8e","type":"inject","z":"a9e5683585deb91e","name":"Update data from the table","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"10","payloadType":"num","x":570,"y":2020,"wires":[["bbae98e54824b222"]]},{"id":"bbae98e54824b222","type":"template","z":"a9e5683585deb91e","name":"","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"UPDATE weather_data\nSET tem = {{payload}}\nWHERE id = 3","output":"str","x":820,"y":2020,"wires":[["9c2befca464db660"]]},{"id":"bceed1e54606b872","type":"MySQLdatabase","name":"Mysql config","host":"${HOST}","port":"${PORT}","db":"${DATABASE}","tz":"+5:30","charset":"UTF8"}]
-{% endrenderFlow %}
+```
+::
### Deploying the Flow
@@ -195,4 +196,4 @@ Below is the complete flow covering all the operations discussed throughout this
After deploying the flow, you can test each operation, such as creating, deleting, updating, and executing other queries, by clicking the inject button. For debugging purposes, add debug nodes to the flow.
-Additionally, if you want to explore the integration of other databases with Node-RED, you can refer to our [database section](/node-red/database/) in the Node-RED learning resources, where we cover databases such as PostgreSQL, MongoDB, InfluxDB, DynamoDB, and more.
\ No newline at end of file
+Additionally, if you want to explore the integration of other databases with Node-RED, you can refer to our [database section](/docs/node-red/database/) in the Node-RED learning resources, where we cover databases such as PostgreSQL, MongoDB, InfluxDB, DynamoDB, and more.
diff --git a/src/node-red/database/postgresql.md b/nuxt/content-guides/node-red/database/postgresql.md
similarity index 98%
rename from src/node-red/database/postgresql.md
rename to nuxt/content-guides/node-red/database/postgresql.md
index b618445b2e..ff65ac89ab 100644
--- a/src/node-red/database/postgresql.md
+++ b/nuxt/content-guides/node-red/database/postgresql.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: PostgreSQL
- parent: Database
+title: Using PostgreSQL with Node-RED (2026 Updated)
+navTitle: PostgreSQL
+navOrder: 50
meta:
- title: Using PostgreSQL with Node-RED (2026 Updated)
description: Learn how to seamlessly integrate PostgreSQL with Node-RED for efficient data management and application development.
keywords: node-red, databases, integration, PostgreSQL, SQL database
-image: /node-red/core-nodes/images/postgresql_with_node-red.png
+image: /docs/node-red/core-nodes/images/postgresql_with_node-red.png
---
-# {{ meta.title }}
+# Using PostgreSQL with Node-RED (2026 Updated)
PostgreSQL is a highly reliable open-source relational database known for its extensive features. It supports diverse data types, robust SQL, and ACID compliance, allowing high-performance systems. Over the years, it has demonstrated reliability, security, and compatibility, which makes it a popular choice for businesses worldwide.
diff --git a/src/node-red/database/redis.md b/nuxt/content-guides/node-red/database/redis.md
similarity index 98%
rename from src/node-red/database/redis.md
rename to nuxt/content-guides/node-red/database/redis.md
index 3b27c16948..e606df113c 100644
--- a/src/node-red/database/redis.md
+++ b/nuxt/content-guides/node-red/database/redis.md
@@ -1,22 +1,21 @@
---
-eleventyNavigation:
- key: Redis
- parent: Database
+title: Using Redis with Node-RED (2026 Updated)
+navTitle: Redis
+navOrder: 50
meta:
- title: Using Redis with Node-RED (2026 Updated)
description: Learn how to integrate Redis with Node-RED for fast data storage, pub/sub messaging, JSON handling, Lua scripting, and advanced Redis operations in Node-RED flows.
keywords: nodered redis, node red redis, node-red-node-redis, redis nodered, redis node red
-certified:
- - "@flowfuse-certified-nodes/redis"
---
-# {{ meta.title }}
+# Using Redis with Node-RED (2026 Updated)
Redis is a powerful in-memory data structure store that can be used as a database, cache, message broker, and streaming engine. When combined with Node-RED, Redis provides a fast and efficient way to store and retrieve data, manage session states, implement pub/sub messaging patterns, and share data across multiple Node-RED instances.
This documentation will walk you through integrating Redis with Node-RED, from basic setup to advanced use cases.
-{% include "components/certified-node-callout.liquid" %}
+::callout{icon="i-lucide-badge-check"}
+**Certified nodes for this technology.** FlowFuse maintains certified nodes for production use covering this technology. [redis](/integrations/?certified=1)
+::
## Getting Started
@@ -366,6 +365,8 @@ For more Redis commands, patterns, and advanced capabilities, refer to the [offi
Below is the complete example that we covered in this document.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"2c33ebeb73062ab3","type":"group","z":"d4f60c79eff5211d","name":"Your First Redis Flow","style":{"label":true},"nodes":["e5f3326ef736cef6","8486e88d8623b42a","d1ebde22d0e224a8","22db25c60af16ab2","fe2be532c53402b3","ebc1e8fc0f1ba2d6","adb95c63d206c633"],"x":514,"y":359,"w":712,"h":162},{"id":"e5f3326ef736cef6","type":"redis-command","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","server":"e370dc92b39a7ba4","command":"SET","name":"","topic":"mykey","params":"[\"Hello from Node-RED\"]","paramsType":"json","payloadType":"json","block":false,"x":810,"y":400,"wires":[["8486e88d8623b42a"]]},{"id":"8486e88d8623b42a","type":"debug","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":970,"y":400,"wires":[]},{"id":"d1ebde22d0e224a8","type":"redis-command","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","server":"e370dc92b39a7ba4","command":"GET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":1000,"y":480,"wires":[["22db25c60af16ab2"]]},{"id":"22db25c60af16ab2","type":"debug","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1130,"y":480,"wires":[]},{"id":"fe2be532c53402b3","type":"change","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","name":"Set Key for GET","rules":[{"t":"set","p":"topic","pt":"msg","to":"mykey","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"[]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":480,"wires":[["d1ebde22d0e224a8"]]},{"id":"ebc1e8fc0f1ba2d6","type":"inject","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":400,"wires":[["e5f3326ef736cef6"]]},{"id":"adb95c63d206c633","type":"inject","z":"d4f60c79eff5211d","g":"2c33ebeb73062ab3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":480,"wires":[["fe2be532c53402b3"]]},{"id":"e370dc92b39a7ba4","type":"redis-config","name":"Local","options":"{\"host\":\"localhost\",\"port\":6379,\"db\":0}","cluster":false,"optionsType":"json"},{"id":"5e2842a4730589c0","type":"group","z":"d4f60c79eff5211d","name":"Working with JSON Data","style":{"label":true},"nodes":["6acc07a393e5ee26","3255e8a9122253b7","a3b0b4a74133778e","4faba929a84fb358","e5c359b934174c8e","abf011c2e81960c4","22a2b54caae5869d","b9598f313e2a9fd2","d0051f637fd1621a","9e59f275496d722d"],"x":514,"y":539,"w":852,"h":162},{"id":"6acc07a393e5ee26","type":"redis-command","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","server":"e370dc92b39a7ba4","command":"SET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":1140,"y":580,"wires":[["3255e8a9122253b7"]]},{"id":"3255e8a9122253b7","type":"debug","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1270,"y":580,"wires":[]},{"id":"a3b0b4a74133778e","type":"inject","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":580,"wires":[["d0051f637fd1621a"]]},{"id":"4faba929a84fb358","type":"redis-command","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","server":"e370dc92b39a7ba4","command":"GET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":1000,"y":660,"wires":[["b9598f313e2a9fd2"]]},{"id":"e5c359b934174c8e","type":"debug","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1270,"y":660,"wires":[]},{"id":"abf011c2e81960c4","type":"change","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"Set Key for GET","rules":[{"t":"set","p":"topic","pt":"msg","to":"sensor:data","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"[]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":660,"wires":[["4faba929a84fb358"]]},{"id":"22a2b54caae5869d","type":"inject","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":660,"wires":[["abf011c2e81960c4"]]},{"id":"b9598f313e2a9fd2","type":"json","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"","property":"payload","action":"","pretty":false,"x":1130,"y":660,"wires":[["e5c359b934174c8e"]]},{"id":"d0051f637fd1621a","type":"change","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"Set Key","rules":[{"t":"set","p":"topic","pt":"msg","to":"sensor:data","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{\t \"temperature\": 22.5,\t \"humidity\": 65,\t \"timestamp\": $now()\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":580,"wires":[["9e59f275496d722d"]]},{"id":"9e59f275496d722d","type":"json","z":"d4f60c79eff5211d","g":"5e2842a4730589c0","name":"","property":"payload","action":"","pretty":false,"x":990,"y":580,"wires":[["6acc07a393e5ee26"]]},{"id":"39de5da95585227f","type":"group","z":"d4f60c79eff5211d","name":"Pub/Sub Messaging","style":{"label":true},"nodes":["7756558fd542d657","bf2dc26107eff967","9856ddb8e7bc28e8","bcd9b22fd67ecf7c","22f0526fab3b589b"],"x":514,"y":719,"w":632,"h":162},{"id":"7756558fd542d657","type":"inject","z":"d4f60c79eff5211d","g":"39de5da95585227f","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":760,"wires":[["9856ddb8e7bc28e8"]]},{"id":"bf2dc26107eff967","type":"redis-out","z":"d4f60c79eff5211d","g":"39de5da95585227f","server":"e370dc92b39a7ba4","command":"publish","name":"","topic":"alerts:temperature","obj":true,"x":1030,"y":760,"wires":[]},{"id":"9856ddb8e7bc28e8","type":"change","z":"d4f60c79eff5211d","g":"39de5da95585227f","name":"Set Alert Message","rules":[{"t":"set","p":"payload","pt":"msg","to":"ALERT: Temperature critical in Zone A: 85°C - Equipment shutdown initiated","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":760,"wires":[["bf2dc26107eff967"]]},{"id":"bcd9b22fd67ecf7c","type":"redis-in","z":"d4f60c79eff5211d","g":"39de5da95585227f","server":"e370dc92b39a7ba4","command":"subscribe","name":"","topic":"alerts:temperature","obj":true,"timeout":0,"x":630,"y":840,"wires":[["22f0526fab3b589b"]]},{"id":"22f0526fab3b589b","type":"debug","z":"d4f60c79eff5211d","g":"39de5da95585227f","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":990,"y":840,"wires":[]},{"id":"adc4b0df6b0e84e3","type":"group","z":"d4f60c79eff5211d","name":"Using Lua Scripts for Atomic Operations","style":{"label":true},"nodes":["ca8a523710138a11","8804755befa1fbc8","0898e80e28b7d276","0b16690b3c2c10df","9a03ebdea51eefdc","c641d8e54620a73a","7c11a5f1c9716527","a07a025db962be50"],"x":514,"y":899,"w":1112,"h":142},{"id":"ca8a523710138a11","type":"inject","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":1000,"wires":[["8804755befa1fbc8"]]},{"id":"8804755befa1fbc8","type":"function","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","name":"Prepare Lua Script Arguments","func":"// msg.payload format: [keys..., args...]\n// First element(s) are the keys, remaining elements are arguments\nmsg.payload = [\n \"inventory:product:SKU-12345\", // KEYS[1]\n 3 // ARGV[1] - quantity requested\n];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":1000,"wires":[["0898e80e28b7d276"]]},{"id":"0898e80e28b7d276","type":"redis-lua-script","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","server":"e370dc92b39a7ba4","name":"","keyval":"1","func":"local key = KEYS[1]\nlocal requested = tonumber(ARGV[1])\n\nlocal current = tonumber(redis.call('GET', key) or \"0\")\n\nif current >= requested then\n redis.call('DECRBY', key, requested)\n return {1, current - requested}\nelse\n return {0, current}\nend","stored":false,"block":false,"x":1140,"y":1000,"wires":[["0b16690b3c2c10df"]]},{"id":"0b16690b3c2c10df","type":"function","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","name":"Format Lua Script Response","func":"const result = msg.payload;\nconst success = result[0];\nconst remaining = result[1];\n\nif (success === 1) {\n msg.payload = {\n status: \"success\",\n message: `Order processed. Remaining stock: ${remaining}`,\n remaining: remaining\n };\n} else {\n msg.payload = {\n status: \"failed\",\n message: `Insufficient stock. Available: ${remaining}`,\n available: remaining\n };\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1340,"y":1000,"wires":[["9a03ebdea51eefdc"]]},{"id":"9a03ebdea51eefdc","type":"debug","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1530,"y":1000,"wires":[]},{"id":"c641d8e54620a73a","type":"redis-command","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","server":"e370dc92b39a7ba4","command":"SET","name":"","topic":"inventory:product:SKU-12345","params":"[10]","paramsType":"json","payloadType":"json","block":false,"x":880,"y":940,"wires":[["7c11a5f1c9716527"]]},{"id":"7c11a5f1c9716527","type":"debug","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1130,"y":940,"wires":[]},{"id":"a07a025db962be50","type":"inject","z":"d4f60c79eff5211d","g":"adc4b0df6b0e84e3","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":940,"wires":[["c641d8e54620a73a"]]},{"id":"01a96fb2044d723f","type":"group","z":"d4f60c79eff5211d","name":"Direct Redis Client Access with redis-instance","style":{"label":true},"nodes":["745f41f352fa2212","a9ec9a8a5a66daa3","e3a5634ef0ebcb78","5192081309e08db9","4f169053bbd7add9","15f0b0462f795828","4a394700c6914961"],"x":514,"y":1059,"w":732,"h":222},{"id":"745f41f352fa2212","type":"redis-instance","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","server":"e370dc92b39a7ba4","name":"","topic":"redis","location":"flow","x":590,"y":1100,"wires":[]},{"id":"a9ec9a8a5a66daa3","type":"inject","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":620,"y":1180,"wires":[["e3a5634ef0ebcb78"]]},{"id":"e3a5634ef0ebcb78","type":"function","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","name":"Batch Store Sensor Readings (Pipeline)","func":"const redis = flow.get('redis');\n\n// Create a pipeline\nconst pipeline = redis.pipeline();\n\n// Add multiple sensor readings in one batch\nconst sensors = [\n { id: 'temp-01', value: 23.5, unit: 'C' },\n { id: 'temp-02', value: 24.1, unit: 'C' },\n { id: 'humidity-01', value: 65, unit: '%' },\n { id: 'pressure-01', value: 1013, unit: 'hPa' }\n];\n\nsensors.forEach(sensor => {\n const key = `sensor:${sensor.id}:latest`;\n const data = JSON.stringify({\n value: sensor.value,\n unit: sensor.unit,\n timestamp: Date.now()\n });\n pipeline.set(key, data, 'EX', 3600); // Expire in 1 hour\n});\n\n// Execute all commands at once\npipeline.exec((err, results) => {\n if (err) {\n node.error(err, msg);\n return;\n }\n\n msg.payload = {\n message: `Stored ${results.length} sensor readings`,\n results: results\n };\n node.send(msg);\n});","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":1180,"wires":[["5192081309e08db9"]]},{"id":"5192081309e08db9","type":"debug","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","name":"debug 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1140,"y":1180,"wires":[]},{"id":"4f169053bbd7add9","type":"inject","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":620,"y":1240,"wires":[["15f0b0462f795828"]]},{"id":"15f0b0462f795828","type":"function","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","name":"Scan and List Latest Sensor Keys","func":"const redis = flow.get('redis');\n\nasync function scanKeys() {\n const matchPattern = 'sensor:*:latest';\n const allKeys = [];\n let cursor = '0';\n\n try {\n do {\n // Scan with pattern matching\n const result = await redis.scan(\n cursor,\n 'MATCH', matchPattern,\n 'COUNT', 100\n );\n\n cursor = result[0];\n const keys = result[1];\n allKeys.push(...keys);\n\n } while (cursor !== '0');\n\n msg.payload = {\n pattern: matchPattern,\n count: allKeys.length,\n keys: allKeys\n };\n\n node.send(msg);\n\n } catch (err) {\n node.error(err, msg);\n }\n}\n\nscanKeys();","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":860,"y":1240,"wires":[["4a394700c6914961"]]},{"id":"4a394700c6914961","type":"debug","z":"d4f60c79eff5211d","g":"01a96fb2044d723f","name":"debug 9","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1140,"y":1240,"wires":[]},{"id":"d7c53907f5b90ca5","type":"global-config","env":[],"modules":{"node-red-contrib-redis":"1.4.0"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/database/sqlite.md b/nuxt/content-guides/node-red/database/sqlite.md
similarity index 99%
rename from src/node-red/database/sqlite.md
rename to nuxt/content-guides/node-red/database/sqlite.md
index f66ddb2e69..e34fcc3d5d 100644
--- a/src/node-red/database/sqlite.md
+++ b/nuxt/content-guides/node-red/database/sqlite.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: SQLite
- parent: Database
+title: Using SQLite with Node-RED (2026 Updated)
+navTitle: SQLite
+navOrder: 50
meta:
- title: Using SQLite with Node-RED (2026 Updated)
description: Learn how to seamlessly integrate SQLite with Node-RED for efficient data management and application development.
keywords: nodered sqlite, node red sqlite, node-red-node-sqlite, sqlite nodered, sqlite node red
---
-# {{ meta.title }}
+# Using SQLite with Node-RED (2026 Updated)
SQLite is a lightweight, self-contained database that does not require a server. It is ideal for quick setups, small applications, and IoT devices, as it stores data directly in files and is easy to manage.
@@ -230,6 +229,8 @@ DELETE FROM devices WHERE device_id = $id;
Below is the complete flow created throughout this guide. You can import it into Node-RED and experiment with it.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"7397d3636bf9eec9","type":"group","z":"9cf82b68bb89e8ce","name":"Create Table ( fixed statement )","style":{"label":true},"nodes":["843c6bddd1fa88dd","c7134f3054775ed9","462a0643f5f85a85"],"x":1034,"y":4459,"w":732,"h":82},{"id":"843c6bddd1fa88dd","type":"inject","z":"9cf82b68bb89e8ce","g":"7397d3636bf9eec9","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1140,"y":4500,"wires":[["c7134f3054775ed9"]]},{"id":"c7134f3054775ed9","type":"sqlite","z":"9cf82b68bb89e8ce","g":"7397d3636bf9eec9","mydb":"c91139d411507971","sqlquery":"fixed","sql":"CREATE TABLE IF NOT EXISTS devices (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n device_id TEXT NOT NULL UNIQUE,\n name TEXT,\n status TEXT,\n location TEXT,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP\n);\n","name":"Create Table","x":1490,"y":4500,"wires":[["462a0643f5f85a85"]]},{"id":"462a0643f5f85a85","type":"debug","z":"9cf82b68bb89e8ce","g":"7397d3636bf9eec9","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1660,"y":4500,"wires":[]},{"id":"c91139d411507971","type":"sqlitedb","db":"/tmp/sqlite","mode":"RWC"},{"id":"191f069422d0f9b4","type":"group","z":"9cf82b68bb89e8ce","name":"Read Records","style":{"label":true},"nodes":["f145276495e6a7bd","b6e61ecb0379b93b","02c7723103391825"],"x":1034,"y":4559,"w":732,"h":82},{"id":"f145276495e6a7bd","type":"inject","z":"9cf82b68bb89e8ce","g":"191f069422d0f9b4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1140,"y":4600,"wires":[["b6e61ecb0379b93b"]]},{"id":"b6e61ecb0379b93b","type":"sqlite","z":"9cf82b68bb89e8ce","g":"191f069422d0f9b4","mydb":"c91139d411507971","sqlquery":"fixed","sql":"SELECT * FROM devices;\n","name":"Read","x":1470,"y":4600,"wires":[["02c7723103391825"]]},{"id":"02c7723103391825","type":"debug","z":"9cf82b68bb89e8ce","g":"191f069422d0f9b4","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1660,"y":4600,"wires":[]},{"id":"c9bf8c21a73cf4f1","type":"group","z":"9cf82b68bb89e8ce","name":"Batch without response","style":{"label":true},"nodes":["d8d2fd8ff4da64fe","7d7556aa27fa6ca8","d15a64daed322158","ea5920b64362e412"],"x":1034,"y":4659,"w":732,"h":82},{"id":"d8d2fd8ff4da64fe","type":"inject","z":"9cf82b68bb89e8ce","g":"c9bf8c21a73cf4f1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1140,"y":4700,"wires":[["ea5920b64362e412"]]},{"id":"7d7556aa27fa6ca8","type":"sqlite","z":"9cf82b68bb89e8ce","g":"c9bf8c21a73cf4f1","mydb":"c91139d411507971","sqlquery":"batch","sql":"INSERT INTO devices (device_id, name, status, location)\nVALUES ($id, $name, $status, $location);","name":"Insert","x":1470,"y":4700,"wires":[["d15a64daed322158"]]},{"id":"d15a64daed322158","type":"debug","z":"9cf82b68bb89e8ce","g":"c9bf8c21a73cf4f1","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1660,"y":4700,"wires":[]},{"id":"ea5920b64362e412","type":"function","z":"9cf82b68bb89e8ce","g":"c9bf8c21a73cf4f1","name":"Batch Insert","func":"// Sample 10 devices\nconst devices = [\n { id: 'DEV002', name: 'Pressure Sensor', status: 'offline', location: 'Factory Floor 2' },\n { id: 'DEV003', name: 'Humidity Sensor', status: 'online', location: 'Warehouse 1' },\n { id: 'DEV004', name: 'Vibration Sensor', status: 'online', location: 'Factory Floor 3' },\n { id: 'DEV005', name: 'Flow Sensor', status: 'offline', location: 'Plant 1' },\n { id: 'DEV006', name: 'Level Sensor', status: 'online', location: 'Tank 1' },\n { id: 'DEV007', name: 'Pressure Gauge', status: 'online', location: 'Plant 2' },\n { id: 'DEV008', name: 'Temperature Sensor 2', status: 'offline', location: 'Warehouse 2' },\n { id: 'DEV009', name: 'Humidity Sensor 2', status: 'online', location: 'Plant 3' },\n { id: 'DEV010', name: 'Flow Meter', status: 'online', location: 'Factory Floor 4' }\n];\n\n// Generate batch SQL\nconst batchSQL = devices.map(d =>\n `INSERT INTO devices (device_id, name, status, location) VALUES \n ('${d.id}', '${d.name}', '${d.status}', '${d.location}');`\n).join('\\n');\n\nmsg.topic = batchSQL;\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1310,"y":4700,"wires":[["7d7556aa27fa6ca8"]]},{"id":"e199d35406609471","type":"group","z":"9cf82b68bb89e8ce","name":"Update Record ( via prepared statement with array )","style":{"label":true},"nodes":["9688278c29895ab0","ea71fbc3ef5cc8bd","9208e6ef30218e35","1ad5c81d925a0310"],"x":1034,"y":4759,"w":732,"h":82},{"id":"9688278c29895ab0","type":"inject","z":"9cf82b68bb89e8ce","g":"e199d35406609471","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1140,"y":4800,"wires":[["9208e6ef30218e35"]]},{"id":"ea71fbc3ef5cc8bd","type":"sqlite","z":"9cf82b68bb89e8ce","g":"e199d35406609471","mydb":"c91139d411507971","sqlquery":"prepared","sql":"UPDATE devices SET status = $1 WHERE device_id = $2;","name":"Update","x":1480,"y":4800,"wires":[["1ad5c81d925a0310"]]},{"id":"9208e6ef30218e35","type":"change","z":"9cf82b68bb89e8ce","g":"e199d35406609471","name":"Set Params","rules":[{"t":"set","p":"params","pt":"msg","to":"[\"offline\", \"DEV002\"]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":1310,"y":4800,"wires":[["ea71fbc3ef5cc8bd"]]},{"id":"1ad5c81d925a0310","type":"debug","z":"9cf82b68bb89e8ce","g":"e199d35406609471","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1660,"y":4800,"wires":[]},{"id":"67085d9fa9b7b5dd","type":"group","z":"9cf82b68bb89e8ce","name":"Delete Record ( via prepared statement with object )","style":{"label":true},"nodes":["c551a497bf776c15","1767cd99e3b03bd2","b3570250013c8703","9ec536a39e055ffe"],"x":1034,"y":4859,"w":732,"h":82},{"id":"c551a497bf776c15","type":"inject","z":"9cf82b68bb89e8ce","g":"67085d9fa9b7b5dd","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1140,"y":4900,"wires":[["b3570250013c8703"]]},{"id":"1767cd99e3b03bd2","type":"sqlite","z":"9cf82b68bb89e8ce","g":"67085d9fa9b7b5dd","mydb":"c91139d411507971","sqlquery":"prepared","sql":"DELETE FROM devices WHERE device_id = $device_id;","name":"Delete","x":1470,"y":4900,"wires":[["9ec536a39e055ffe"]]},{"id":"b3570250013c8703","type":"change","z":"9cf82b68bb89e8ce","g":"67085d9fa9b7b5dd","name":"Set Params","rules":[{"t":"set","p":"params","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"params.$device_id","pt":"msg","to":"DEV006","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1310,"y":4900,"wires":[["1767cd99e3b03bd2"]]},{"id":"9ec536a39e055ffe","type":"debug","z":"9cf82b68bb89e8ce","g":"67085d9fa9b7b5dd","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1660,"y":4900,"wires":[]},{"id":"4c464964391e1c1c","type":"global-config","env":[],"modules":{"node-red-node-sqlite":"1.1.1"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/database/timescaledb.md b/nuxt/content-guides/node-red/database/timescaledb.md
similarity index 97%
rename from src/node-red/database/timescaledb.md
rename to nuxt/content-guides/node-red/database/timescaledb.md
index aff38d57c4..6bce94c071 100644
--- a/src/node-red/database/timescaledb.md
+++ b/nuxt/content-guides/node-red/database/timescaledb.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: TimescaleDB
- parent: Database
+title: Using TimescaleDB with Node-RED (2026 Updated)
+navTitle: TimescaleDB
+navOrder: 50
meta:
- title: Using TimescaleDB with Node-RED (2026 Updated)
description: Learn how to integrate TimescaleDB with Node-RED for storing and managing time-series data efficiently.
keywords: node-red timescaledb, nodered postgresql, nodered time-series database
-image: /node-red/database/images/timescaledb-with-node-red.png
+image: /docs/node-red/database/images/timescaledb-with-node-red.png
---
-# {{ meta.title }}
+# Using TimescaleDB with Node-RED (2026 Updated)
In the context of IoT and IIoT applications, time series databases are essential for storing data based on timestamps. While InfluxDB has been a popular choice for a long time, another time series database, TimescaleDB, is gaining popularity. This guide will cover how to use TimescaleDB with Node-RED, how TimescaleDB works, and the queries needed when building IoT applications.
@@ -87,9 +86,11 @@ SELECT create_hypertable('sensor_data', 'time');
2. Drag an Inject node onto the canvas, which we will use to trigger the operation.
3. Connect the Inject node's output to the input of the PostgreSQL node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"d766709f13c8410b","type":"inject","z":"7748186d67ad0a58","name":"Create hypertable","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":140,"wires":[["a977cb646de2a30b"]]},{"id":"a977cb646de2a30b","type":"postgresql","z":"7748186d67ad0a58","name":"PostgreSQL","query":"CREATE TABLE sensor_data (\n time TIMESTAMPTZ NOT NULL,\n location TEXT,\n temperature DOUBLE PRECISION\n);\n\nSELECT create_hypertable('sensor_data', 'time');\n","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":490,"y":140,"wires":[["c843732f2088f83e"]]},{"id":"c843732f2088f83e","type":"debug","z":"7748186d67ad0a58","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":740,"y":140,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Inserting Data into the Table
@@ -119,9 +120,11 @@ VALUES (
5. Drag a Debug node onto the canvas.
6. Connect the output of the Inject nodes to the input of the PostgreSQL node and the output of PostgreSQL to the input of the Debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"c42dfdaa44a02eda","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"INSERT INTO sensor_data (time, location, temperature)\nVALUES (now(), '{{msg.payload.location}}', '{{msg.payload.temperature}}');\n","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":490,"y":540,"wires":[["f88822fc9fdf90dc"]]},{"id":"f88822fc9fdf90dc","type":"debug","z":"7748186d67ad0a58","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":700,"y":540,"wires":[]},{"id":"b3b5fc6ad830145e","type":"inject","z":"7748186d67ad0a58","name":"Sensor placed in the New York","props":[{"p":"payload.location","v":"New York","vt":"str"},{"p":"payload.temperature","v":"$floor(($random() * 21) + 30)","vt":"jsonata"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":230,"y":580,"wires":[["c42dfdaa44a02eda"]]},{"id":"2d07d8d73ed43f37","type":"inject","z":"7748186d67ad0a58","name":"Sensor placed in the Chicago","props":[{"p":"payload.location","v":"Chicago","vt":"str"},{"p":"payload.temperature","v":"$floor(($random() * 21) + 30)","vt":"jsonata"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":230,"y":520,"wires":[["c42dfdaa44a02eda"]]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Updating data to the table
@@ -140,9 +143,11 @@ UPDATE sensor_data
3. Drag a Debug node onto the canvas.
4. Connect the output of the Inject node to the input of the PostgreSQL node and the output of the PostgreSQL node to the input of the Debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"702b9169ff00396c","type":"inject","z":"7748186d67ad0a58","name":"Updating data based on condition","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":1120,"wires":[["81dcb2d851bb9316"]]},{"id":"81dcb2d851bb9316","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"-- Update temperature data in the sensor_data table\nUPDATE sensor_data\n SET temperature = temperature + 0.1\n WHERE time >= '2024-05-29T11:50:25.859Z' -- Starting timestamp for the update\n AND time < '2024-05-29T12:17:43.305Z'; -- Ending timestamp for the update\n","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":530,"y":1120,"wires":[["29e28595a9f5a0df"]]},{"id":"29e28595a9f5a0df","type":"debug","z":"7748186d67ad0a58","name":"debug 14","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":760,"y":1120,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Deleting data to the table
@@ -157,9 +162,11 @@ WHERE temperature < 35 -- Delete rows where the temperature is less than 35 degr
3. Drag a Debug node onto the canvas.
4. Connect the output of the Inject node to the input of the PostgreSQL node and the output of the PostgreSQL node to the input of the Debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"702b9169ff00396c","type":"inject","z":"7748186d67ad0a58","name":"Delete data based on condition","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":1120,"wires":[["81dcb2d851bb9316"]]},{"id":"81dcb2d851bb9316","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"-- Delete rows from the sensor_data table where the temperature is below 35 degrees Celsius or humidity is below 60%\nDELETE FROM sensor_data\nWHERE temperature < 35 -- Delete rows where the temperature is less than 35 degrees Celsius","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":530,"y":1120,"wires":[["29e28595a9f5a0df"]]},{"id":"29e28595a9f5a0df","type":"debug","z":"7748186d67ad0a58","name":"debug 14","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":760,"y":1120,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Retrieving all data from the table
@@ -173,9 +180,11 @@ SELECT * FROM sensor_data;
3. Drag a Debug node onto the canvas.
4. Connect the output of the Inject node to the input of the PostgreSQL node and the output of the PostgreSQL node to the input of the Debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"d551e15f7013e970","type":"inject","z":"7748186d67ad0a58","name":"Retrieve all data","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":660,"wires":[["858063c8e7d90f50"]]},{"id":"858063c8e7d90f50","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"SELECT * FROM sensor_data;","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":550,"y":660,"wires":[["79156835d5ad4c4d"]]},{"id":"79156835d5ad4c4d","type":"debug","z":"7748186d67ad0a58","name":"debug 9","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":780,"y":660,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Retrieve Recent Data
@@ -195,9 +204,11 @@ LIMIT 100; -- Limit the results to 100 rows
3. Drag a Debug node onto the canvas.
4. Connect the output of the Inject node to the input of the PostgreSQL node and the output of the PostgreSQL node to the input of the Debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"9436c79e9d9e3593","type":"inject","z":"7748186d67ad0a58","name":"Retrieve last 100 data ordered by time","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":720,"wires":[["6d26117dd61d0ecc"]]},{"id":"6d26117dd61d0ecc","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"SELECT * FROM sensor_data ORDER BY time DESC LIMIT 100;","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":550,"y":720,"wires":[["092a08307ae6d63c"]]},{"id":"092a08307ae6d63c","type":"debug","z":"7748186d67ad0a58","name":"debug 10","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":780,"y":720,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Retrieve Data Based on Time Range
@@ -216,9 +227,11 @@ WHERE time > NOW() - INTERVAL '400 SECONDS';
3. Drag a Debug node onto the canvas.
4. Connect the output of the inject node to the input of the PostgreSQL node, and connect the output of the PostgreSQL node to the input of the debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"60e111b1f6e09613","type":"inject","z":"7748186d67ad0a58","name":"Retrieve data based on time range","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":240,"y":880,"wires":[["0b1ec338dbb164f9"]]},{"id":"0b1ec338dbb164f9","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"-- Aggregate data into specific time buckets\nSELECT time_bucket('15 minutes', time) AS fifteen_min, -- Create time buckets of 15 minutes\n location, -- Location of the sensor\n MAX(temperature) AS max_temp -- Calculate the maximum temperature within each time bucket\nFROM sensor_data -- Select data from the conditions table\nWHERE time > NOW() - INTERVAL '3 hours' -- Filter data to include only the last 3 hours\nGROUP BY fifteen_min, location -- Group data by time buckets and location\nORDER BY fifteen_min DESC, max_temp DESC; -- Order the results by time bucket in descending order, and then by maximum temperature in descending order\n","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":550,"y":880,"wires":[["f6f6c4fa651ee351"]]},{"id":"f6f6c4fa651ee351","type":"debug","z":"7748186d67ad0a58","name":"debug 13","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":780,"y":880,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Aggregating data into specific time bucket
@@ -241,9 +254,11 @@ ORDER BY fifteen_min DESC, max_temp DESC; -- Order the results by time bucket in
3. Drag the Debug node onto the canvas.
4. Connect the output of the inject node to the input of the PostgreSQL node, and connect the output of the PostgreSQL node to the input of the debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"60e111b1f6e09613","type":"inject","z":"7748186d67ad0a58","name":"Aggregating data into specific time bucket","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":880,"wires":[["0b1ec338dbb164f9"]]},{"id":"0b1ec338dbb164f9","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"-- Aggregate data into specific time buckets\nSELECT time_bucket('15 minutes', time) AS fifteen_min, -- Create time buckets of 15 minutes\n location, -- Location of the sensor\n MAX(temperature) AS max_temp -- Calculate the maximum temperature within each time bucket\nFROM sensor_data -- Select data from the conditions table\nWHERE time > NOW() - INTERVAL '3 hours' -- Filter data to include only the last 3 hours\nGROUP BY fifteen_min, location -- Group data by time buckets and location\nORDER BY fifteen_min DESC, max_temp DESC; -- Order the results by time bucket in descending order, and then by maximum temperature in descending order\n","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":550,"y":880,"wires":[["f6f6c4fa651ee351"]]},{"id":"f6f6c4fa651ee351","type":"debug","z":"7748186d67ad0a58","name":"debug 13","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":780,"y":880,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
### Dropping the table
@@ -257,12 +272,14 @@ DROP TABLE IF EXISTS sensor_data;
3. Drag the Debug node onto the canvas.
4. Connect the output of the inject node to the input of the PostgreSQL node, and connect the output of the PostgreSQL node to the input of the debug node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"5275332fecd1c715","type":"inject","z":"7748186d67ad0a58","name":"Drop table","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":960,"wires":[["382c26de90712487"]]},{"id":"382c26de90712487","type":"postgresql","z":"7748186d67ad0a58","name":"","query":"DROP TABLE IF EXISTS sensor_data;","postgreSQLConfig":"ea1f8e3d9db95245","split":false,"rowsPerMsg":1,"outputs":1,"x":450,"y":960,"wires":[["d7eb0c9020c968c5"]]},{"id":"d7eb0c9020c968c5","type":"debug","z":"7748186d67ad0a58","name":"debug 12","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":720,"y":960,"wires":[]},{"id":"ea1f8e3d9db95245","type":"postgreSQLConfig","name":"TimescaleDB Configurations","host":"${HOST}","hostFieldType":"str","port":"${PORT}","portFieldType":"num","database":"${DATABASE}","databaseFieldType":"str","ssl":"false","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"${USERNAME}","userFieldType":"str","password":"${PASSWORD}","passwordFieldType":"str"}]
-{% endrenderFlow %}
+```
+::
## Deploying the Flow
1. To test the imported flows, you need to deploy them. To do that, click on the deploy button located in the top right corner.
-After deploying the flow, you can test each operation such as creating, deleting, updating, and other queries by clicking on the inject button. Upon successful operation, you will be able to see the results in the debug panel of the sidebar. If you want to learn any additional information about PostgreSQL, you can refer to the [Using PostgreSQL with Node-RED](/node-red/database/postgresql/) where you will also find the section which shows the messages received after a successful operation by the PostgresWQL node.
\ No newline at end of file
+After deploying the flow, you can test each operation such as creating, deleting, updating, and other queries by clicking on the inject button. Upon successful operation, you will be able to see the results in the debug panel of the sidebar. If you want to learn any additional information about PostgreSQL, you can refer to the [Using PostgreSQL with Node-RED](/docs/node-red/database/postgresql/) where you will also find the section which shows the messages received after a successful operation by the PostgresWQL node.
diff --git a/nuxt/content-guides/node-red/getting-started/README.md b/nuxt/content-guides/node-red/getting-started/README.md
new file mode 100644
index 0000000000..491d39aa42
--- /dev/null
+++ b/nuxt/content-guides/node-red/getting-started/README.md
@@ -0,0 +1,22 @@
+---
+title: Getting Started with Node-RED
+navTitle: Getting started
+navOrder: 2
+meta:
+ description: Learn the basics of Node-RED, a powerful tool for IoT integration and workflow automation.
+ keywords: node-red, node-red basics, node-red foundation, node-red editor
+---
+
+# Getting Started with Node-RED
+
+This section provides an overview of Node-RED, a robust visual tool designed for integrating IoT devices and automating workflows without needing extensive programming knowledge.
+
+- [Getting Started with the Node-RED Editor](/docs/node-red/getting-started/editor/): Learn about the powerful features of Node-RED Editor.
+- [Node-RED Port (localhost:1880)](/docs/node-red/getting-started/node-red-port/): Learn how to configure Node-RED ports, change default settings, secure your installation, and set up remote access with FlowFuse.
+- [How to Update Node-RED](/docs/node-red/getting-started/update-node-red/): Learn how to update Node-RED across different installation methods including npm, Raspberry Pi, Docker, and FlowFuse
+- [Node-RED Library – A Curated and Actively Maintained List of Nodes](/docs/node-red/getting-started/library/): Browse the Node-RED Library for community-built nodes and integrations. FlowFuse's curated catalog offers tested, documented, enterprise-ready solutions with professional support for critical deployments.
+- [Installing Node-RED on Android](/docs/node-red/getting-started/node-red-android/): Learn how to install and run Node-RED on Android devices using Termux
+- [Understanding Node-RED Messages](/docs/node-red/getting-started/node-red-messages/): A comprehensive guide to working with Node-RED messages, ensuring error-free flows and optimized data handling.
+- [Strings in Node-RED: Convert String to Number, Split, Concatenate, Trim, and More](/docs/node-red/getting-started/string/): Learn essential string operations in Node-RED including converting between strings and numbers, splitting and concatenating text, parsing JSON, extracting substrings, trimming whitespace, and more. Step-by-step guide with practical examples.
+- [Node-RED Programming](/docs/node-red/getting-started/programming/): Master Node-RED programming fundamentals including flows, nodes, messages, conditional logic, and data manipulation. Learn essential concepts for building sophisticated visual programming solutions.
+- [Working with Dates and Times in Node-RED](/docs/node-red/getting-started/date-and-time/): Learn how to handle dates and times in Node-RED without coding. Master timestamps, formatting, timezones, calculations, and time-based automation with visual nodes.
diff --git a/src/node-red/getting-started/date-and-time.md b/nuxt/content-guides/node-red/getting-started/date-and-time.md
similarity index 98%
rename from src/node-red/getting-started/date-and-time.md
rename to nuxt/content-guides/node-red/getting-started/date-and-time.md
index 26f2a6ed0f..7a5f743c35 100644
--- a/src/node-red/getting-started/date-and-time.md
+++ b/nuxt/content-guides/node-red/getting-started/date-and-time.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: Date & Time
- order: 7
- parent: Getting Started
+title: Working with Dates and Times in Node-RED
+navTitle: Date & Time
+navOrder: 7
meta:
- title: Working with Dates and Times in Node-RED
- description: Learn how to handle dates and times in Node-RED without coding. Master timestamps, formatting, timezones, calculations, and time-based automation with visual nodes.
+ description: Learn how to handle dates and times in Node-RED without coding. Master timestamps, formatting, timezones, calculations, and time-based automation with visual nodes.
keywords: Node-RED, dates, times, timestamps, JSONata, Moment.js, Date/Time Formatter, timezones, date formatting, time calculations, inject node, change node, human-readable dates, relative time, time-based automation, date math, date difference, ISO 8601, flow automation, Node-RED tutorial, date manipulation, scheduling, event logging, time conversion, time adjustment, visual programming
---
-# {{meta.title}}
+# Working with Dates and Times in Node-RED
Working with dates and times comes up constantly in Node-RED. Whether you're logging events, scheduling tasks, checking business hours, displaying the current time, or pulling historical data, it all relies on handling timestamps correctly. The best part is that you can manage all of this using visual nodes, without writing any code.
@@ -48,9 +46,11 @@ In the inject/change node, select **JSONata expression** from the payload type d
This JSONata approach works identically in both nodes, use whichever fits your flow better.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"9341cec1a1f9e50e","type":"group","z":"d7101f3a4d45deed","name":"Getting the Current Time in Node-RED","style":{"label":true},"nodes":["89cb30afb7fec451","51937bef067f5257","547ec4b9d6a389c9"],"x":108,"y":73,"w":804,"h":694},{"id":"89cb30afb7fec451","type":"group","z":"d7101f3a4d45deed","g":"9341cec1a1f9e50e","name":"Using Change node ( JSONata )","style":{"label":true},"nodes":["b5bdfd1b2a3b9c27","444d17c32b8bd1c1","276e0fcf607c944d","07fbc83f895b7aa4","ac8e5e28a896f52c","8d4143fb8361c469","aff528c64b9568ad"],"x":134,"y":539,"w":752,"h":202},{"id":"b5bdfd1b2a3b9c27","type":"inject","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"Inject","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":230,"y":640,"wires":[["444d17c32b8bd1c1","276e0fcf607c944d","07fbc83f895b7aa4"]]},{"id":"444d17c32b8bd1c1","type":"change","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"milliseconds since epoch","rules":[{"t":"set","p":"payload","pt":"msg","to":"$millis()","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":580,"wires":[["ac8e5e28a896f52c"]]},{"id":"276e0fcf607c944d","type":"change","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"YYYY-MM-DDTHH:mm:ss.sssZ","rules":[{"t":"set","p":"payload","pt":"msg","to":"$now()","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":640,"wires":[["8d4143fb8361c469"]]},{"id":"07fbc83f895b7aa4","type":"change","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"JavaScript Date object","rules":[{"t":"set","p":"payload","pt":"msg","to":"$moment()","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":700,"wires":[["aff528c64b9568ad"]]},{"id":"ac8e5e28a896f52c","type":"debug","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":580,"wires":[]},{"id":"8d4143fb8361c469","type":"debug","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":640,"wires":[]},{"id":"aff528c64b9568ad","type":"debug","z":"d7101f3a4d45deed","g":"89cb30afb7fec451","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":700,"wires":[]},{"id":"51937bef067f5257","type":"group","z":"d7101f3a4d45deed","g":"9341cec1a1f9e50e","name":"Using Change node ( Timestamp option )","style":{"label":true},"nodes":["375d143d48695d37","72fcba815fa5cfdd","4e50e0a2e436b658","d41aa61b456bd3a1","f5bec9511bc8f77f","9d3fb7b89adafa8d","7e60fb224e4ff747"],"x":134,"y":319,"w":752,"h":202},{"id":"375d143d48695d37","type":"inject","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"Inject","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":230,"y":420,"wires":[["72fcba815fa5cfdd","4e50e0a2e436b658","d41aa61b456bd3a1"]]},{"id":"72fcba815fa5cfdd","type":"change","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"milliseconds since epoch","rules":[{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":360,"wires":[["f5bec9511bc8f77f"]]},{"id":"4e50e0a2e436b658","type":"change","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"YYYY-MM-DDTHH:mm:ss.sssZ","rules":[{"t":"set","p":"payload","pt":"msg","to":"iso","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":420,"wires":[["9d3fb7b89adafa8d"]]},{"id":"d41aa61b456bd3a1","type":"change","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"JavaScript Date object","rules":[{"t":"set","p":"payload","pt":"msg","to":"object","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":480,"wires":[["7e60fb224e4ff747"]]},{"id":"f5bec9511bc8f77f","type":"debug","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":360,"wires":[]},{"id":"9d3fb7b89adafa8d","type":"debug","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":420,"wires":[]},{"id":"7e60fb224e4ff747","type":"debug","z":"d7101f3a4d45deed","g":"51937bef067f5257","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":480,"wires":[]},{"id":"547ec4b9d6a389c9","type":"group","z":"d7101f3a4d45deed","g":"9341cec1a1f9e50e","name":"Using Inject Nodes ( Timestamp Option )","style":{"label":true},"nodes":["499ab581537f5598","b9bd4fdc2f424cbf","b969db92319355f3","ccc969cd1ed85eda","ab178e4e3109f9b0","cdb0cfd879846420"],"x":134,"y":99,"w":752,"h":202},{"id":"499ab581537f5598","type":"inject","z":"d7101f3a4d45deed","g":"547ec4b9d6a389c9","name":"milliseconds since epoch","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":310,"y":140,"wires":[["b9bd4fdc2f424cbf"]]},{"id":"b9bd4fdc2f424cbf","type":"debug","z":"d7101f3a4d45deed","g":"547ec4b9d6a389c9","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":140,"wires":[]},{"id":"b969db92319355f3","type":"inject","z":"d7101f3a4d45deed","g":"547ec4b9d6a389c9","name":"YYYY-MM-DDTHH:mm:ss.sssZ","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"iso","payloadType":"date","x":340,"y":200,"wires":[["ccc969cd1ed85eda"]]},{"id":"ccc969cd1ed85eda","type":"debug","z":"d7101f3a4d45deed","g":"547ec4b9d6a389c9","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":200,"wires":[]},{"id":"ab178e4e3109f9b0","type":"inject","z":"d7101f3a4d45deed","g":"547ec4b9d6a389c9","name":"JavaScript Date object","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"object","payloadType":"date","x":300,"y":260,"wires":[["cdb0cfd879846420"]]},{"id":"cdb0cfd879846420","type":"debug","z":"d7101f3a4d45deed","g":"547ec4b9d6a389c9","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":260,"wires":[]}]
-{% endrenderFlow %}
+```
+::
> For more advanced date/time operations and formatting, see the [JSONata documentation](https://docs.jsonata.org/date-time-functions).
@@ -172,9 +172,11 @@ JSONata uses square brackets, but the codes are different from the **Date/Time F
- `$fromMillis(payload, '[M]/[D]/[Y]')` → 12/11/2024
- `$fromMillis(payload, '[h]:[m01] [P]')` → 3:45 PM
-{% renderFlow %}
+::render-flow
+```json
[{"id":"1e8a1110d36d3f33","type":"group","z":"d7101f3a4d45deed","name":"Formatting Dates for Display","style":{"label":true},"nodes":["f019c2f5ed9d8104","1699577150167eb5"],"x":108,"y":793,"w":804,"h":414},{"id":"f019c2f5ed9d8104","type":"group","z":"d7101f3a4d45deed","g":"1e8a1110d36d3f33","name":"The Moment Nodes","style":{"label":true},"nodes":["bd06565765141868","898b23fa51f95edf","f734d9e8979b20b2","f3a3a5a9974816d3","2de95300ee070276","d5f3d8aeaaeffa7d","b713b196b48c62d3"],"x":134,"y":819,"w":752,"h":202},{"id":"bd06565765141868","type":"moment","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"MMMM D, YYYY","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":0,"adjType":"days","adjDir":"add","format":"MMMM D, YYYY","locale":"en-US","output":"payload","outputType":"msg","outTz":"Africa/Abidjan","x":460,"y":860,"wires":[["898b23fa51f95edf"]]},{"id":"898b23fa51f95edf","type":"debug","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":860,"wires":[]},{"id":"f734d9e8979b20b2","type":"inject","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":920,"wires":[["f3a3a5a9974816d3","d5f3d8aeaaeffa7d","bd06565765141868"]]},{"id":"f3a3a5a9974816d3","type":"moment","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"MMMM D, YYYY [at] h:mm A","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":0,"adjType":"days","adjDir":"add","format":"MMMM D, YYYY [at] h:mm A","locale":"en-US","output":"payload","outputType":"msg","outTz":"Africa/Abidjan","x":510,"y":920,"wires":[["2de95300ee070276"]]},{"id":"2de95300ee070276","type":"debug","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":920,"wires":[]},{"id":"d5f3d8aeaaeffa7d","type":"moment","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"fromNow","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":0,"adjType":"days","adjDir":"add","format":"fromNow","locale":"en-US","output":"payload","outputType":"msg","outTz":"Africa/Abidjan","x":430,"y":980,"wires":[["b713b196b48c62d3"]]},{"id":"b713b196b48c62d3","type":"debug","z":"d7101f3a4d45deed","g":"f019c2f5ed9d8104","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":980,"wires":[]},{"id":"1699577150167eb5","type":"group","z":"d7101f3a4d45deed","g":"1e8a1110d36d3f33","name":"JSONata Formatting","style":{"label":true},"nodes":["678f13a9fad9c397","cf810cbd301fddb0","9443cb33ea42e25b","255b135032862816","2d3b9aee8b0ebe5b"],"x":134,"y":1039,"w":752,"h":142},{"id":"678f13a9fad9c397","type":"inject","z":"d7101f3a4d45deed","g":"1699577150167eb5","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":1120,"wires":[["cf810cbd301fddb0","2d3b9aee8b0ebe5b"]]},{"id":"cf810cbd301fddb0","type":"change","z":"d7101f3a4d45deed","g":"1699577150167eb5","name":"$fromMillis(payload, '[M]/[D]/[Y]')","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis(payload, '[M]/[D]/[Y]')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":1080,"wires":[["9443cb33ea42e25b"]]},{"id":"9443cb33ea42e25b","type":"debug","z":"d7101f3a4d45deed","g":"1699577150167eb5","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1080,"wires":[]},{"id":"255b135032862816","type":"debug","z":"d7101f3a4d45deed","g":"1699577150167eb5","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1140,"wires":[]},{"id":"2d3b9aee8b0ebe5b","type":"change","z":"d7101f3a4d45deed","g":"1699577150167eb5","name":"$fromMillis(payload, '[h]:[m01] [P]')","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis(payload, '[h]:[m01] [P]')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":1140,"wires":[["255b135032862816"]]},{"id":"5e97a86213b906bf","type":"global-config","env":[],"modules":{"node-red-contrib-moment":"5.0.0"}}]
-{% endrenderFlow %}
+```
+::
## Handling Time Zones
@@ -226,9 +228,11 @@ $fromMillis(payload, '[M]/[D]/[Y] [h]:[m01] [P]', '-0500')
The offset is a string like `-0500` (5 hours behind UTC). This works, but you have to know the offset and manage daylight saving time yourself. The **Date/Time Formatter** node handles all of that automatically.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"894fb9d3ddfa14d7","type":"group","z":"d7101f3a4d45deed","name":"Handling Time Zones","style":{"label":true},"nodes":["4d735b1f71f79189","0c31bb975d166128"],"x":108,"y":1233,"w":804,"h":474},{"id":"4d735b1f71f79189","type":"group","z":"d7101f3a4d45deed","g":"894fb9d3ddfa14d7","name":"Converting to a Different Timezone Using Moment nodes","style":{"label":true},"nodes":["995cf0548ac66af1","963db1d838c1a34d","faefd25e268f68e9","9259ebfd137a16b0","32f2b829e7a6de84","6a4a272401d45e61","48bf3927e175904c"],"x":134,"y":1259,"w":752,"h":202},{"id":"995cf0548ac66af1","type":"inject","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":1360,"wires":[["963db1d838c1a34d","6a4a272401d45e61","48bf3927e175904c"]]},{"id":"963db1d838c1a34d","type":"moment","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"America/New_York","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":0,"adjType":"days","adjDir":"add","format":"MMMM D, YYYY h:mm A z","locale":"en-US","output":"payload","outputType":"msg","outTz":"America/New_York","x":470,"y":1300,"wires":[["faefd25e268f68e9"]]},{"id":"faefd25e268f68e9","type":"debug","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1300,"wires":[]},{"id":"9259ebfd137a16b0","type":"debug","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1360,"wires":[]},{"id":"32f2b829e7a6de84","type":"debug","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1420,"wires":[]},{"id":"6a4a272401d45e61","type":"moment","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"Europe/London","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":0,"adjType":"days","adjDir":"add","format":"MMMM D, YYYY h:mm A z","locale":"en-US","output":"payload","outputType":"msg","outTz":"Europe/London","x":460,"y":1360,"wires":[["9259ebfd137a16b0"]]},{"id":"48bf3927e175904c","type":"moment","z":"d7101f3a4d45deed","g":"4d735b1f71f79189","name":"Asia/Tokyo","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":0,"adjType":"days","adjDir":"add","format":"MMMM D, YYYY h:mm A z","locale":"en-US","output":"payload","outputType":"msg","outTz":"Asia/Tokyo","x":440,"y":1420,"wires":[["32f2b829e7a6de84"]]},{"id":"0c31bb975d166128","type":"group","z":"d7101f3a4d45deed","g":"894fb9d3ddfa14d7","name":"Converting to a Different Timezone Using JSONata","style":{"label":true},"nodes":["ee7822f24a2792cb","0a006c9e87656cfc","31122b7227db8ce7","c62c7e7b3b450e85","6dfde983c76894c3","74e37a7cc2080238","f0c786e6db30ffad"],"x":134,"y":1479,"w":752,"h":202},{"id":"ee7822f24a2792cb","type":"inject","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":1580,"wires":[["6dfde983c76894c3","74e37a7cc2080238","f0c786e6db30ffad"]]},{"id":"0a006c9e87656cfc","type":"debug","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1520,"wires":[]},{"id":"31122b7227db8ce7","type":"debug","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1580,"wires":[]},{"id":"c62c7e7b3b450e85","type":"debug","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1640,"wires":[]},{"id":"6dfde983c76894c3","type":"change","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"America/New_York ( Winter )","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis(payload, '[M]/[D]/[Y] [h]:[m01] [P]', '-0500')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1520,"wires":[["0a006c9e87656cfc"]]},{"id":"74e37a7cc2080238","type":"change","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"Europe/London ( Winter )","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis(payload, '[M]/[D]/[Y] [h]:[m01] [P]', '+0000')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":1580,"wires":[["31122b7227db8ce7"]]},{"id":"f0c786e6db30ffad","type":"change","z":"d7101f3a4d45deed","g":"0c31bb975d166128","name":"Asia/Tokyo ( Winter )","rules":[{"t":"set","p":"payload","pt":"msg","to":"$fromMillis(payload, '[M]/[D]/[Y] [h]:[m01] [P]', '+0900')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":1640,"wires":[["c62c7e7b3b450e85"]]},{"id":"dcb2806bdd099173","type":"global-config","env":[],"modules":{"node-red-contrib-moment":"5.0.0"}}]
-{% endrenderFlow %}
+```
+::
## Doing Math with Dates
@@ -312,6 +316,8 @@ This example calculates the difference between a timestamp seven days ago and th
Hit the **Inject** button. The **Debug** tab will show the number of days difference (**7**).
-{% renderFlow %}
+::render-flow
+```json
[{"id":"48b48dea5aef0701","type":"group","z":"d7101f3a4d45deed","name":"Doing Math with Dates","style":{"label":true},"nodes":["a2137766a67faf67","bb370162520e95d7"],"x":108,"y":1733,"w":964,"h":254},{"id":"a2137766a67faf67","type":"group","z":"d7101f3a4d45deed","g":"48b48dea5aef0701","name":"Using Moment nodes","style":{"label":true},"nodes":["aa71711160b1b545","f76e05557f807562","bfd33b878fefe852"],"x":134,"y":1759,"w":912,"h":82},{"id":"aa71711160b1b545","type":"inject","z":"d7101f3a4d45deed","g":"a2137766a67faf67","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":1800,"wires":[["f76e05557f807562"]]},{"id":"f76e05557f807562","type":"moment","z":"d7101f3a4d45deed","g":"a2137766a67faf67","name":"+ 1 days","topic":"","input":"payload","inputType":"msg","inTz":"Africa/Abidjan","adjAmount":"1","adjType":"days","adjDir":"add","format":"YYYY-MM-DD","locale":"en-US","output":"payload","outputType":"msg","outTz":"Africa/Abidjan","x":430,"y":1800,"wires":[["bfd33b878fefe852"]]},{"id":"bfd33b878fefe852","type":"debug","z":"d7101f3a4d45deed","g":"a2137766a67faf67","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":940,"y":1800,"wires":[]},{"id":"bb370162520e95d7","type":"group","z":"d7101f3a4d45deed","g":"48b48dea5aef0701","name":"Using JSONata","style":{"label":true},"nodes":["551b26f1bfda2d88","4881ffafeb5da64c","82c0370128a1f80d","29f632a5c68ecc55"],"x":134,"y":1879,"w":912,"h":82},{"id":"551b26f1bfda2d88","type":"inject","z":"d7101f3a4d45deed","g":"bb370162520e95d7","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":1920,"wires":[["82c0370128a1f80d"]]},{"id":"4881ffafeb5da64c","type":"debug","z":"d7101f3a4d45deed","g":"bb370162520e95d7","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"days_difference","targetType":"msg","statusVal":"","statusType":"auto","x":940,"y":1920,"wires":[]},{"id":"82c0370128a1f80d","type":"change","z":"d7101f3a4d45deed","g":"bb370162520e95d7","name":"Set Start and End Time","rules":[{"t":"move","p":"payload","pt":"msg","to":"end_time","tot":"msg"},{"t":"set","p":"start_time","pt":"msg","to":"msg.end_time - (7 * 86400000)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":1920,"wires":[["29f632a5c68ecc55"]]},{"id":"29f632a5c68ecc55","type":"change","z":"d7101f3a4d45deed","g":"bb370162520e95d7","name":"Difference in Days","rules":[{"t":"set","p":"days_difference","pt":"msg","to":"(msg.end_time - msg.start_time) / 86400000","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":1920,"wires":[["4881ffafeb5da64c"]]},{"id":"ccc081e4ccaba879","type":"global-config","env":[],"modules":{"node-red-contrib-moment":"5.0.0"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/getting-started/editor/index.md b/nuxt/content-guides/node-red/getting-started/editor/README.md
similarity index 84%
rename from src/node-red/getting-started/editor/index.md
rename to nuxt/content-guides/node-red/getting-started/editor/README.md
index 27862cacb3..d77cb136f2 100644
--- a/src/node-red/getting-started/editor/index.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/README.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: Editor
- order: 1
- parent: Getting Started
+title: Getting Started with the Node-RED Editor
+navTitle: Getting started
+navOrder: 2
meta:
- title: Getting Started with the Node-RED Editor
- description: Learn about the powerful features of Node-RED Editor.
+ description: Learn about the powerful features of Node-RED Editor.
keywords: node red editor, node-red editor, nodered flow, node-red flows, nodered workspace
---
-# {{meta.title}}
+# Getting Started with the Node-RED Editor
The Node-RED Editor is one of the most essential components of Node-RED. As the main focus of Node-RED is to enable visual programming, the editor provides a graphical interface that allows users to create, configure, and manage flows easily.
diff --git a/src/node-red/getting-started/editor/header.md b/nuxt/content-guides/node-red/getting-started/editor/header.md
similarity index 97%
rename from src/node-red/getting-started/editor/header.md
rename to nuxt/content-guides/node-red/getting-started/editor/header.md
index 0a0301f9e7..6872ec3971 100644
--- a/src/node-red/getting-started/editor/header.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/header.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: Header
- parent: Editor
+title: Node-RED Editor Header component
+navTitle: Header
+navOrder: 50
meta:
- title: Node-RED Editor Header component
- description: Explore the features available in the Node-RED Editor header component, essential for navigation and configuration.
+ description: Explore the features available in the Node-RED Editor header component, essential for navigation and configuration.
keywords: node-red, node-red editor
---
-# {{meta.title}}
+# Node-RED Editor Header component
The Node-RED editor header is a central component that facilitates navigation to Node-RED main settings, provides a deploy button for executing flows, and access to the user profile.
@@ -160,7 +159,7 @@ Clicking on this option will open the sidebars config tab that will allow you to
{data-zoomable}
-This option allows you to manage the [flow](/node-red/terminology/#flow) tabs.
+This option allows you to manage the [flow](/docs/node-red/terminology/#flow) tabs.
- **Add**: Adds a new flow tab.
- **Edit**: Edits the current flow tab.
@@ -170,7 +169,7 @@ This option allows you to manage the [flow](/node-red/terminology/#flow) tabs.
{data-zoomable}
-This option allows you to create the [subflow](/node-red/terminology/#subflow).
+This option allows you to create the [subflow](/docs/node-red/terminology/#subflow).
- **Create Subflow**: Creates a new subflow tab.
- **Selection to Subflow**: Converts the selected nodes into a subflow.
diff --git a/src/node-red/getting-started/editor/images/adding-nodes-to-group.webm b/nuxt/content-guides/node-red/getting-started/editor/images/adding-nodes-to-group.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/adding-nodes-to-group.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/adding-nodes-to-group.webm
diff --git a/src/node-red/getting-started/editor/images/creating-group.webm b/nuxt/content-guides/node-red/getting-started/editor/images/creating-group.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/creating-group.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/creating-group.webm
diff --git a/src/node-red/getting-started/editor/images/deleting-wires.webm b/nuxt/content-guides/node-red/getting-started/editor/images/deleting-wires.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/deleting-wires.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/deleting-wires.webm
diff --git a/src/node-red/getting-started/editor/images/deploy-button-active.png b/nuxt/content-guides/node-red/getting-started/editor/images/deploy-button-active.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/deploy-button-active.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/deploy-button-active.png
diff --git a/src/node-red/getting-started/editor/images/deploy-button-inactive.png b/nuxt/content-guides/node-red/getting-started/editor/images/deploy-button-inactive.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/deploy-button-inactive.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/deploy-button-inactive.png
diff --git a/src/node-red/getting-started/editor/images/deploy-button-options.png b/nuxt/content-guides/node-red/getting-started/editor/images/deploy-button-options.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/deploy-button-options.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/deploy-button-options.png
diff --git a/src/node-red/getting-started/editor/images/detaching-nodes-while-keeping-wires.webm b/nuxt/content-guides/node-red/getting-started/editor/images/detaching-nodes-while-keeping-wires.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/detaching-nodes-while-keeping-wires.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/detaching-nodes-while-keeping-wires.webm
diff --git a/src/node-red/getting-started/editor/images/editor-flow-tabs.png b/nuxt/content-guides/node-red/getting-started/editor/images/editor-flow-tabs.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/editor-flow-tabs.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/editor-flow-tabs.png
diff --git a/src/node-red/getting-started/editor/images/editor-workspace.png b/nuxt/content-guides/node-red/getting-started/editor/images/editor-workspace.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/editor-workspace.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/editor-workspace.png
diff --git a/src/node-red/getting-started/editor/images/environment-variables-options.png b/nuxt/content-guides/node-red/getting-started/editor/images/environment-variables-options.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/environment-variables-options.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/environment-variables-options.png
diff --git a/src/node-red/getting-started/editor/images/group-description.png b/nuxt/content-guides/node-red/getting-started/editor/images/group-description.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/group-description.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/group-description.png
diff --git a/src/node-red/getting-started/editor/images/group-level-env.png b/nuxt/content-guides/node-red/getting-started/editor/images/group-level-env.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/group-level-env.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/group-level-env.png
diff --git a/src/node-red/getting-started/editor/images/group-merging-groups.webm b/nuxt/content-guides/node-red/getting-started/editor/images/group-merging-groups.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/group-merging-groups.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/group-merging-groups.webm
diff --git a/src/node-red/getting-started/editor/images/group-properties-edit.png b/nuxt/content-guides/node-red/getting-started/editor/images/group-properties-edit.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/group-properties-edit.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/group-properties-edit.png
diff --git a/src/node-red/getting-started/editor/images/group-properties.png b/nuxt/content-guides/node-red/getting-started/editor/images/group-properties.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/group-properties.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/group-properties.png
diff --git a/src/node-red/getting-started/editor/images/group.png b/nuxt/content-guides/node-red/getting-started/editor/images/group.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/group.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/group.png
diff --git a/src/node-red/getting-started/editor/images/header-flowfuse-instance.png b/nuxt/content-guides/node-red/getting-started/editor/images/header-flowfuse-instance.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/header-flowfuse-instance.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/header-flowfuse-instance.png
diff --git a/src/node-red/getting-started/editor/images/header.png b/nuxt/content-guides/node-red/getting-started/editor/images/header.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/header.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/header.png
diff --git a/src/node-red/getting-started/editor/images/lasso-tool.webm b/nuxt/content-guides/node-red/getting-started/editor/images/lasso-tool.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/lasso-tool.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/lasso-tool.webm
diff --git a/src/node-red/getting-started/editor/images/main-menu-arrange-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-arrange-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-arrange-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-arrange-option.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-configuration-nodes.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-configuration-nodes.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-configuration-nodes.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-configuration-nodes.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-edit-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-edit-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-edit-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-edit-option.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-export-local.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-export-local.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-export-local.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-export-local.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-export-team.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-export-team.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-export-team.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-export-team.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-export.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-export.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-export.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-export.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-flows.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-flows.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-flows.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-flows.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-groups.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-groups.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-groups.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-groups.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-import.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-import.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-import.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-import.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-search-flow-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-search-flow-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-search-flow-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-search-flow-option.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-search-tab.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-search-tab.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-search-tab.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-search-tab.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-settings.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-settings.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-settings.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-settings.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-subflows.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-subflows.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-subflows.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-subflows.png
diff --git a/src/node-red/getting-started/editor/images/main-menu-view-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/main-menu-view-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/main-menu-view-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/main-menu-view-option.png
diff --git a/src/node-red/getting-started/editor/images/moving-multiple-wires.webm b/nuxt/content-guides/node-red/getting-started/editor/images/moving-multiple-wires.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/moving-multiple-wires.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/moving-multiple-wires.webm
diff --git a/src/node-red/getting-started/editor/images/moving-wire.webm b/nuxt/content-guides/node-red/getting-started/editor/images/moving-wire.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/moving-wire.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/moving-wire.webm
diff --git a/src/node-red/getting-started/editor/images/mqtt-in-node-status.png b/nuxt/content-guides/node-red/getting-started/editor/images/mqtt-in-node-status.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/mqtt-in-node-status.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/mqtt-in-node-status.png
diff --git a/src/node-red/getting-started/editor/images/node-add-config-node.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-add-config-node.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-add-config-node.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-add-config-node.png
diff --git a/src/node-red/getting-started/editor/images/node-appearance-tab.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-appearance-tab.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-appearance-tab.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-appearance-tab.png
diff --git a/src/node-red/getting-started/editor/images/node-buttons.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-buttons.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-buttons.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-buttons.png
diff --git a/src/node-red/getting-started/editor/images/node-description-tab.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-description-tab.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-description-tab.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-description-tab.png
diff --git a/src/node-red/getting-started/editor/images/node-edit-properties-tab.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-edit-properties-tab.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-edit-properties-tab.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-edit-properties-tab.png
diff --git a/src/node-red/getting-started/editor/images/node-enable-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-enable-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-enable-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-enable-option.png
diff --git a/src/node-red/getting-started/editor/images/node-help.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-help.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-help.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-help.png
diff --git a/src/node-red/getting-started/editor/images/node-indicating-error.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-indicating-error.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-indicating-error.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-indicating-error.png
diff --git a/src/node-red/getting-started/editor/images/node-indicating-how-many-nodes-using-config-node.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-indicating-how-many-nodes-using-config-node.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-indicating-how-many-nodes-using-config-node.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-indicating-how-many-nodes-using-config-node.png
diff --git a/src/node-red/getting-started/editor/images/node-indicating-undeployed-changes.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-indicating-undeployed-changes.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-indicating-undeployed-changes.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-indicating-undeployed-changes.png
diff --git a/src/node-red/getting-started/editor/images/node-input-ouput-port.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-input-ouput-port.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-input-ouput-port.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-input-ouput-port.png
diff --git a/src/node-red/getting-started/editor/images/node-quick-add-dialog.webm b/nuxt/content-guides/node-red/getting-started/editor/images/node-quick-add-dialog.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-quick-add-dialog.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-quick-add-dialog.webm
diff --git a/src/node-red/getting-started/editor/images/node-red-editor-palette.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-editor-palette.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-editor-palette.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-editor-palette.png
diff --git a/src/node-red/getting-started/editor/images/node-red-editor-window.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-editor-window.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-editor-window.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-editor-window.png
diff --git a/src/node-red/getting-started/editor/images/node-red-export-local-library.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-export-local-library.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-export-local-library.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-export-local-library.png
diff --git a/src/node-red/getting-started/editor/images/node-red-node.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-node.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-node.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-node.png
diff --git a/src/node-red/getting-started/editor/images/node-red-palette-category.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-category.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-palette-category.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-category.png
diff --git a/src/node-red/getting-started/editor/images/node-red-palette-collapse-expand.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-collapse-expand.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-palette-collapse-expand.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-collapse-expand.png
diff --git a/src/node-red/getting-started/editor/images/node-red-palette-export-scope.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-export-scope.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-palette-export-scope.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-export-scope.png
diff --git a/src/node-red/getting-started/editor/images/node-red-palette-json.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-json.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-palette-json.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-json.png
diff --git a/src/node-red/getting-started/editor/images/node-red-palette-search.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-search.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-palette-search.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-search.png
diff --git a/src/node-red/getting-started/editor/images/node-red-palette-toggle.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-toggle.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-palette-toggle.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-palette-toggle.png
diff --git a/src/node-red/getting-started/editor/images/node-red-pallete-manger-nodes-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-pallete-manger-nodes-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-pallete-manger-nodes-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-pallete-manger-nodes-option.png
diff --git a/src/node-red/getting-started/editor/images/node-red-sidebar.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-red-sidebar.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-red-sidebar.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-red-sidebar.png
diff --git a/src/node-red/getting-started/editor/images/node-wire.png b/nuxt/content-guides/node-red/getting-started/editor/images/node-wire.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/node-wire.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/node-wire.png
diff --git a/src/node-red/getting-started/editor/images/pallete-manger-filter-and-sort-options.png b/nuxt/content-guides/node-red/getting-started/editor/images/pallete-manger-filter-and-sort-options.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/pallete-manger-filter-and-sort-options.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/pallete-manger-filter-and-sort-options.png
diff --git a/src/node-red/getting-started/editor/images/removing-node-from-group.webm b/nuxt/content-guides/node-red/getting-started/editor/images/removing-node-from-group.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/removing-node-from-group.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/removing-node-from-group.webm
diff --git a/src/node-red/getting-started/editor/images/removing-nodes-from-group.webm b/nuxt/content-guides/node-red/getting-started/editor/images/removing-nodes-from-group.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/removing-nodes-from-group.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/removing-nodes-from-group.webm
diff --git a/src/node-red/getting-started/editor/images/removing-nodes-while-keeping-wires.webm b/nuxt/content-guides/node-red/getting-started/editor/images/removing-nodes-while-keeping-wires.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/removing-nodes-while-keeping-wires.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/removing-nodes-while-keeping-wires.webm
diff --git a/src/node-red/getting-started/editor/images/selecting-connected-nodes.webm b/nuxt/content-guides/node-red/getting-started/editor/images/selecting-connected-nodes.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/selecting-connected-nodes.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/selecting-connected-nodes.webm
diff --git a/src/node-red/getting-started/editor/images/selecting-downward-connected-nodes.webm b/nuxt/content-guides/node-red/getting-started/editor/images/selecting-downward-connected-nodes.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/selecting-downward-connected-nodes.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/selecting-downward-connected-nodes.webm
diff --git a/src/node-red/getting-started/editor/images/selecting-flows.webm b/nuxt/content-guides/node-red/getting-started/editor/images/selecting-flows.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/selecting-flows.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/selecting-flows.webm
diff --git a/src/node-red/getting-started/editor/images/selecting-upward-connected-nodes.webm b/nuxt/content-guides/node-red/getting-started/editor/images/selecting-upward-connected-nodes.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/selecting-upward-connected-nodes.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/selecting-upward-connected-nodes.webm
diff --git a/src/node-red/getting-started/editor/images/slicing-wires.webm b/nuxt/content-guides/node-red/getting-started/editor/images/slicing-wires.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/slicing-wires.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/slicing-wires.webm
diff --git a/src/node-red/getting-started/editor/images/subflow-appearance-tab.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-appearance-tab.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-appearance-tab.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-appearance-tab.png
diff --git a/src/node-red/getting-started/editor/images/subflow-converting-nodes-to-subflow.webm b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-converting-nodes-to-subflow.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-converting-nodes-to-subflow.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-converting-nodes-to-subflow.webm
diff --git a/src/node-red/getting-started/editor/images/subflow-custom-properties.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-custom-properties.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-custom-properties.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-custom-properties.png
diff --git a/src/node-red/getting-started/editor/images/subflow-delete-option.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-delete-option.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-delete-option.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-delete-option.png
diff --git a/src/node-red/getting-started/editor/images/subflow-editing-properties.webm b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-editing-properties.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-editing-properties.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-editing-properties.webm
diff --git a/src/node-red/getting-started/editor/images/subflow-input-output.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-input-output.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-input-output.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-input-output.png
diff --git a/src/node-red/getting-started/editor/images/subflow-module-tab.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-module-tab.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-module-tab.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-module-tab.png
diff --git a/src/node-red/getting-started/editor/images/subflow-node.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-node.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-node.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-node.png
diff --git a/src/node-red/getting-started/editor/images/subflow-property-description.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-property-description.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-property-description.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-property-description.png
diff --git a/src/node-red/getting-started/editor/images/subflow-status-node.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-status-node.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-status-node.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-status-node.png
diff --git a/src/node-red/getting-started/editor/images/subflow-ui-preview.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-ui-preview.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-ui-preview.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-ui-preview.png
diff --git a/src/node-red/getting-started/editor/images/subflow-window.png b/nuxt/content-guides/node-red/getting-started/editor/images/subflow-window.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/subflow-window.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/subflow-window.png
diff --git a/src/node-red/getting-started/editor/images/wiring-nodes-together.webm b/nuxt/content-guides/node-red/getting-started/editor/images/wiring-nodes-together.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/wiring-nodes-together.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/wiring-nodes-together.webm
diff --git a/src/node-red/getting-started/editor/images/workspace-add-flow.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-add-flow.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-add-flow.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-add-flow.png
diff --git a/src/node-red/getting-started/editor/images/workspace-delete-flow-dialog.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-delete-flow-dialog.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-delete-flow-dialog.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-delete-flow-dialog.png
diff --git a/src/node-red/getting-started/editor/images/workspace-delete-flow.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-delete-flow.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-delete-flow.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-delete-flow.png
diff --git a/src/node-red/getting-started/editor/images/workspace-disable-enable-flow.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-disable-enable-flow.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-disable-enable-flow.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-disable-enable-flow.png
diff --git a/src/node-red/getting-started/editor/images/workspace-enable-disable-flow.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-enable-disable-flow.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-enable-disable-flow.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-enable-disable-flow.png
diff --git a/src/node-red/getting-started/editor/images/workspace-flow-edit.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-flow-edit.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-flow-edit.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-flow-edit.png
diff --git a/src/node-red/getting-started/editor/images/workspace-navigator-tool.webm b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-navigator-tool.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-navigator-tool.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-navigator-tool.webm
diff --git a/src/node-red/getting-started/editor/images/workspace-reordering-flow.webm b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-reordering-flow.webm
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-reordering-flow.webm
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-reordering-flow.webm
diff --git a/src/node-red/getting-started/editor/images/workspace-search-tool.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-search-tool.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-search-tool.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-search-tool.png
diff --git a/src/node-red/getting-started/editor/images/workspace-view-tool.png b/nuxt/content-guides/node-red/getting-started/editor/images/workspace-view-tool.png
similarity index 100%
rename from src/node-red/getting-started/editor/images/workspace-view-tool.png
rename to nuxt/content-guides/node-red/getting-started/editor/images/workspace-view-tool.png
diff --git a/src/node-red/getting-started/editor/palette.md b/nuxt/content-guides/node-red/getting-started/editor/palette.md
similarity index 92%
rename from src/node-red/getting-started/editor/palette.md
rename to nuxt/content-guides/node-red/getting-started/editor/palette.md
index e3983cd54f..6f1d5d5194 100644
--- a/src/node-red/getting-started/editor/palette.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/palette.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: Palette
- parent: Editor
+title: Node-RED Editor Palette
+navTitle: Palette
+navOrder: 50
meta:
- title: Node-RED Editor Palette
description: Explore the features available in the Node-RED Editor palette, essential for navigation and configuration.
keywords: node-red, node-red editor
---
-# {{meta.title}}
+# Node-RED Editor Palette
The Palette is the left sidebar that contains all available nodes, including core nodes and third-party nodes that are installed.
@@ -40,4 +39,4 @@ Clicking on the down arrow icon will expand all categories back as default.
{data-zoomable}
-Clicking this button hides the palette sidebar. To show it again, click on the button once more.
\ No newline at end of file
+Clicking this button hides the palette sidebar. To show it again, click on the button once more.
diff --git a/src/node-red/getting-started/editor/sidebar.md b/nuxt/content-guides/node-red/getting-started/editor/sidebar.md
similarity index 98%
rename from src/node-red/getting-started/editor/sidebar.md
rename to nuxt/content-guides/node-red/getting-started/editor/sidebar.md
index e28746cd33..85d1ce8fea 100644
--- a/src/node-red/getting-started/editor/sidebar.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/sidebar.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: Sidebar
- parent: Editor
+title: Node-RED Editor Sidebar component
+navTitle: Sidebar
+navOrder: 50
meta:
- title: Node-RED Editor Sidebar component
description: Explore the features available in the Node-RED Editor sidebar component.
keywords: node-red, node-red editor, sidebar
---
-# {{meta.title}}
+# Node-RED Editor Sidebar component
The sidebar is located on the right side of your Node-RED Editor. It contains a collection of different tools that make Node-RED easier to use, such as managing nodes, configuration, context storage, and more.
@@ -160,4 +159,4 @@ To resize the sidebar, hover the mouse over the sidebar's border until the curso
To switch between panels, click the expand icon in the top-right corner. This will open a menu with all the available panels.
-Alternatively, you can click on the small boxes with different icons at the top.
\ No newline at end of file
+Alternatively, you can click on the small boxes with different icons at the top.
diff --git a/src/node-red/getting-started/editor/workspace.md b/nuxt/content-guides/node-red/getting-started/editor/workspace.md
similarity index 89%
rename from src/node-red/getting-started/editor/workspace.md
rename to nuxt/content-guides/node-red/getting-started/editor/workspace.md
index ba6a0c58d8..3f0b97fb58 100644
--- a/src/node-red/getting-started/editor/workspace.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/workspace.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: Workspace
- parent: Editor
+title: Node-RED Editor Workspace
+navTitle: Workspace
+navOrder: 50
meta:
- title: Node-RED Editor Workspace
description: Explore the features available in the Node-RED Editor workspace component
keywords: node-red, node-red editor, workspace
---
-# {{meta.title}}
+# Node-RED Editor Workspace
The workspace is the main area in the editor where you build application flows by dropping nodes from the palette.
@@ -20,7 +19,7 @@ The workspace is the main area in the editor where you build application flows b
The workspace provides view tools at the footer in the right corner. This includes zoom in (`Ctrl` + `+`) and zoom out (`Ctrl` + `-`) buttons to control the view of the workspace and reset the zoom level to its default.
-
+
Additionally, it provides a view navigator that allows you to see a scaled-down view of the entire workspace. In this view, you can also see the currently visible area of the workspace in the editor. To jump to a specific workspace area, click on that area in the view navigator.
@@ -74,7 +73,7 @@ Alternatively, right-click on the flow tab and select "Disable/Enable" from the
### Reordering Flows
-
+
Flows can be reordered by clicking and dragging the flow tab to the desired position. More options for the flow tab menu can be accessed by clicking on the top-right dropdown arrow icon.
@@ -98,7 +97,7 @@ It will create the subflow window like a flow tab for you.
### Editing a Subflow
-
+
To open the subflow edit dialog, double-click on the subflow node, then click on the "edit template properties". You can give the name for that subflow, add the description by clicking on the top-right
@@ -130,7 +129,7 @@ To delete the subflow, click on the "delete subflow" button at the top of the su
### Converting Nodes into a Subflow
-
+
If you have nodes on the workspace and you want to create a subflow of them, you can select them by pressing the left mouse key and drawing a rectangle around them. Then click on subflow -> selection to subflow in the main menu.
@@ -184,7 +183,7 @@ Nodes can be added from the [Node-RED palette](./palette.md) by dragging them on
Node-RED Editor provides a quick and easy way to add nodes via the palette:
-
+
- Press `Ctrl` or `Command` and click on the workspace.
- Select the desired node from the dialog, which contains all available nodes from the main node palette.
@@ -253,7 +252,7 @@ The "wires" refer to the connections that link nodes together to define the flow
### Wiring Nodes Together
-
+
To connect the nodes using the wires, left-click on the node's output port and drag the wire to the destination input port. Additionally, if you press the `Ctrl`/`Command` and mouse left key on the input or output port you will not need to hold the left mouse key or any other button to drag the wire. To connect it to the destination port, press the left mouse key on destination port. If the `Ctrl`/`Command` key remains pressed after connecting to the destination port, and if that port's node has an output port, a new wire will be dragged.
@@ -261,23 +260,23 @@ The wires can be connected from the input port to the output port, not from the
### Deleting Wires
-
+
To delete wires, click the left mouse button to select the first wire. To select multiple wires, press and hold the `Ctrl\Command` key while clicking each wire with the left mouse button. If you use only the left mouse button, you can select only one wire at a time. After selecting the wires, press the 'delete' or 'backspace' key to delete them.
### Moving Wires
-
+
To disconnect the wire from the port, select the wire by clicking on it. Then press and hold the `Shift` key while the left mouse key is pressed on the port. When the mouse is dragged you'll see the wire disconnects from the port and can be connected to another port.
-
+
If a port has multiple wires connected to it, if none of them are selected when the button is pressed with the Shift key held, all of the wires will move.
### Slicing Wires
-
+
Wires can also be removed by slicing through them. You can do this by holding the `Alt`/`Option` key and then drawing the line for slicing by holding the left mouse key.
@@ -285,13 +284,13 @@ Wires can also be removed by slicing through them. You can do this by holding th
#### Keeping Wire While Deleting Node
-
+
To do that, press and hold the `Ctrl`/`Command` key, select the node by clicking the left mouse key, and then press the "delete" or "backspace" button.
#### Detaching Node from Wires
-
+
To use this option, you have to set the [keyboard shortcut](./header.md#keyboard-shortcuts) for the "detach-selected-nodes" action.
@@ -303,7 +302,7 @@ In Node-RED, groups can be created for better organization, containing a single
### Creating a Flow Group
-
+
To create a flow group, select nodes (by holding the Ctrl key or drawing a rectangle around them). Navigate to `Groups -> Group selection` in the main menu or press `Ctrl + Shift + G`.
@@ -329,25 +328,25 @@ To add a description to the group, double-click on it. Select the third option f
### Adding Nodes to a Group
-
+
To add nodes to an existing group, drag and drop them into the group. This can be done one node at a time. Groups can also be nested within each other in the same manner.
### Removing Nodes from a Group
-
+
To remove nodes from a group, select the nodes and navigate to `Groups -> Remove selection` in the main menu. Similarly, remove a group from another group. Alternatively, click on a node, hold the 'Alt' key, and drag it outside of the group.
### Merging Groups
-
+
To merge multiple groups into a single group, select the groups. Go to `Main Menu -> Groups -> Merge selection`.
### Ungrouping Selected Nodes
-
+
To ungroup nodes from a group, select the nodes. Go to `Main Menu -> Groups -> Ungroup selection`.
@@ -359,30 +358,30 @@ A node can be selected or deselected by clicking on it. To select multiple nodes
### Lasso Tool
-
+
Node-RED provides a lasso tool to make selection faster. To use the lasso tool, press the left mouse key and drag the cursor, then you can select multiple nodes by drawing a rectangle around them.
### Selecting Connected Nodes
-
+
To select all connected nodes to a specific node, press the `Shift` button and click on the middle of that node.
### Selecting All Upstream Nodes
-
+
To select all of the connected nodes that are before that specific node, press the `Shift` button and while holding it click on the left part of that node.
### Selecting All Downstream Nodes
-
+
To select all of the connected nodes that are after that specific node, press the `Shift` button and while holding it click on the right part of that node.
### Selecting Flows
-
+
To select the flow tabs, press the `Ctrl/Command` key and while holding it click on the flow tab you want to select. Now you can then delete, export, or copy them collectively.
diff --git a/src/node-red/getting-started/images/adding-breakpoints.webm b/nuxt/content-guides/node-red/getting-started/images/adding-breakpoints.webm
similarity index 100%
rename from src/node-red/getting-started/images/adding-breakpoints.webm
rename to nuxt/content-guides/node-red/getting-started/images/adding-breakpoints.webm
diff --git a/src/node-red/getting-started/images/breakpoint-debugging.webm b/nuxt/content-guides/node-red/getting-started/images/breakpoint-debugging.webm
similarity index 100%
rename from src/node-red/getting-started/images/breakpoint-debugging.webm
rename to nuxt/content-guides/node-red/getting-started/images/breakpoint-debugging.webm
diff --git a/src/node-red/getting-started/images/breakpoints-section.png b/nuxt/content-guides/node-red/getting-started/images/breakpoints-section.png
similarity index 100%
rename from src/node-red/getting-started/images/breakpoints-section.png
rename to nuxt/content-guides/node-red/getting-started/images/breakpoints-section.png
diff --git a/src/node-red/getting-started/images/debugger-controls.png b/nuxt/content-guides/node-red/getting-started/images/debugger-controls.png
similarity index 100%
rename from src/node-red/getting-started/images/debugger-controls.png
rename to nuxt/content-guides/node-red/getting-started/images/debugger-controls.png
diff --git a/src/node-red/getting-started/images/disable-enable-button.png b/nuxt/content-guides/node-red/getting-started/images/disable-enable-button.png
similarity index 100%
rename from src/node-red/getting-started/images/disable-enable-button.png
rename to nuxt/content-guides/node-red/getting-started/images/disable-enable-button.png
diff --git a/src/node-red/getting-started/images/disabling-breakpoints.webm b/nuxt/content-guides/node-red/getting-started/images/disabling-breakpoints.webm
similarity index 100%
rename from src/node-red/getting-started/images/disabling-breakpoints.webm
rename to nuxt/content-guides/node-red/getting-started/images/disabling-breakpoints.webm
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-change-node.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-change-node.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-change-node.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-change-node.png
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-calculating-avg.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-calculating-avg.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-calculating-avg.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-calculating-avg.png
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-combining-node.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-combining-node.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-combining-node.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-join-node-combining-node.png
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-sort-node.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-sort-node.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-sort-node.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-sort-node.png
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-switch-node.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-switch-node.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-switch-node.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-data-with-node-red-switch-node.png
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-switch-node.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-switch-node.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-switch-node.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-switch-node.png
diff --git a/src/node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png b/nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png
similarity index 100%
rename from src/node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png
rename to nuxt/content-guides/node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png
diff --git a/src/node-red/getting-started/images/if-else-with-node-red.png b/nuxt/content-guides/node-red/getting-started/images/if-else-with-node-red.png
similarity index 100%
rename from src/node-red/getting-started/images/if-else-with-node-red.png
rename to nuxt/content-guides/node-red/getting-started/images/if-else-with-node-red.png
diff --git a/src/node-red/getting-started/images/message-section.png b/nuxt/content-guides/node-red/getting-started/images/message-section.png
similarity index 100%
rename from src/node-red/getting-started/images/message-section.png
rename to nuxt/content-guides/node-red/getting-started/images/message-section.png
diff --git a/src/node-red/getting-started/images/mistake-1-solution.webm b/nuxt/content-guides/node-red/getting-started/images/mistake-1-solution.webm
similarity index 100%
rename from src/node-red/getting-started/images/mistake-1-solution.webm
rename to nuxt/content-guides/node-red/getting-started/images/mistake-1-solution.webm
diff --git a/src/node-red/getting-started/images/mistake1.webm b/nuxt/content-guides/node-red/getting-started/images/mistake1.webm
similarity index 100%
rename from src/node-red/getting-started/images/mistake1.webm
rename to nuxt/content-guides/node-red/getting-started/images/mistake1.webm
diff --git a/src/node-red/getting-started/images/node-red-data-types.webm b/nuxt/content-guides/node-red/getting-started/images/node-red-data-types.webm
similarity index 100%
rename from src/node-red/getting-started/images/node-red-data-types.webm
rename to nuxt/content-guides/node-red/getting-started/images/node-red-data-types.webm
diff --git a/src/node-red/getting-started/images/node-red-instance-update.png b/nuxt/content-guides/node-red/getting-started/images/node-red-instance-update.png
similarity index 100%
rename from src/node-red/getting-started/images/node-red-instance-update.png
rename to nuxt/content-guides/node-red/getting-started/images/node-red-instance-update.png
diff --git a/src/node-red/getting-started/images/node-red-message-passing.webm b/nuxt/content-guides/node-red/getting-started/images/node-red-message-passing.webm
similarity index 100%
rename from src/node-red/getting-started/images/node-red-message-passing.webm
rename to nuxt/content-guides/node-red/getting-started/images/node-red-message-passing.webm
diff --git a/src/node-red/getting-started/images/proceeding-further-execution.webm b/nuxt/content-guides/node-red/getting-started/images/proceeding-further-execution.webm
similarity index 100%
rename from src/node-red/getting-started/images/proceeding-further-execution.webm
rename to nuxt/content-guides/node-red/getting-started/images/proceeding-further-execution.webm
diff --git a/src/node-red/getting-started/images/removing-breakpoints.webm b/nuxt/content-guides/node-red/getting-started/images/removing-breakpoints.webm
similarity index 100%
rename from src/node-red/getting-started/images/removing-breakpoints.webm
rename to nuxt/content-guides/node-red/getting-started/images/removing-breakpoints.webm
diff --git a/src/node-red/getting-started/images/schedule-update.png b/nuxt/content-guides/node-red/getting-started/images/schedule-update.png
similarity index 100%
rename from src/node-red/getting-started/images/schedule-update.png
rename to nuxt/content-guides/node-red/getting-started/images/schedule-update.png
diff --git a/src/node-red/getting-started/images/update-node-red-cta-1.png b/nuxt/content-guides/node-red/getting-started/images/update-node-red-cta-1.png
similarity index 100%
rename from src/node-red/getting-started/images/update-node-red-cta-1.png
rename to nuxt/content-guides/node-red/getting-started/images/update-node-red-cta-1.png
diff --git a/src/node-red/getting-started/library/index.md b/nuxt/content-guides/node-red/getting-started/library/README.md
similarity index 81%
rename from src/node-red/getting-started/library/index.md
rename to nuxt/content-guides/node-red/getting-started/library/README.md
index 2189eb298a..8b22c126b3 100644
--- a/src/node-red/getting-started/library/index.md
+++ b/nuxt/content-guides/node-red/getting-started/library/README.md
@@ -1,13 +1,10 @@
---
-metaTitle: "Node-RED Library: Curated List of Nodes"
-eleventyNavigation:
- key: Node-RED Library
- parent: Getting Started
- order: 3
+title: Node-RED Library – A Curated and Actively Maintained List of Nodes
+navTitle: Getting started
+navOrder: 2
meta:
- title: Node-RED Library – A Curated and Actively Maintained List of Nodes
- description: Browse the Node-RED Library for community-built nodes and integrations. FlowFuse's curated catalog offers tested, documented, enterprise-ready solutions with professional support for critical deployments.
- keywords: node-red library, node-red nodes, node-red integrations, community nodes, node-red packages, custom nodes, node-red flows
+ description: Browse the Node-RED Library for community-built nodes and integrations. FlowFuse's curated catalog offers tested, documented, enterprise-ready solutions with professional support for critical deployments.
+ keywords: node-red library, node-red nodes, node-red integrations, community nodes, node-red packages, custom nodes, node-red flows
---
# Node-RED Library
@@ -47,7 +44,7 @@ To install nodes through the Node-RED editor:
5. Search for the required node
6. Click **Install** next to the package
-
+
Installed nodes appear in your palette immediately and are ready for use in your flows.
diff --git a/src/node-red/getting-started/library/images/installing-node-red-node.webm b/nuxt/content-guides/node-red/getting-started/library/images/installing-node-red-node.webm
similarity index 100%
rename from src/node-red/getting-started/library/images/installing-node-red-node.webm
rename to nuxt/content-guides/node-red/getting-started/library/images/installing-node-red-node.webm
diff --git a/src/node-red/getting-started/node-red-android.md b/nuxt/content-guides/node-red/getting-started/node-red-android.md
similarity index 96%
rename from src/node-red/getting-started/node-red-android.md
rename to nuxt/content-guides/node-red/getting-started/node-red-android.md
index a1e90b5bfb..7d2f6954ec 100644
--- a/src/node-red/getting-started/node-red-android.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-android.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: Node-RED On Android
- order: 4
- parent: Getting Started
+title: Installing Node-RED on Android
+navTitle: Node-RED On Android
+navOrder: 4
meta:
- title: Installing Node-RED on Android
description: Learn how to install and run Node-RED on Android devices using Termux
keywords: node-red android, termux, android installation, mobile node-red
---
-# {{ meta.title }}
+# Installing Node-RED on Android
You can run Node-RED on Android devices using Termux, a terminal emulator and Linux environment app. This guide walks you through the installation process and helps you get Node-RED running on your Android phone or tablet.
diff --git a/src/node-red/getting-started/node-red-messages.md b/nuxt/content-guides/node-red/getting-started/node-red-messages.md
similarity index 95%
rename from src/node-red/getting-started/node-red-messages.md
rename to nuxt/content-guides/node-red/getting-started/node-red-messages.md
index 9eab49a2de..04f49cc7f3 100644
--- a/src/node-red/getting-started/node-red-messages.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-messages.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: Working with Messages
- order: 5
- parent: Getting Started
+title: Understanding Node-RED Messages
+navTitle: Working with Messages
+navOrder: 5
meta:
- title: Understanding Node-RED Messages
description: A comprehensive guide to working with Node-RED messages, ensuring error-free flows and optimized data handling.
keywords: msg.payload node red, node red message structure, node-red messages, node red msg.payload, msg.payload, node red msg.payload array
---
-# {{ meta.title }}
+# Understanding Node-RED Messages
Node-RED operates by passing messages between nodes to create dynamic IoT, automation, and data-processing workflows. Each message transports data that nodes read, modify, process, or analyze. Understanding message structure and handling is essential for building reliable flows. Poor message management can cause subtle bugs like data overwrites, infinite loops, or system crashes.
@@ -19,7 +17,7 @@ This guide explores Node-RED message mechanics, common pitfalls, and best practi
Messages in Node-RED are data packets that flow between nodes in your workflow. Node-RED follows an event-driven architecture where nodes act as both event emitters and listeners, with messages serving as the communication medium between them.
-
+
_Node-RED message passing visualization_
Messages carry the data that powers your workflows, sensor readings, user inputs, API responses, and more. Fundamentally, Node-RED messages are JavaScript objects, providing a flexible structure for managing and transferring data throughout your flows.
@@ -69,7 +67,7 @@ Message property values can be any JavaScript-supported data type:
- [Buffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
- Other complex data types
-
+
_Various data types supported by Node-RED_
## How to Clone Messages and Properties
@@ -245,7 +243,7 @@ Steps:
3. Set the action to "Always convert to JavaScript Object" and click Done.
4. Connect the JSON node between the source node (sending the JSON string) and the destination node (requiring the parsed object).
-The JSON node automatically converts incoming JSON strings into JavaScript objects. For more details, see the [JSON node documentation](/node-red/core-nodes/json/).
+The JSON node automatically converts incoming JSON strings into JavaScript objects. For more details, see the [JSON node documentation](/docs/node-red/core-nodes/json/).
## Common Mistakes to Avoid
@@ -271,10 +269,10 @@ return msg;
This commonly occurs when an Inject node sends `msg.payload` as a string or number, then a Change node attempts to add properties to it.
-
+
_Incorrect: Adding properties to a non-object type_
-
+
_Correct: Initialize as object before adding properties_
### 2. Overwriting the Entire Message Object
@@ -324,4 +322,4 @@ msg.payload = msg.payload * 2;
return msg; // Flow continues
```
-By mastering Node-RED message handling and avoiding common mistakes, you can build robust, efficient workflows. Understanding JSON conversion, message cloning, and proper property management ensures smooth data flow between nodes and maintainable, error-free applications.
\ No newline at end of file
+By mastering Node-RED message handling and avoiding common mistakes, you can build robust, efficient workflows. Understanding JSON conversion, message cloning, and proper property management ensures smooth data flow between nodes and maintainable, error-free applications.
diff --git a/src/node-red/getting-started/node-red-port.md b/nuxt/content-guides/node-red/getting-started/node-red-port.md
similarity index 96%
rename from src/node-red/getting-started/node-red-port.md
rename to nuxt/content-guides/node-red/getting-started/node-red-port.md
index d42a2394ab..fa8778f9f3 100644
--- a/src/node-red/getting-started/node-red-port.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-port.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: Port Configuration
- order: 2
- parent: Getting Started
+title: Node-RED Port (localhost:1880)
+navTitle: Port Configuration
+navOrder: 2
meta:
- title: Node-RED Port (localhost:1880)
description: Learn how to configure Node-RED ports, change default settings, secure your installation, and set up remote access with FlowFuse.
keywords: Node-RED port, port 1880, change Node-RED port, Node-RED security, remote access, FlowFuse, port configuration
---
-# {{ meta.title }}
+# Node-RED Port (localhost:1880)
Node-RED runs on **port 1880** by default. This is the network port where Node-RED listens for connections. When you start Node-RED, you access the editor by opening `http://localhost:1880` in your browser. All your HTTP endpoints and the Node-RED interface are served through this port.
diff --git a/nuxt/content-guides/node-red/getting-started/programming/README.md b/nuxt/content-guides/node-red/getting-started/programming/README.md
new file mode 100644
index 0000000000..e6611c8828
--- /dev/null
+++ b/nuxt/content-guides/node-red/getting-started/programming/README.md
@@ -0,0 +1,21 @@
+---
+title: Node-RED Programming
+navTitle: Getting started
+navOrder: 2
+meta:
+ description: Master Node-RED programming fundamentals including flows, nodes, messages, conditional logic, and data manipulation. Learn essential concepts for building sophisticated visual programming solutions.
+ keywords: Node-RED programming, visual programming, Node-RED flows, Node-RED nodes, message passing, conditional logic, data manipulation, flow-based programming, Node-RED tutorial, IoT programming
+---
+
+# Node-RED Programming
+
+Learn the core programming concepts you'll use every day in Node-RED. This section covers the fundamental building blocks that will help you create more sophisticated and reliable flows.
+
+## What's Covered
+
+Programming in Node-RED means working with flows, nodes, and messages. Even though you're working visually, you'll still need to understand key programming concepts like conditional logic, loops, and data manipulation. This section teaches you how to implement these concepts using Node-RED's visual approach.
+
+- [How to Debug Node-RED Flows Using Debugger](/docs/node-red/getting-started/programming/debugging-flows/): Debug Node-RED flows using the Debugger. Learn to set breakpoints, step through execution, and inspect messages for efficient troubleshooting.
+- [How to Filter, Map, Sort, and Reduce Data in Node-RED](/docs/node-red/getting-started/programming/data-tranformation/): Learn how to perform data transformation in Node-RED with a low-code approach.
+- [How to implement loops in Node-RED flows](/docs/node-red/getting-started/programming/loop/): Learn how to implement while, for, and for...of loops in Node-RED with core and custom nodes for efficient data processing and automation.
+- [How to Use If-Else Logic in Node-RED](/docs/node-red/getting-started/programming/if-else/): Learn how to implement If-Else logic in Node-RED with our step-by-step guide. Use Function and Switch nodes for dynamic, conditional flows.
diff --git a/src/node-red/getting-started/programming/data-tranformation.md b/nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md
similarity index 97%
rename from src/node-red/getting-started/programming/data-tranformation.md
rename to nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md
index 8ef74b5a7d..8cc235e160 100644
--- a/src/node-red/getting-started/programming/data-tranformation.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: Data Tranformation
- parent: Programming
+title: How to Filter, Map, Sort, and Reduce Data in Node-RED
+navTitle: Data Tranformation
+navOrder: 50
meta:
- title: How to Filter, Map, Sort, and Reduce Data in Node-RED
- description: Learn how to perform data transformation in Node-RED with a low-code approach.
+ description: Learn how to perform data transformation in Node-RED with a low-code approach.
keywords: how to filter data in node-red, how to map array in node-red, how to map data in node-red, how to sort data in node-red, how to reduce data in node-red, data transformation in node-red
-image: /node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png
+image: /docs/node-red/getting-started/images/filtering-mapping-sorting-reducing-with-node-red.png
---
-# {{meta.title}}
+# How to Filter, Map, Sort, and Reduce Data in Node-RED
Data transformation is at the heart of most Node-RED applications, whether you're processing IoT sensor readings, cleaning API responses, or preparing data for visualization. While you could write JavaScript functions to handle these operations, Node-RED's visual, low-code approach offers a more maintainable and accessible alternative that anyone on your team can understand and modify.
@@ -92,9 +91,11 @@ Filter, map, sort, and reduce are essential functions in data processing because
However, you've noticed that the temperature data is in Kelvin, but you need it in Celsius. Additionally, the data is not correctly ordered by timestamp, and you only need the data of June 17th. Finally, you want to calculate the average temperature for that day. Users who are not familiar with Node-RED basics can use a JavaScript function node to achieve this, as shown below:
-{% renderFlow %}
+::render-flow
+```json
[{"id":"306d455509a3747e","type":"inject","z":"977143edb097b685","name":"Inject the sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"timestamp\":\"2024-06-17T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-17T11:00:00Z\",\"temperature\":299.15},{\"timestamp\":\"2024-06-17T10:30:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-17T10:15:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-17T10:45:00Z\",\"temperature\":303.15},{\"timestamp\":\"2024-06-18T09:00:00Z\",\"temperature\":297.15},{\"timestamp\":\"2024-06-18T10:00:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-18T11:00:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-18T12:00:00Z\",\"temperature\":302.15},{\"timestamp\":\"2024-06-19T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-19T11:00:00Z\",\"temperature\":299.15}]","payloadType":"json","x":260,"y":200,"wires":[["c58e1653fe5511eb"]]},{"id":"c58e1653fe5511eb","type":"function","z":"977143edb097b685","name":"Filtering, mapping, reducing and sorting data with traditional coding","func":"let sensorData = msg.payload;\n\nconst filteredData = sensorData\n .filter(item => item.timestamp.startsWith(\"2024-06-17\"))\n .map(item => ({\n timestamp: item.timestamp,\n temperature: item.temperature - 273.15\n }));\n\nfilteredData.sort((a, b) => (a.timestamp > b.timestamp) ? 1 : ((b.timestamp > a.timestamp) ? -1 : 0));\n\nconst totalTemperature = filteredData.reduce((acc, entry) => acc + entry.temperature, 0);\nconst averageTemperature = totalTemperature / filteredData.length;\n\nmsg.payload = {\n sensorData: filteredData,\n averageTemperature: averageTemperature\n};\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":200,"wires":[["827c7d2009eeb046"]]},{"id":"827c7d2009eeb046","type":"debug","z":"977143edb097b685","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1060,"y":200,"wires":[]}]
-{% endrenderFlow %}
+```
+::
Using function nodes isn't wrong, but it adds complexity to your applications, for more information refer to the [Drawbacks of using Fuction nodes](/blog/2023/03/why-should-you-use-node-red-function-nodes/#_5-benefits-of-avoiding-function-nodes) Article. Since not everyone on the team may be familiar with JavaScript, it can limit who can solve business problems. To keep the application flow simple, using a low-code approach to perform these operations is crucial.
@@ -113,9 +114,11 @@ Mapping often refers to the process of applying a function to each item in a lis
{data-zoomable}
-{% renderFlow %}
+::render-flow
+```json
[{"id":"306d455509a3747e","type":"inject","z":"977143edb097b685","name":"Inject the sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[ {\"timestamp\":\"2024-06-17T10:00:00Z\",\"temperature\":298.15}, {\"timestamp\":\"2024-06-17T11:00:00Z\",\"temperature\":299.15}, {\"timestamp\":\"2024-06-17T10:30:00Z\",\"temperature\":300.15}, {\"timestamp\":\"2024-06-17T10:15:00Z\",\"temperature\":301.15}, {\"timestamp\":\"2024-06-17T10:45:00Z\",\"temperature\":303.15}, {\"timestamp\":\"2024-06-18T09:00:00Z\",\"temperature\":297.15}, {\"timestamp\":\"2024-06-18T10:00:00Z\",\"temperature\":300.15}, {\"timestamp\":\"2024-06-18T11:00:00Z\",\"temperature\":301.15}, {\"timestamp\":\"2024-06-18T12:00:00Z\",\"temperature\":302.15}, {\"timestamp\":\"2024-06-19T10:00:00Z\",\"temperature\":298.15}, {\"timestamp\":\"2024-06-19T11:00:00Z\",\"temperature\":299.15} ]","payloadType":"json","x":500,"y":540,"wires":[["9d9c0688468e1aae"]]},{"id":"9d9c0688468e1aae","type":"split","z":"977143edb097b685","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":860,"y":540,"wires":[["785125a70fbdc554"]]},{"id":"785125a70fbdc554","type":"change","z":"977143edb097b685","name":"Converting the temperature data from kelvin to celsius","rules":[{"t":"set","p":"payload.temperature","pt":"msg","to":"payload.temperature - 273.15","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1340,"y":540,"wires":[["9cd6e05b88ec26fd"]]},{"id":"9cd6e05b88ec26fd","type":"join","z":"977143edb097b685","name":"Creating new array by combining message sequence","mode":"auto","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":1820,"y":540,"wires":[["244660d81bf5e5b2"]]},{"id":"244660d81bf5e5b2","type":"debug","z":"977143edb097b685","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":2140,"y":540,"wires":[]}]
-{% endrenderFlow %}
+```
+::
## Filtering
@@ -127,9 +130,11 @@ Filtering is the process of selecting specific items from an array to create a n
{data-zoomable}
-{% renderFlow %}
+::render-flow
+```json
[{"id":"4e76a2328451b4c3","type":"inject","z":"977143edb097b685","name":"Inject the sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"timestamp\":\"2024-06-17T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-17T11:00:00Z\",\"temperature\":299.15},{\"timestamp\":\"2024-06-17T10:30:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-17T10:15:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-17T10:45:00Z\",\"temperature\":303.15},{\"timestamp\":\"2024-06-18T09:00:00Z\",\"temperature\":297.15},{\"timestamp\":\"2024-06-18T10:00:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-18T11:00:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-18T12:00:00Z\",\"temperature\":302.15},{\"timestamp\":\"2024-06-19T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-19T11:00:00Z\",\"temperature\":299.15}]","payloadType":"json","x":380,"y":480,"wires":[["f4e07a31f505a50c"]]},{"id":"f4e07a31f505a50c","type":"split","z":"977143edb097b685","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":680,"y":480,"wires":[["fcd6a0a1497203a9"]]},{"id":"bed1a7d861fa9e3d","type":"join","z":"977143edb097b685","name":"Creating new array by combining message sequence","mode":"auto","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"0","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":1940,"y":480,"wires":[["2449811ec79bc220"]]},{"id":"2449811ec79bc220","type":"debug","z":"977143edb097b685","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2240,"y":480,"wires":[]},{"id":"7797594a508cfb46","type":"switch","z":"977143edb097b685","name":"Routing message sequence based on condition","property":"payload.timestamp","propertyType":"msg","rules":[{"t":"cont","v":"2024-06-17","vt":"str"}],"checkall":"true","repair":true,"outputs":1,"x":1520,"y":480,"wires":[["bed1a7d861fa9e3d"]]},{"id":"fcd6a0a1497203a9","type":"change","z":"977143edb097b685","name":"Converting the temperature data from kelvin to celsius","rules":[{"t":"set","p":"payload.temperature","pt":"msg","to":"payload.temperature - 273.15","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":480,"wires":[["7797594a508cfb46"]]}]
-{% endrenderFlow %}
+```
+::
## Sorting
@@ -141,9 +146,11 @@ To perform sorting, we have to use the Node-RED Sort Node.
{data-zoomable}
-{% renderFlow %}
+::render-flow
+```json
[{"id":"8b66990baca45f2d","type":"inject","z":"977143edb097b685","name":"Inject the sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"timestamp\":\"2024-06-17T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-17T11:00:00Z\",\"temperature\":299.15},{\"timestamp\":\"2024-06-17T10:30:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-17T10:15:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-17T10:45:00Z\",\"temperature\":303.15},{\"timestamp\":\"2024-06-18T09:00:00Z\",\"temperature\":297.15},{\"timestamp\":\"2024-06-18T10:00:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-18T11:00:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-18T12:00:00Z\",\"temperature\":302.15},{\"timestamp\":\"2024-06-19T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-19T11:00:00Z\",\"temperature\":299.15}]","payloadType":"json","x":400,"y":540,"wires":[["f0d48b57cbd10fdd"]]},{"id":"f0d48b57cbd10fdd","type":"split","z":"977143edb097b685","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":740,"y":540,"wires":[["fdc21a267f7583e7"]]},{"id":"fdc21a267f7583e7","type":"change","z":"977143edb097b685","name":"Correcting the temperature property","rules":[{"t":"set","p":"payload.temperature","pt":"msg","to":"payload.temperature - 273.15","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":540,"wires":[["7d4ce2d4e21cd914"]]},{"id":"c273215c8c9cebee","type":"join","z":"977143edb097b685","name":"Creating new array by combining message sequence","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":1860,"y":540,"wires":[["c423efbd0581367a"]]},{"id":"e70d15ced7405755","type":"debug","z":"977143edb097b685","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2480,"y":540,"wires":[]},{"id":"7d4ce2d4e21cd914","type":"switch","z":"977143edb097b685","name":"Routing message sequence based on condition","property":"payload.timestamp","propertyType":"msg","rules":[{"t":"cont","v":"2024-06-17","vt":"str"}],"checkall":"true","repair":true,"outputs":1,"x":1440,"y":540,"wires":[["c273215c8c9cebee"]]},{"id":"c423efbd0581367a","type":"sort","z":"977143edb097b685","name":"Sorting data based on timestamp","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"timestamp","msgKeyType":"jsonata","seqKey":"payload.timestamp","seqKeyType":"jsonata","x":2240,"y":540,"wires":[["e70d15ced7405755"]]}]
-{% endrenderFlow %}
+```
+::
## Reducing
@@ -157,6 +164,8 @@ Reducing refers to the process of combining elements of a data structure (such a
In this configuration, the Join node is set to reduce sequence mode. The initial value of the accumulator ($A) is initialized to 0. As each message is processed, the current temperature (payload.temperature) is added to $A. Once all messages have been processed, the accumulated sum $A is divided by the total number of messages ($N) to compute the average temperature.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"5ff0902202c21e85","type":"inject","z":"977143edb097b685","name":"Inject the sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"timestamp\":\"2024-06-17T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-17T11:00:00Z\",\"temperature\":299.15},{\"timestamp\":\"2024-06-17T10:30:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-17T10:15:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-17T10:45:00Z\",\"temperature\":303.15},{\"timestamp\":\"2024-06-18T09:00:00Z\",\"temperature\":297.15},{\"timestamp\":\"2024-06-18T10:00:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-18T11:00:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-18T12:00:00Z\",\"temperature\":302.15},{\"timestamp\":\"2024-06-19T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-19T11:00:00Z\",\"temperature\":299.15}]","payloadType":"json","x":580,"y":620,"wires":[["993ffc096c3e8089"]]},{"id":"993ffc096c3e8089","type":"split","z":"977143edb097b685","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":900,"y":620,"wires":[["3bbb68c2dc2a0f5c"]]},{"id":"3bbb68c2dc2a0f5c","type":"change","z":"977143edb097b685","name":"Correcting the temperature property","rules":[{"t":"set","p":"payload.temperature","pt":"msg","to":"payload.temperature - 273.15","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1280,"y":620,"wires":[["580210c585730f97"]]},{"id":"01e7066b3ff012e7","type":"join","z":"977143edb097b685","name":"Creating new array by combining message sequence","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":2060,"y":620,"wires":[["27cc5d5e90f7facd","6116c1efc3f7f682"]]},{"id":"580210c585730f97","type":"switch","z":"977143edb097b685","name":"Routing message sequence based on condition","property":"payload.timestamp","propertyType":"msg","rules":[{"t":"cont","v":"2024-06-17","vt":"str"}],"checkall":"true","repair":true,"outputs":1,"x":1640,"y":620,"wires":[["01e7066b3ff012e7"]]},{"id":"27cc5d5e90f7facd","type":"sort","z":"977143edb097b685","name":"Sorting data based on timestamp","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"timestamp","msgKeyType":"jsonata","seqKey":"payload.timestamp","seqKeyType":"jsonata","x":2440,"y":620,"wires":[["f1f93a7b4575daf1"]]},{"id":"362ec9c482688cf6","type":"debug","z":"977143edb097b685","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":3140,"y":740,"wires":[]},{"id":"b9f2f83a330140ca","type":"join","z":"977143edb097b685","name":"Calculating the the average of temperature","mode":"reduce","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"","reduceRight":false,"reduceExp":"$A+ payload.temperature","reduceInit":"0","reduceInitType":"num","reduceFixup":"$A/$N","x":2890,"y":740,"wires":[["362ec9c482688cf6"]]},{"id":"f1f93a7b4575daf1","type":"debug","z":"977143edb097b685","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2720,"y":620,"wires":[]},{"id":"6116c1efc3f7f682","type":"split","z":"977143edb097b685","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":2460,"y":740,"wires":[["b9f2f83a330140ca"]]}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/getting-started/programming/debugging-flows.md b/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
similarity index 90%
rename from src/node-red/getting-started/programming/debugging-flows.md
rename to nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
index da2f030725..cf5c9f04d9 100644
--- a/src/node-red/getting-started/programming/debugging-flows.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
@@ -1,16 +1,15 @@
---
-eleventyNavigation:
- key: Debugging
- parent: Programming
+title: How to Debug Node-RED Flows Using Debugger
+navTitle: Debugging
+navOrder: 50
meta:
- title: How to Debug Node-RED Flows Using Debugger
description: Debug Node-RED flows using the Debugger. Learn to set breakpoints, step through execution, and inspect messages for efficient troubleshooting.
keywords: node-red-debugger, node red debug, nodered debug, node-red debug, how to debug nodered flows, how to debug node-red flows
---
-# {{meta.title}}
+# How to Debug Node-RED Flows Using Debugger
-When it comes to debugging application flows in Node-RED, the tool most Node-RED developers often reach for is the [Debug](/node-red/core-nodes/debug/) node. It provides a simple way to output message payloads or other data to the debug sidebar, helping you gain insights into how your flow is working. But what if you needed more control and visibility over the flow’s execution? What if you wanted to step through each node in detail, inspect variables, or pause the flow at specific points to understand what’s happening?
+When it comes to debugging application flows in Node-RED, the tool most Node-RED developers often reach for is the [Debug](/docs/node-red/core-nodes/debug/) node. It provides a simple way to output message payloads or other data to the debug sidebar, helping you gain insights into how your flow is working. But what if you needed more control and visibility over the flow’s execution? What if you wanted to step through each node in detail, inspect variables, or pause the flow at specific points to understand what’s happening?
In these cases, using the **Node-RED Debugger** becomes invaluable. The debugger allows you to trace the execution of your flows interactively, set breakpoints, and gain deeper insights beyond what the Debug node offers. This Documentation will show you how to effectively use the Node-RED Debugger to pinpoint issues and fine-tune your applications.
@@ -57,9 +56,11 @@ To illustrate how to use the Node-RED Debugger effectively, let’s consider a f
3. Create a new array from the filtered results.
4. Split the array and calculate the average temperature.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"3c012808d6b397e2","type":"group","z":"9cf82b68bb89e8ce","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["5ff0902202c21e85","993ffc096c3e8089","3bbb68c2dc2a0f5c","580210c585730f97","362ec9c482688cf6","b9f2f83a330140ca","6116c1efc3f7f682","01e7066b3ff012e7"],"x":394,"y":1899,"w":532,"h":642},{"id":"5ff0902202c21e85","type":"inject","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Inject the sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"timestamp\":\"2024-06-17T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-17T11:00:00Z\",\"temperature\":299.15},{\"timestamp\":\"2024-06-17T10:30:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-17T10:15:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-17T10:45:00Z\",\"temperature\":303.15},{\"timestamp\":\"2024-06-18T09:00:00Z\",\"temperature\":297.15},{\"timestamp\":\"2024-06-18T10:00:00Z\",\"temperature\":300.15},{\"timestamp\":\"2024-06-18T11:00:00Z\",\"temperature\":301.15},{\"timestamp\":\"2024-06-18T12:00:00Z\",\"temperature\":302.15},{\"timestamp\":\"2024-06-19T10:00:00Z\",\"temperature\":298.15},{\"timestamp\":\"2024-06-19T11:00:00Z\",\"temperature\":299.15}]","payloadType":"json","x":540,"y":1940,"wires":[["993ffc096c3e8089"]]},{"id":"993ffc096c3e8089","type":"split","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":640,"y":2020,"wires":[["3bbb68c2dc2a0f5c"]]},{"id":"3bbb68c2dc2a0f5c","type":"change","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Kelvin to celcius","rules":[{"t":"set","p":"payload.temperature","pt":"msg","to":"payload.temperature - 273.15","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":2100,"wires":[["580210c585730f97"]]},{"id":"580210c585730f97","type":"switch","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Routing message sequence based on condition","property":"payload.timestamp","propertyType":"msg","rules":[{"t":"cont","v":"2024-06-17","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":660,"y":2180,"wires":[["01e7066b3ff012e7"]]},{"id":"362ec9c482688cf6","type":"debug","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":650,"y":2500,"wires":[]},{"id":"b9f2f83a330140ca","type":"join","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Calculating the the average of temperature","mode":"reduce","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":true,"accumulate":true,"timeout":"","count":"","reduceRight":false,"reduceExp":"$A+ payload.temperature","reduceInit":"0","reduceInitType":"num","reduceFixup":"$A/$N","x":690,"y":2400,"wires":[["362ec9c482688cf6"]]},{"id":"6116c1efc3f7f682","type":"split","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Splits a message into a sequence of messages.","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":700,"y":2340,"wires":[["b9f2f83a330140ca"]]},{"id":"01e7066b3ff012e7","type":"join","z":"9cf82b68bb89e8ce","g":"3c012808d6b397e2","name":"Creating new array by combining message sequence","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":true,"accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":700,"y":2260,"wires":[["6116c1efc3f7f682"]]}]
-{% endrenderFlow %}
+```
+::
However, clicking the **Inject** node once does not produce the expected results; instead, it requires clicking again to get the output. This indicates that there might be a timing issue or a logic flaw in the flow that prevents it from processing correctly on the first click. Let's debug the flow with a debugger now.
@@ -87,7 +88,7 @@ At the top of the Debugger tab, you will find controls to stop the runtime manua
Now, let's diagnose the flow. We’ll manually pause the runtime, then step through each part of the flow using the debugger controls, observing the changes at each step.
-
+
_Video showing the execution of flow while debugger enabled and how to proceed to subsequent execution_
Follow these steps:
@@ -103,13 +104,13 @@ Manually stepping through the flow is useful for understanding how the flow oper
## Adding Breakpoints for Debugging Flows
-Now that we've pinpointed the problem to be somewhere between the[Switch](/node-red/core-nodes/switch/) node and the [Join](/node-red/core-nodes/join/) node, it’s time to leverage breakpoints for a more efficient debugging experience. These breakpoints allow you to pause the flow automatically allowing you to inspect messages and context without having to step through each node manually. This is especially useful for larger or more intricate flows.
+Now that we've pinpointed the problem to be somewhere between the[Switch](/docs/node-red/core-nodes/switch/) node and the [Join](/docs/node-red/core-nodes/join/) node, it’s time to leverage breakpoints for a more efficient debugging experience. These breakpoints allow you to pause the flow automatically allowing you to inspect messages and context without having to step through each node manually. This is especially useful for larger or more intricate flows.
First, let’s discuss where exactly we should add breakpoints. Our previous debugging shows that all 11 messages are correctly reaching the input of the Switch node. However, we need to check how many messages pass through the Switch node's condition and whether they contain the required part object for the Join node to create a single value (array).
To do this, we should add breakpoints at the output of the Switch node to monitor how many messages pass through, as well as at the input and output of the Join node. This will help us determine how many messages are reaching the input of the Join node and whether they contain the part object necessary for the Join node to automatically convert them into an array of those objects.
-
+
_Video showing how to add breakpoints_
To add a breakpoint:
@@ -121,7 +122,7 @@ To add a breakpoint:
### Debugging: Pinpointing the Exact Problem and Solving the Issue in the Flow
-
+
_Video showing the execution of the flow with added breakpoints, indicating the number of each input/output being sent and received for debugging._
Start by clicking the inject node to trigger execution, which will pause at the output of the switch node. Check the blue rectangle to see how many messages have passed through; it shows only a few, not 11, indicating that only those messages met the condition. As you proceed, you will see those messages also reaching the input of the join node correctly.
@@ -136,7 +137,7 @@ Now that you’ve learned how to add breakpoints and pinpoint problems, lets loo
### Disabling Breakpoints
-
+
_Video showing two ways of disabling breakpoints_
To disable a breakpoint without removing it:
@@ -149,7 +150,7 @@ To disable a breakpoint without removing it:
### Removing Breakpoints
-
+
_Video showing two ways of removing breakpoints_
To remove a breakpoint:
diff --git a/src/node-red/getting-started/programming/if-else.md b/nuxt/content-guides/node-red/getting-started/programming/if-else.md
similarity index 93%
rename from src/node-red/getting-started/programming/if-else.md
rename to nuxt/content-guides/node-red/getting-started/programming/if-else.md
index 46a809beed..8c94d3292b 100644
--- a/src/node-red/getting-started/programming/if-else.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/if-else.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: If-Else
- parent: Programming
+title: How to Use If-Else Logic in Node-RED
+navTitle: If-Else
+navOrder: 50
meta:
- title: How to Use If-Else Logic in Node-RED
- description: Learn how to implement If-Else logic in Node-RED with our step-by-step guide. Use Function and Switch nodes for dynamic, conditional flows.
+ description: Learn how to implement If-Else logic in Node-RED with our step-by-step guide. Use Function and Switch nodes for dynamic, conditional flows.
keywords: node red if else, nodered switch, node-red conditional flow, node red function if else, node red if then else example, node red switch example, node red switch node example, node red function multiple outputs example, node red function example,node red function if, node red if, node red if node, node red if then
-image: /node-red/getting-started/images/if-else-with-node-red.png
+image: /docs/node-red/getting-started/images/if-else-with-node-red.png
---
-# {{meta.title}}
+# How to Use If-Else Logic in Node-RED
Human decision-making is often guided by a series of "if this, then that" choices, whether it's deciding what to wear based on the weather or determining the quickest route to work depending on traffic. This kind of logic is equally crucial in systems, especially those built in Node-RED. Just as we make decisions based on various factors, systems must evaluate conditions and choose the appropriate course of action.
@@ -36,7 +35,7 @@ To implement If-Else logic in Node-RED, you can use the Switch node, which align
### Using Switch Node
-The [Switch](/node-red/core-nodes/switch/) node in Node-RED is used for routing messages based on specific conditions, offering a straightforward, low-code approach to implementing conditional logic in your flows. The Switch node allows you to set up rules using a visual interface, making it ideal for users who prefer a more intuitive method for handling conditions. However, it’s important to note that the Switch node represents a different, independent concept known as the "[switch statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch)." While it serves a similar purpose to If-Else logic by building conditional flows, it operates under its own programming paradigm.
+The [Switch](/docs/node-red/core-nodes/switch/) node in Node-RED is used for routing messages based on specific conditions, offering a straightforward, low-code approach to implementing conditional logic in your flows. The Switch node allows you to set up rules using a visual interface, making it ideal for users who prefer a more intuitive method for handling conditions. However, it’s important to note that the Switch node represents a different, independent concept known as the "[switch statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch)." While it serves a similar purpose to If-Else logic by building conditional flows, it operates under its own programming paradigm.
To demonstrate the Switch node, we'll set up a flow to make decisions based on the temperature value. We will route messages through different outputs based on temperature thresholds.
@@ -73,13 +72,15 @@ graph TD
```
_Node-RED flow using the Switch node to route messages based on temperature thresholds._
-{% renderFlow %}
+::render-flow
+```json
[{"id":"b90722a28f81c014","type":"group","z":"9cf82b68bb89e8ce","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["dd0d3432c348e1f2","21bd53568877f3b5","5277e184faad375a","7fbf37916236a960","aac1f46169e431ab","3eb24a6129d8fa8e","36e51d42d34c9587"],"x":194,"y":1219,"w":892,"h":322},{"id":"dd0d3432c348e1f2","type":"debug","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"high temperature","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":1260,"wires":[]},{"id":"21bd53568877f3b5","type":"inject","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"Temperature","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random()*100","payloadType":"jsonata","x":310,"y":1380,"wires":[["5277e184faad375a"]]},{"id":"5277e184faad375a","type":"switch","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"30","vt":"num"},{"t":"lte","v":"30","vt":"num"},{"t":"lte","v":"20","vt":"num"},{"t":"lte","v":"10","vt":"num"}],"checkall":"true","repair":false,"outputs":4,"x":530,"y":1380,"wires":[["dd0d3432c348e1f2"],["7fbf37916236a960"],["7fbf37916236a960"],["3eb24a6129d8fa8e"]]},{"id":"7fbf37916236a960","type":"switch","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"20","vt":"num"},{"t":"gt","v":"10","vt":"num"}],"checkall":"false","repair":false,"outputs":2,"x":670,"y":1380,"wires":[["aac1f46169e431ab"],["36e51d42d34c9587"]]},{"id":"aac1f46169e431ab","type":"debug","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"for medium temperature","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":930,"y":1340,"wires":[]},{"id":"3eb24a6129d8fa8e","type":"debug","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"for very low temperature","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":1500,"wires":[]},{"id":"36e51d42d34c9587","type":"debug","z":"9cf82b68bb89e8ce","g":"b90722a28f81c014","name":"for low temperature","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":910,"y":1420,"wires":[]}]
-{% endrenderFlow %}
+```
+::
### Using Function Node
-The [Function](/node-red/core-nodes/function/) node allows for more complex logic by writing JavaScript. It's suitable when you need more control, or multiple values must be checked together.
+The [Function](/docs/node-red/core-nodes/function/) node allows for more complex logic by writing JavaScript. It's suitable when you need more control, or multiple values must be checked together.
For demonstration purposes, let's use the temperature example where we determine whether to turn the air conditioner on or off based on the temperature:
@@ -100,7 +101,7 @@ Before moving further, let's pause and understand what’s happening in the code
In Node-RED, `msg.payload` is used to carry data through the flow. Initially, it holds the temperature value injected by the Inject node. The Function node then processes this value using If-Else logic. If the temperature exceeds 30°C, `msg.payload` is set to `"Turn on the air conditioner"`, indicating that the air conditioner should be turned on. If the temperature is 30°C or lower, `msg.payload` is set to `"No action required"`, signaling that the air conditioner should remain off. This updated `msg.payload` is then passed on to the next node, ensuring the system responds appropriately based on the temperature input.
-Many people need clarification on the messaging system in Node-RED. For a deeper understanding of how messaging works in Node-RED, I recommend going through this document: [Node-RED Messaging Guide](/node-red/getting-started/node-red-messages/).
+Many people need clarification on the messaging system in Node-RED. For a deeper understanding of how messaging works in Node-RED, I recommend going through this document: [Node-RED Messaging Guide](/docs/node-red/getting-started/node-red-messages/).
3. Next, drag the Debug node onto the canvas and connect it to the output of the Function node. This will allow you to see the results of your conditional logic in the Node-RED debug window.
4. Deploy the flow by clicking the "Deploy" button in the top-right corner of the Node-RED editor.
@@ -115,9 +116,11 @@ flowchart TD
D --> E[End]
```
-{% renderFlow %}
+::render-flow
+```json
[{"id":"51ffa77e55eb7f63","type":"group","z":"9cf82b68bb89e8ce","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["19fa09374c1be7c4","ff41ef215d860c1a","773175cbe8014372"],"x":154,"y":2059,"w":672,"h":82},{"id":"19fa09374c1be7c4","type":"inject","z":"9cf82b68bb89e8ce","g":"51ffa77e55eb7f63","name":"Temperature","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random()*100","payloadType":"jsonata","x":270,"y":2100,"wires":[["ff41ef215d860c1a"]]},{"id":"ff41ef215d860c1a","type":"function","z":"9cf82b68bb89e8ce","g":"51ffa77e55eb7f63","name":"Temperature Threshold Check","func":"let Temperature = msg.payload;\n if (Temperature > 30) {\n msg.payload = \"Turn on the air conditioner\";\n } else {\n msg.payload = \"No action required\";\n }\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":2100,"wires":[["773175cbe8014372"]]},{"id":"773175cbe8014372","type":"debug","z":"9cf82b68bb89e8ce","g":"51ffa77e55eb7f63","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":730,"y":2100,"wires":[]}]
-{% endrenderFlow %}
+```
+::
_Node-RED flow using the Function node to implement simple If-Else logic for temperature control._
@@ -182,9 +185,11 @@ flowchart TD
```
_Node-RED flow using the Function node with multiple outputs for handling various conditions like temperature and humidity._
-{% renderFlow %}
+::render-flow
+```json
[{"id":"301a31b0972b0b20","type":"group","z":"9cf82b68bb89e8ce","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["dbd085ed607e41de","31d2e8f0c871143d","b169bf385ca85f6c","aa7ab6452f4a7791","cc3e6e2c70643f6f","800508e428d74f5f"],"x":114,"y":719,"w":832,"h":202},{"id":"dbd085ed607e41de","type":"inject","z":"9cf82b68bb89e8ce","g":"301a31b0972b0b20","name":"Temperature && Humidity","props":[{"p":"payload.temperature","v":"$random() * 100","vt":"jsonata"},{"p":"payload.humidity","v":"$random() * 100","vt":"jsonata"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":820,"wires":[["31d2e8f0c871143d"]]},{"id":"31d2e8f0c871143d","type":"function","z":"9cf82b68bb89e8ce","g":"301a31b0972b0b20","name":"Temp/Humidity Decision Engine","func":"let Temperature = msg.payload.temperature;\nlet Humidity = msg.payload.humidity;\n\n// Initialize output array\nlet outputs = [null, null, null, null];\n\nif (Temperature > 30 && Humidity < 40) {\n // High temperature and low humidity\n outputs[0] = { payload: \"High temperature and low humidity: Turn on the air conditioner and use a humidifier\" };\n} else if (Temperature > 30 && Humidity >= 40) {\n // High temperature and high humidity\n outputs[1] = { payload: \"High temperature and high humidity: Turn on the air conditioner\" };\n} else if (Temperature < 15 && Humidity < 40) {\n // Low temperature and low humidity\n outputs[2] = { payload: \"Low temperature and low humidity: Turn on the heater and use a humidifier\" };\n} else if (Temperature < 15 && Humidity >= 40) {\n // Low temperature and high humidity\n outputs[3] = { payload: \"Low temperature and high humidity: Turn on the heater\" };\n}\n\nreturn outputs;","outputs":4,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":820,"wires":[["b169bf385ca85f6c"],["aa7ab6452f4a7791"],["cc3e6e2c70643f6f"],["800508e428d74f5f"]]},{"id":"b169bf385ca85f6c","type":"debug","z":"9cf82b68bb89e8ce","g":"301a31b0972b0b20","name":"Output 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":840,"y":760,"wires":[]},{"id":"aa7ab6452f4a7791","type":"debug","z":"9cf82b68bb89e8ce","g":"301a31b0972b0b20","name":"Output 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":840,"y":800,"wires":[]},{"id":"cc3e6e2c70643f6f","type":"debug","z":"9cf82b68bb89e8ce","g":"301a31b0972b0b20","name":"Output 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":840,"y":840,"wires":[]},{"id":"800508e428d74f5f","type":"debug","z":"9cf82b68bb89e8ce","g":"301a31b0972b0b20","name":"Output 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":840,"y":880,"wires":[]}]
-{% endrenderFlow %}
+```
+::
## Choosing Between the Function Node and Switch Node
@@ -194,4 +199,4 @@ The Function node excels in scenarios where complex logic and detailed message p
In contrast, the Switch node is designed for simpler, value-based routing. It is ideal for straightforward scenarios where you need to route messages based on a single value with multiple possible outputs. This node enables you to create rules based on specific values or conditions without the need for complex logic or extensive message modifications. If your routing logic involves basic comparisons and does not require advanced processing or calculations, the Switch node provides a more streamlined and intuitive approach.
-In summary, choose the Function node for intricate decision-making and detailed message processing, while the Switch node is better suited for scenarios where simple value-based routing is sufficient.
\ No newline at end of file
+In summary, choose the Function node for intricate decision-making and detailed message processing, while the Switch node is better suited for scenarios where simple value-based routing is sufficient.
diff --git a/src/node-red/getting-started/programming/loop.md b/nuxt/content-guides/node-red/getting-started/programming/loop.md
similarity index 96%
rename from src/node-red/getting-started/programming/loop.md
rename to nuxt/content-guides/node-red/getting-started/programming/loop.md
index e92552f5e9..43bf9b975d 100644
--- a/src/node-red/getting-started/programming/loop.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/loop.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: Loop
- parent: Programming
+title: How to implement loops in Node-RED flows
+navTitle: Loop
+navOrder: 50
meta:
- title: How to implement loops in Node-RED flows
description: Learn how to implement while, for, and for...of loops in Node-RED with core and custom nodes for efficient data processing and automation.
keywords: node red loop, nodered loop, node red for loop, node-red loop, nodered for of, node-red while loop, node-red do while loop
---
-# {{meta.title}}
+# How to implement loops in Node-RED flows
Handling repetitive tasks is a common challenge in automation and data processing. Whether you need to iterate over large datasets, perform calculations, or execute operations based on conditions multiple times, using loops can significantly enhance efficiency and scalability.
@@ -61,9 +60,11 @@ graph TD
F --> G["Debug Node Display Current String"]
```
-{% renderFlow %}
+::render-flow
+```json
[{"id":"e90dc2e50e40896c","type":"group","z":"a3aa840957f658c6","name":"While Loop","style":{"label":true},"nodes":["cf6ebf02d00eaca6","696d89aa4050cd13","0e2d9f0447cf6226","bb6074cdf4398cb6","e19304ab31199315","c467509c666ee400","06bb08e3f0d7888a"],"x":34,"y":139,"w":1092,"h":242},{"id":"cf6ebf02d00eaca6","type":"switch","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"Does the msg.i contains Z","property":"i","propertyType":"msg","rules":[{"t":"cont","v":"Z","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":590,"y":240,"wires":[["c467509c666ee400"],["0e2d9f0447cf6226","06bb08e3f0d7888a"]]},{"id":"696d89aa4050cd13","type":"debug","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"End","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1030,"y":180,"wires":[]},{"id":"0e2d9f0447cf6226","type":"debug","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"Output \"i\"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"i","targetType":"msg","statusVal":"","statusType":"auto","x":900,"y":260,"wires":[]},{"id":"bb6074cdf4398cb6","type":"inject","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"Start","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"[\"hello\",\"run\",\"why\"]","payloadType":"json","x":130,"y":240,"wires":[["e19304ab31199315"]]},{"id":"e19304ab31199315","type":"change","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"initilized i with empty string","rules":[{"t":"set","p":"i","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":240,"wires":[["cf6ebf02d00eaca6"]]},{"id":"c467509c666ee400","type":"change","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"if not Set payload to \"completed\"","rules":[{"t":"set","p":"payload","pt":"msg","to":"Completed","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":180,"wires":[["696d89aa4050cd13"]]},{"id":"06bb08e3f0d7888a","type":"function","z":"a3aa840957f658c6","g":"e90dc2e50e40896c","name":"Append Random Letter","func":"// add ASCII char from 32 to 126\nmsg.i += String.fromCharCode(Math.random()*26 + 65);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":340,"wires":[["cf6ebf02d00eaca6"]]}]
-{% endrenderFlow %}
+```
+::
#### For Loop
@@ -94,13 +95,15 @@ graph TD
G --> H["Debug Node Display Completion Message"]
```
-{% renderFlow %}
+::render-flow
+```json
[{"id":"3b546e0612673478","type":"group","z":"a3aa840957f658c6","name":"ForLoop","style":{"label":true},"nodes":["8ee16c1d06fca9fe","64a4a7329631d7ce","e67a34285a7e6979","e2074cf01ed451fd","811cedbe7cb89415","8bbccc2d10c36bfe","826d819a06810184","e7ebf7f66cda1370"],"x":14,"y":159,"w":1292,"h":242},{"id":"8ee16c1d06fca9fe","type":"switch","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"if msg.i == msg.payload.length","property":"i","propertyType":"msg","rules":[{"t":"eq","v":"payload.length","vt":"msg"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":550,"y":280,"wires":[["8bbccc2d10c36bfe"],["e7ebf7f66cda1370","826d819a06810184"]]},{"id":"64a4a7329631d7ce","type":"debug","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"End","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1210,"y":200,"wires":[]},{"id":"e67a34285a7e6979","type":"debug","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"Output \"i\"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1080,"y":340,"wires":[]},{"id":"e2074cf01ed451fd","type":"inject","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"Start","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"[\"hello\",\"run\",\"why\"]","payloadType":"json","x":110,"y":280,"wires":[["811cedbe7cb89415"]]},{"id":"811cedbe7cb89415","type":"change","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"initilized i with 0","rules":[{"t":"set","p":"i","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":280,"wires":[["8ee16c1d06fca9fe"]]},{"id":"8bbccc2d10c36bfe","type":"change","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"If 'i' equals array length, set payload to \"completed\".","rules":[{"t":"set","p":"payload","pt":"msg","to":"Completed","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":200,"wires":[["64a4a7329631d7ce"]]},{"id":"826d819a06810184","type":"change","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"Access array element with i","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[msg.i]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":340,"wires":[["e67a34285a7e6979"]]},{"id":"e7ebf7f66cda1370","type":"change","z":"a3aa840957f658c6","g":"3b546e0612673478","name":"increment i by 1","rules":[{"t":"set","p":"i","pt":"msg","to":"i+1","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":360,"wires":[["8ee16c1d06fca9fe"]]}]
-{% endrenderFlow %}
+```
+::
#### For...of / ForEach Loop
-In traditional programming, `for...of` and `forEach` loops are commonly used to iterate through arrays or object properties, allowing for individual element processing. Since Node-RED doesn’t include these specific constructs, you can replicate their functionality by using a combination of nodes, particularly the **[Split](/node-red/core-nodes/split/)** and **[Join](/node-red/core-nodes/join/)** nodes.
+In traditional programming, `for...of` and `forEach` loops are commonly used to iterate through arrays or object properties, allowing for individual element processing. Since Node-RED doesn’t include these specific constructs, you can replicate their functionality by using a combination of nodes, particularly the **[Split](/docs/node-red/core-nodes/split/)** and **[Join](/docs/node-red/core-nodes/join/)** nodes.
Here’s how you can replicate this functionality in Node-RED:
@@ -111,7 +114,7 @@ Here’s how you can replicate this functionality in Node-RED:
When you click the **Inject** button, the **Split** node will process each element of the array individually, and the **Debug** node will display each one in the debug window.
-To explore how you can map, sort, filter, and reduce data using this approach, check out our guide: [How to Filter, Map, Sort, and Reduce Data in Node-RED](/node-red/getting-started/programming/data-tranformation/).
+To explore how you can map, sort, filter, and reduce data using this approach, check out our guide: [How to Filter, Map, Sort, and Reduce Data in Node-RED](/docs/node-red/getting-started/programming/data-tranformation/).
```mermaid
graph TD
@@ -123,13 +126,15 @@ graph TD
C["Debug Node\nDisplays each element"]
```
-{% renderFlow %}
+::render-flow
+```json
[{"id":"2e67b2739f364a71","type":"group","z":"a3aa840957f658c6","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["2add2705d262010d","40333c736844a0c1","3337ba024a363a15","2f7d2aab12b3b4e9"],"x":254,"y":219,"w":532,"h":162},{"id":"2add2705d262010d","type":"inject","z":"a3aa840957f658c6","g":"2e67b2739f364a71","name":"Array","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"foo\",\"bar\",\"foobar\"]","payloadType":"json","x":350,"y":340,"wires":[["40333c736844a0c1"]]},{"id":"40333c736844a0c1","type":"split","z":"a3aa840957f658c6","g":"2e67b2739f364a71","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":530,"y":320,"wires":[["3337ba024a363a15"]]},{"id":"3337ba024a363a15","type":"debug","z":"a3aa840957f658c6","g":"2e67b2739f364a71","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":320,"wires":[]},{"id":"2f7d2aab12b3b4e9","type":"inject","z":"a3aa840957f658c6","g":"2e67b2739f364a71","name":"Object","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"first\":\"Hello World\",\"second\":8,\"third\":true}","payloadType":"json","x":350,"y":260,"wires":[["40333c736844a0c1"]]}]
-{% endrenderFlow %}
+```
+::
#### Implementing Loops with the Function Node
-Implementing loops with the **[Function](/node-red/core-nodes/function/)** node is straightforward if you're familiar with JavaScript, as it allows you to write custom code. However, a common issue is figuring out how to send a message on each iteration without ending the loop after the first iteration. In this section, we’ll show you how to implement loops in the `Function` node correctly, ensuring that each iteration is processed and sent out properly without prematurely breaking the loop.
+Implementing loops with the **[Function](/docs/node-red/core-nodes/function/)** node is straightforward if you're familiar with JavaScript, as it allows you to write custom code. However, a common issue is figuring out how to send a message on each iteration without ending the loop after the first iteration. In this section, we’ll show you how to implement loops in the `Function` node correctly, ensuring that each iteration is processed and sent out properly without prematurely breaking the loop.
For demonstration purposes, we will implement a `for` loop.
@@ -149,9 +154,11 @@ When you deploy the flow and click the **Inject** button, each item in the array
By using `node.send()` inside the loop, you ensure that each iteration produces a separate message, and the Function node can handle multiple messages efficiently. For more information on on this, refer to [Documentation on Sending messages asynchronously](https://nodered.org/docs/user-guide/writing-functions#sending-messages-asynchronously).
-{% renderFlow %}
+::render-flow
+```json
[{"id":"50be2bac3b058be5","type":"group","z":"a3aa840957f658c6","name":"","style":{"label":true},"nodes":["97c288f32955c6ab","7a8404c44ac0749b","0d7b9cb51669ad9b"],"x":414,"y":499,"w":612,"h":82},{"id":"97c288f32955c6ab","type":"inject","z":"a3aa840957f658c6","g":"50be2bac3b058be5","name":"Array","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[1, \"hello\", \"%\", true]","payloadType":"json","x":510,"y":540,"wires":[["7a8404c44ac0749b"]]},{"id":"7a8404c44ac0749b","type":"function","z":"a3aa840957f658c6","g":"50be2bac3b058be5","name":"For Loop","func":"for (let i = 0; i < msg.payload.length; i++) {\n // Create a new message for each item\n let newMsg = { ...msg }; // Copy the original msg object\n newMsg.payload = msg.payload[i]; // Set payload to the current item\n node.send(newMsg)\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":540,"wires":[["0d7b9cb51669ad9b"]]},{"id":"0d7b9cb51669ad9b","type":"debug","z":"a3aa840957f658c6","g":"50be2bac3b058be5","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":540,"wires":[]}]
-{% endrenderFlow %}
+```
+::
### Implementing Loops in Node-RED with Custom node
@@ -177,9 +184,11 @@ Throughtout this section we will show you how you can implement loops in Node-RE
6. Drag a **Debug** node onto the canvas and connect its input to the output of the **Function** node. This will print the current `msg.payload` after each iteration.
7. Drag another **Debug** node onto the canvas and connect its input to the first output of the **Loop** node. This will print when the loop exits, indicating that the condition has been met.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"7ef4d41cf74f75c3","type":"group","z":"a3aa840957f658c6","name":"While Loop","style":{"label":true},"nodes":["3ba931b1.fb48d6","2d297dfa.6e660a","5aef28e.0f9e7d8","90c49008.053a58","9a725668.330148","e88b0e8c.d39858","9c48f336.471ea","66144af4.d1ec9c","75c6b326.e934d4","77ac6763.8e54b8","99b265b4.e2d3c8"],"x":174,"y":139,"w":672,"h":322},{"id":"3ba931b1.fb48d6","type":"loop","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"","kind":"cond","count":"10","initial":"1","step":"1","condition":"msg.payload.includes(\"Z\") != true","conditionType":"js","when":"after","enumeration":"enum","enumerationType":"msg","limit":"","loopPayload":"loop-keep","finalPayload":"final-last","x":500,"y":280,"wires":[["5aef28e.0f9e7d8"],["e88b0e8c.d39858"]]},{"id":"2d297dfa.6e660a","type":"inject","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":270,"y":280,"wires":[["3ba931b1.fb48d6"]]},{"id":"5aef28e.0f9e7d8","type":"debug","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"loop","targetType":"msg","statusVal":"","statusType":"auto","x":720,"y":280,"wires":[]},{"id":"90c49008.053a58","type":"comment","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"Example: Conditional loop","info":"","x":310,"y":180,"wires":[]},{"id":"9a725668.330148","type":"comment","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"Show final status","info":"","x":740,"y":240,"wires":[]},{"id":"e88b0e8c.d39858","type":"function","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"","func":"// add ASCII char from 32 to 126\nmsg.payload += String.fromCharCode(Math.random()*26 + 65);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":340,"y":380,"wires":[["66144af4.d1ec9c","9c48f336.471ea"]]},{"id":"9c48f336.471ea","type":"debug","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":380,"wires":[]},{"id":"66144af4.d1ec9c","type":"delay","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"","pauseType":"delay","timeout":"0.5","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":530,"y":355,"wires":[["3ba931b1.fb48d6"]]},{"id":"75c6b326.e934d4","type":"comment","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"Show string","info":"","x":730,"y":420,"wires":[]},{"id":"77ac6763.8e54b8","type":"comment","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"Add char to string","info":"","x":370,"y":420,"wires":[]},{"id":"99b265b4.e2d3c8","type":"comment","z":"a3aa840957f658c6","g":"7ef4d41cf74f75c3","name":"Repeat until string doesn't finish with \"Z\"","info":"","x":480,"y":240,"wires":[]}]
-{% endrenderFlow %}
+```
+::
#### For Loop
@@ -190,9 +199,11 @@ Throughtout this section we will show you how you can implement loops in Node-RE
5. Then, drag another **Change** node onto the canvas. Configure this node to set `msg.payload` to `msg.payload[msg.loop.value]`, which extracts the current array element using the loop’s counter (`msg.loop.value`). The **Loop** node generates properties like `value`, which is the counter we are incrementing.
6. Finally, drag a **Debug** node onto the canvas and connect it to the output of the previous **Change** node to print the current array element in each iteration.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"ac4bfead30be7380","type":"group","z":"a3aa840957f658c6","name":"For Loop","style":{"label":true},"nodes":["2ffc3d07.23e6fa","6e1118b1.449db8","ea852667d724cfb1","880a2501f7d153ba","bd58a956.c036b","60a8f138.0909a","44f1a29403157bba"],"x":74,"y":199,"w":1132,"h":202},{"id":"2ffc3d07.23e6fa","type":"inject","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"foo\",\"bar\",\"foobar\"]","payloadType":"json","x":210,"y":260,"wires":[["880a2501f7d153ba"]]},{"id":"6e1118b1.449db8","type":"loop","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"","kind":"fcnt","count":"","initial":"0","step":"1","condition":"","conditionType":"js","when":"before","enumeration":"enum","enumerationType":"msg","limit":"","loopPayload":"loop-orig","finalPayload":"final-last","x":710,"y":260,"wires":[["44f1a29403157bba"],["bd58a956.c036b"]]},{"id":"ea852667d724cfb1","type":"change","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"Access array element with loop counter","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[msg.loop.value]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":840,"y":360,"wires":[["60a8f138.0909a"]]},{"id":"880a2501f7d153ba","type":"change","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"Set Pass Count","rules":[{"t":"set","p":"count","pt":"msg","to":"payload.length","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":260,"wires":[["6e1118b1.449db8"]]},{"id":"bd58a956.c036b","type":"change","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"Repeat","rules":[],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":360,"wires":[["6e1118b1.449db8","ea852667d724cfb1"]]},{"id":"60a8f138.0909a","type":"debug","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1090,"y":360,"wires":[]},{"id":"44f1a29403157bba","type":"debug","z":"a3aa840957f658c6","g":"ac4bfead30be7380","name":"End Loop","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1000,"y":240,"wires":[]}]
-{% endrenderFlow %}
+```
+::
#### For...of / ForEach Loop
@@ -203,6 +214,8 @@ Throughtout this section we will show you how you can implement loops in Node-RE
With the **Enumeration** kind, you can iterate through different types of data such as arrays, strings, objects, and more, making this loop versatile for handling various data structures.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"65d854c9098393e8","type":"group","z":"a3aa840957f658c6","name":"For of/ for each","style":{"label":true},"nodes":["38630ff1.21721","f009e0e9.24576","cf52b6dd.5febb8","33031c0e.1b115c","b28b06f1.38eb48","6e593ff2.dc94c"],"x":334,"y":219,"w":722,"h":222},{"id":"38630ff1.21721","type":"inject","z":"a3aa840957f658c6","g":"65d854c9098393e8","name":"Object","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"first\":\"Hello World\",\"second\":8,\"third\":true}","payloadType":"json","x":430,"y":260,"wires":[["f009e0e9.24576"]]},{"id":"f009e0e9.24576","type":"loop","z":"a3aa840957f658c6","g":"65d854c9098393e8","name":"","kind":"enum","count":"","initial":"","step":"","condition":"","conditionType":"js","when":"before","enumeration":"payload","enumerationType":"msg","limit":"","loopPayload":"loop-val","finalPayload":"final-orig","x":740,"y":300,"wires":[["cf52b6dd.5febb8"],["33031c0e.1b115c","6e593ff2.dc94c"]]},{"id":"cf52b6dd.5febb8","type":"debug","z":"a3aa840957f658c6","g":"65d854c9098393e8","name":"Loop End","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"loop","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":280,"wires":[]},{"id":"33031c0e.1b115c","type":"change","z":"a3aa840957f658c6","g":"65d854c9098393e8","name":"Repeat","rules":[],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":400,"wires":[["f009e0e9.24576"]]},{"id":"b28b06f1.38eb48","type":"inject","z":"a3aa840957f658c6","g":"65d854c9098393e8","name":"Array","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"foo\",\"bar\",\"foobar\"]","payloadType":"json","x":430,"y":320,"wires":[["f009e0e9.24576"]]},{"id":"6e593ff2.dc94c","type":"debug","z":"a3aa840957f658c6","g":"65d854c9098393e8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":940,"y":400,"wires":[]}]
-{% endrenderFlow %}
\ No newline at end of file
+```
+::
diff --git a/src/node-red/getting-started/string.md b/nuxt/content-guides/node-red/getting-started/string.md
similarity index 94%
rename from src/node-red/getting-started/string.md
rename to nuxt/content-guides/node-red/getting-started/string.md
index 399fb01cf3..9ce47960e6 100644
--- a/src/node-red/getting-started/string.md
+++ b/nuxt/content-guides/node-red/getting-started/string.md
@@ -1,16 +1,13 @@
---
-metaTitle: "Strings in Node-RED: Convert, Split & Trim"
-eleventyNavigation:
- key: String
- order: 6
- parent: Getting Started
+title: 'Strings in Node-RED: Convert String to Number, Split, Concatenate, Trim, and More'
+navTitle: String
+navOrder: 6
meta:
- title: "Strings in Node-RED: Convert String to Number, Split, Concatenate, Trim, and More"
description: Learn essential string operations in Node-RED including converting between strings and numbers, splitting and concatenating text, parsing JSON, extracting substrings, trimming whitespace, and more. Step-by-step guide with practical examples.
keywords: node-red strings, working with strings in node-red, node-red string operations, node-red string manipulation, node-red text processing, node-red change node strings, node-red jsonata string functions, node-red template node strings, node-red function node strings, node-red parsing strings, node-red string conversion, node-red split string, node-red concatenate strings
---
-# {{ meta.title }}
+# Strings in Node-RED: Convert String to Number, Split, Concatenate, Trim, and More
Strings are one of the most common data types in Node-RED. Whether you're converting sensor values, parsing API responses, or building dynamic messages, understanding string operations is essential for building reliable flows.
@@ -26,9 +23,11 @@ One of the most frequent operations is converting string values to numbers for m
If your payload contains the string `"42"`, it becomes the number `42`. You can now use this in calculations or comparisons.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"2ac68ed1d9a7b380","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"42","payloadType":"str","x":530,"y":200,"wires":[["a14a67ac6574ae82"]]},{"id":"a14a67ac6574ae82","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"String to Number","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":200,"wires":[["225f4c6a47df2d3c"]]},{"id":"225f4c6a47df2d3c","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":200,"wires":[]}]
-{% endrenderFlow %}
+```
+::
## Converting Number to String
@@ -42,9 +41,11 @@ Converting numbers to strings is useful for displaying values, building messages
A number like `42` becomes the string `"42"`, ready for text operations.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"38b9110190ddba53","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"42","payloadType":"num","x":530,"y":260,"wires":[["06a4b6591a791f8d"]]},{"id":"06a4b6591a791f8d","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Number to String","rules":[{"t":"set","p":"payload","pt":"msg","to":"$string(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":260,"wires":[["c560426627a56137"]]},{"id":"c560426627a56137","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":260,"wires":[]}]
-{% endrenderFlow %}
+```
+::
## Splitting Strings
@@ -64,9 +65,11 @@ Splitting strings is essential when parsing CSV data, breaking apart delimited v
The Split node creates separate messages for each segment. For example, if you receive a log entry `"2024-12-15 14:30:45 ERROR Database connection failed"` and you split using spaces, it produces separate messages: first `"2024-12-15"`, then `"14:30:45"`, then `"ERROR"`, then `"Database"`, and so on. Each piece flows through your subsequent nodes one at a time, allowing you to extract the timestamp, severity level, and message separately.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"6a5c13d2e5bf152f","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2024-12-15 14:30:45 ERROR Database connection failed","payloadType":"str","x":530,"y":320,"wires":[["4d3e4c715e94572b"]]},{"id":"9e90b663c08067fc","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":320,"wires":[]},{"id":"4d3e4c715e94572b","type":"split","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Split String","splt":" ","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":690,"y":320,"wires":[["9e90b663c08067fc"]]}]
-{% endrenderFlow %}
+```
+::
## Concatenating Strings
@@ -74,15 +77,17 @@ Combining strings is common when building messages, URLs, or formatted output.
1. Add a **Template** node after the nodes containing your data
2. Double-click to open the Template configuration
-3. Write your text and insert variables using {% raw %}`{{variableName}}`{% endraw %} syntax
+3. Write your text and insert variables using `{{variableName}}` syntax
4. Click **Done**
5. Connect to where you need the processed data
-Each {% raw %}`{{variableName}}`{% endraw %} is replaced with actual data. For example, the template {% raw %}`Hello {{payload.name}}, your order #{{payload.orderId}} has shipped to {{payload.city}}.`{% endraw %} with data containing name "Sarah", orderId "12345", and city "Portland" produces: `Hello Sarah, your order #12345 has shipped to Portland.`
+Each `{{variableName}}` is replaced with actual data. For example, the template `Hello `{{payload.name}}`, your order #`{{payload.orderId}}` has shipped to `{{payload.city}}`.` with data containing name "Sarah", orderId "12345", and city "Portland" produces: `Hello Sarah, your order #12345 has shipped to Portland.`
-{% renderFlow %}
+::render-flow
+```json
[{"id":"60cb0a1d79b095a3","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"name\":\"Sarah\",\"orderId\":\"12345\",\"city\":\"Portland\"}","payloadType":"json","x":530,"y":380,"wires":[["88d113db79d20417"]]},{"id":"9ed0bd64daa0cf35","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":380,"wires":[]},{"id":"88d113db79d20417","type":"template","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Concatenating Strings","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Hello {{payload.name}}, your order #{{payload.orderId}} has shipped to {{payload.city}}.","output":"str","x":720,"y":380,"wires":[["9ed0bd64daa0cf35"]]}]
-{% endrenderFlow %}
+```
+::
## Parsing JSON Strings
@@ -96,9 +101,11 @@ API responses and stored data often arrive as JSON strings, text that looks like
The JSON node detects your data type automatically. String `'{"temperature":22,"humidity":65}'` becomes an object `{temperature: 22, humidity: 65}` so you can access `msg.payload.temperature`.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"987d6b5ce96f863e","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"temperature\":22,\"humidity\":65}","payloadType":"str","x":530,"y":440,"wires":[["b92eb17c8dac49d8"]]},{"id":"b92eb17c8dac49d8","type":"json","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Parsing JSON Strings","property":"payload","action":"","pretty":false,"x":720,"y":440,"wires":[["a34030e2d9a085fe"]]},{"id":"a34030e2d9a085fe","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":440,"wires":[]}]
-{% endrenderFlow %}
+```
+::
## Extracting Substrings
@@ -117,9 +124,11 @@ Getting specific parts of a string is useful for parsing fixed-format data, extr
- `$substring(payload, 6, 5)` on `"Hello World"` gives `"World"`
- `$substring(payload, 6)` (no length) on `"Hello World"` gives `"World"` (all remaining characters)
-{% renderFlow %}
+::render-flow
+```json
[{"id":"1505bbcdda1ef70f","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World","payloadType":"str","x":510,"y":500,"wires":[["7e13b90c9b8e48de"]]},{"id":"7b985161f48aaf74","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":500,"wires":[]},{"id":"7e13b90c9b8e48de","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Extracting Substrings","rules":[{"t":"set","p":"payload","pt":"msg","to":"$substring(payload, 0, 5)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":500,"wires":[["7b985161f48aaf74"]]}]
-{% endrenderFlow %}
+```
+::
## Trimming Whitespace
@@ -133,9 +142,11 @@ Removing unwanted spaces, tabs, or line breaks from strings prevents comparison
Whitespace from both ends is removed. `" Hello World "` becomes `"Hello World"`. The space between words stays, only edge spaces are removed.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"f43fff4b7a32185d","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":" Hello World ","payloadType":"str","x":510,"y":560,"wires":[["9ed2f66a591cefab"]]},{"id":"603d7024f06979bc","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":560,"wires":[]},{"id":"9ed2f66a591cefab","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Trimming Whitespace","rules":[{"t":"set","p":"payload","pt":"msg","to":"$trim(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":560,"wires":[["603d7024f06979bc"]]}]
-{% endrenderFlow %}
+```
+::
## Changing Case
@@ -149,13 +160,17 @@ Converting string case helps with standardization and comparison since computers
Using `$uppercase(payload)`, the string `"hello world"` becomes `"HELLO WORLD"`. Using `$lowercase(payload)`, the string `"Hello World"` becomes `"hello world"`.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"9b39fb9d0a75e5c2","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World","payloadType":"str","x":510,"y":620,"wires":[["04389bee4b520eb3"]]},{"id":"3390eac6826821a7","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":620,"wires":[]},{"id":"04389bee4b520eb3","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Changing Case : Lowercase","rules":[{"t":"set","p":"payload","pt":"msg","to":"$lowercase(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":620,"wires":[["3390eac6826821a7"]]}]
-{% endrenderFlow %}
+```
+::
-{% renderFlow %}
+::render-flow
+```json
[{"id":"2bee50ed8273a7d7","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":680,"wires":[]},{"id":"b2d16a456b083325","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Changing Case : Uppercase","rules":[{"t":"set","p":"payload","pt":"msg","to":"$uppercase(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":680,"wires":[["2bee50ed8273a7d7"]]},{"id":"2f20f09b8146b397","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World","payloadType":"str","x":510,"y":680,"wires":[["b2d16a456b083325"]]}]
-{% endrenderFlow %}
+```
+::
## Replacing Text
@@ -169,9 +184,11 @@ Finding and replacing text within strings lets you correct values, standardize f
All occurrences are replaced. `"I love apples and apples are great"` with `$replace(payload, "apples", "oranges")` gives `"I love oranges and oranges are great"`.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"dfbb9d1ce41c8672","type":"debug","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":950,"y":740,"wires":[]},{"id":"33d81d6a0d2d0f0c","type":"change","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"Replacing Text","rules":[{"t":"set","p":"payload","pt":"msg","to":"$replace(payload, \"apples\", \"oranges\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":700,"y":740,"wires":[["dfbb9d1ce41c8672"]]},{"id":"a8e1a932d619fa33","type":"inject","z":"c16e1fb8932e7e73","g":"09a33e651efa47a8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"I love apples and apples are great","payloadType":"str","x":530,"y":740,"wires":[["33d81d6a0d2d0f0c"]]}]
-{% endrenderFlow %}
+```
+::
## Checking String Length
@@ -185,9 +202,11 @@ Determining string length helps with validation or conditional processing.
The string `"Hello"` returns `5`. Use this value in conditions or validation logic.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"3791a3fac7be4833","type":"debug","z":"b446dfa04d79d359","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"length","targetType":"msg","statusVal":"","statusType":"auto","x":1110,"y":1260,"wires":[]},{"id":"fca2213c932b1fea","type":"change","z":"b446dfa04d79d359","name":"Checking String Length","rules":[{"t":"set","p":"length","pt":"msg","to":"$length(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":1260,"wires":[["3791a3fac7be4833"]]},{"id":"706d864345a0c62c","type":"inject","z":"b446dfa04d79d359","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello","payloadType":"str","x":710,"y":1260,"wires":[["fca2213c932b1fea"]]}]
-{% endrenderFlow %}
+```
+::
## Checking if String Contains Text
@@ -201,9 +220,11 @@ Testing whether a string contains specific text helps with filtering and conditi
Returns `true` if found, `false` if not. `"The quick brown fox"` with `$contains(payload, "quick")` returns `true`. Use in Switch nodes to route messages differently based on content.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"eb5fe8fe6e00e603","type":"debug","z":"b446dfa04d79d359","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":710,"y":1740,"wires":[]},{"id":"daba355f3ca9693f","type":"change","z":"b446dfa04d79d359","name":"Checking if String Contains Text","rules":[{"t":"set","p":"contains","pt":"msg","to":"$contains(payload, \"quick\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1740,"wires":[["eb5fe8fe6e00e603"]]},{"id":"ca2ab085f8edec8c","type":"inject","z":"b446dfa04d79d359","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"The quick brown fox","payloadType":"str","x":270,"y":1740,"wires":[["daba355f3ca9693f"]]}]
-{% endrenderFlow %}
+```
+::
## Complex String Operations
diff --git a/src/node-red/getting-started/update-node-red.md b/nuxt/content-guides/node-red/getting-started/update-node-red.md
similarity index 93%
rename from src/node-red/getting-started/update-node-red.md
rename to nuxt/content-guides/node-red/getting-started/update-node-red.md
index 260c1d9302..6e53ab2ba9 100644
--- a/src/node-red/getting-started/update-node-red.md
+++ b/nuxt/content-guides/node-red/getting-started/update-node-red.md
@@ -1,22 +1,20 @@
---
-eleventyNavigation:
- key: Update Node-RED
- order: 3
- parent: Getting Started
+title: How to Update Node-RED
+navTitle: Update Node-RED
+navOrder: 3
meta:
- title: How to Update Node-RED
description: Learn how to update Node-RED across different installation methods including npm, Raspberry Pi, Docker, and FlowFuse
keywords: Node-RED update, upgrade Node-RED, Node-RED version, update npm Node-RED
---
-# {{meta.title}}
+# How to Update Node-RED
Regular updates keep your Node-RED installation running smoothly with the latest features, improvements, and bug fixes. Each new release brings enhancements that expand what you can build and improve your development experience. Whether you installed Node-RED through npm, used the Raspberry Pi script, or are running it in Docker, this guide walks you through the update process step by step. We'll also cover how to check your version, update your installation, and handle updates in FlowFuse.
-
+
## Checking Your Current Version
@@ -133,4 +131,4 @@ Instead of updating instances manually, you can schedule automatic updates in fl
{data-zoomable}
-FlowFuse will then automatically update your Node-RED instances within the defined maintenance window, ensuring they stay up to date without manual intervention.
\ No newline at end of file
+FlowFuse will then automatically update your Node-RED instances within the defined maintenance window, ensuring they stay up to date without manual intervention.
diff --git a/nuxt/content-guides/node-red/hardware/README.md b/nuxt/content-guides/node-red/hardware/README.md
new file mode 100644
index 0000000000..a3f8dd3ab2
--- /dev/null
+++ b/nuxt/content-guides/node-red/hardware/README.md
@@ -0,0 +1,25 @@
+---
+title: Setting Up Node-RED on Different Hardware
+navTitle: Hardware
+navOrder: 8
+meta:
+ description: Learn how to set up Node-RED on various hardware platforms, such as Raspberry Pi, Arduino, and more.
+ keywords: node-red, node-red hardware setup, raspberry pi, arduino, siemens iot2050,
+---
+
+# Setting Up Node-RED on Different Hardware
+
+Node-RED is highly versatile and can be set up on a wide range of hardware devices, including popular choices like Raspberry Pi, Ardiuno, Siemens 2050, and more. This flexibility allows you to connect Node-RED to diverse devices and sensors, which enables the creation of interactive and automated systems.
+
+By setting up Node-RED on different hardware, you can easily integrate physical inputs with digital systems. This capability is essential for IoT (Internet of Things) applications, where data from sensors and devices can trigger actions or responses in real-time.
+
+## Resources
+
+Here are some resources to help you get started with Node-RED on diffrent hardware devices:
+
+- [Run Node-RED on Siemens IoT2050](/docs/node-red/hardware/siemens-iot-2050/): In this guide, we will discuss how to install FlowFuse Device agent on Siemens IoT2050.
+- [Setting Node-RED on BLIIOT ARMxy BL340](/docs/node-red/hardware/armxy-bl340/): Guide to setting up Node-RED on BLIIOT ARMxy BL340, including installation and configuration steps.
+- [Setting Node-RED on Raspberry Pi 4](/docs/node-red/hardware/raspberry-pi-4/): Learn how to install the FlowFuse Edge Agent on the Raspberry Pi 4 effortlessly. Manage your device with Node-RED through FlowFuse with ease.
+- [Setting Node-RED on Robustel EG5120](/docs/node-red/hardware/robustel-eg5120/): In this guide, we will discuss how to install FlowFuse Device agent on Robustel EG5120.
+- [Setting up Node-RED on Opto-22 Groov Rio R7](/docs/node-red/hardware/opto-22-groove-rio-7-mm2001-10/): Learn how to install and configure Node-RED on the Opto-22 Groov Rio R7, a rugged edge I/O module for industrial applications.
+- [Setting Up Node-RED on Raspberry Pi 5](/docs/node-red/hardware/raspberry-pi-5/): Learn how to install the FlowFuse Edge Agent on the Raspberry Pi 5 effortlessly. Manage your device with Node-RED through FlowFuse with ease.
diff --git a/nuxt/content-guides/node-red/hardware/armxy-bl340.md b/nuxt/content-guides/node-red/hardware/armxy-bl340.md
new file mode 100644
index 0000000000..9637f669b3
--- /dev/null
+++ b/nuxt/content-guides/node-red/hardware/armxy-bl340.md
@@ -0,0 +1,116 @@
+---
+title: Setting Node-RED on BLIIOT ARMxy BL340
+navTitle: BLIIOT ARMxy BL340
+navOrder: 50
+meta:
+ description: Guide to setting up Node-RED on BLIIOT ARMxy BL340, including installation and configuration steps.
+ keywords: node-red, flowfuse, BLIIOT ARMxy BL340
+image: /docs/node-red/hardware/images/armxy-bl340.jpg
+---
+
+# Setting Node-RED on BLIIOT ARMxy BL340
+
+## Specifications
+
+| | |
+|---|---|
+| Model | BLIIoT ARMxy BL340 Series |
+| RAM MB | 2048 |
+| Processor | ARM Cortex-A53 |
+| GPU | G31 MP2 |
+| IO Points | Optional (GPIO, RS485,CAN,RS232,DI/DO/AI/AO etc.,) |
+| Connectivity | Dual-band Wi-Fi 6, Bluetooth 5.2, Gigabit Ethernet, 4x USB 3.0, 1x USB-C |
+| Clock Speed | Up to 1.4 GHz |
+| Storage | SD, SDHC and SDXC(UHS-I) card |
+| Display Output | HDMI 2.0, DisplayPort |
+| Power Supply | 9~36V DC |
+
+The BLIIOT ARMxy BL340 is a high-performance single-board computer designed for demanding applications, including edge computing, automation, and embedded systems. Featuring an octa-core ARM processor, advanced connectivity options, and support for high-speed storage, it provides a powerful platform for developers and engineers.
+
+Integrating this powerful hardware with FlowFuse not only enhances its capabilities but also simplifies the management and deployment process.
+
+
Note
+
+Exciting Update! FlowFuse is now available as three different products: [FlowFuse Edge for OT teams](/product/edge/), [FlowFuse Hub for IT teams](/product/hub/), and [FlowFuse Fleet](/product/fleet/) for managing devices at scale. Visit the [FlowFuse Product Page](/product/) to learn how the platform enables you to build and manage industrial apps at scale.
+
+
+
+## Prerequisites
+
+Before proceeding with the installation, ensure you have the following:
+
+- **BLIIOT ARMxy BL340** – A functioning device with internet access.
+- **FlowFuse Account** - Ensure you have a FlowFuse account. If not, you can create a free account that allows you to manage up to two edge devices for free. For more information, refer to [FlowFuse Free Tier](/blog/2024/12/flowfuse-release-2-12/)
+- **Sudo Privileges** – Administrator access to install required packages.
+
+## Getting Started
+
+This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the BLIIOT ARMxy BL340, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
+
+### Installing FlowFuse Device Agent
+
+Before we start, it is recommended to update and upgrade your system to ensure all your packages are up to date:
+
+```bash
+sudo apt update && sudo apt upgrade -y
+```
+
+Next, let's install the FlowFuse device agent with the following script.
+
+```bash
+bash <(curl -sL https://raw.githubusercontent.com/FlowFuse/device-agent/main/service/raspbian-install-device-agent.sh)
+```
+
+This script installs the Node.js runtime (if not already installed), sets up the FlowFuse device agent, and configures the device to automatically run the FlowFuse agent on boot and restart it in case of a crash.
+
+To verify that the service is running, use the following command:
+
+```bash
+sudo systemctl status flowfuse-device-agent.service
+```
+
+If running, you should see a result similar to the one shown in the image below:
+
+{data-zoomable}
+
+### Registering the Device to Connect to FlowFuse
+
+Once you have installed the FlowFuse Device Agent, you need to register the hardware to connect it to your FlowFuse team.
+
+For instructions on how to register the hardware with your FlowFuse team, follow the documentation: [Register your Remote Instance](/docs/device-agent/register/).
+
+When registering your hardware, you will be presented with a dialog containing a one-time passcode command that the Device Agent uses to retrieve its configuration. **Make sure to copy it.**
+
+{data-zoomable}
+
+### Connecting Device
+
+Execute the command you have copied with sudo as shown below
+
+```bash
+sudo flowfuse-device-agent -o https://app.flowfuse.com
+```
+
+Once executed, you should see an output similar to the one below, indicating that the FlowFuse Device Agent has been successfully configured:
+
+```bash
+[AGENT] 3/21/2025 7:09:25 PM [info] Entering Device setup...
+[AGENT] 3/21/2025 7:09:27 PM [info] Device setup was successful
+[AGENT] 3/21/2025 7:09:27 PM [info] To start the Device Agent with the new configuration, run the following command:
+[AGENT] 3/21/2025 7:09:27 PM [info] flowfuse-device-agent
+```
+
+Now, you can check the remote instance in the FlowFuse platform, where its status should be displayed as **"running."**.
+
+{data-zoomable}
+
+Now, when your device reboots, the FlowFuse Device Agent will automatically start, ensuring that your BLIIOT ARMxy BL340 remains connected to the FlowFuse platform.
+
+## Accessing Node-RED Editor.
+
+1. Login into your FlowFuse account.
+2. Click on the remote instances option in the left sidebar.
+3. Click on the device and enable the developer mode by clicking on the top right-corner switch.
+4. Once Developer Mode is enabled, click on the Open Editor option located next to the that switch.
+
+For more information refer to [FlowFuse documentation](/docs/user/introduction/#working-with-devices)
diff --git a/src/node-red/hardware/images/GRV-R7-MM1001-10.jpeg b/nuxt/content-guides/node-red/hardware/images/GRV-R7-MM1001-10.jpeg
similarity index 100%
rename from src/node-red/hardware/images/GRV-R7-MM1001-10.jpeg
rename to nuxt/content-guides/node-red/hardware/images/GRV-R7-MM1001-10.jpeg
diff --git a/src/node-red/hardware/images/armxy-bl340.jpg b/nuxt/content-guides/node-red/hardware/images/armxy-bl340.jpg
similarity index 100%
rename from src/node-red/hardware/images/armxy-bl340.jpg
rename to nuxt/content-guides/node-red/hardware/images/armxy-bl340.jpg
diff --git a/src/node-red/hardware/images/configuration-dailog-with-one-time-code.png b/nuxt/content-guides/node-red/hardware/images/configuration-dailog-with-one-time-code.png
similarity index 100%
rename from src/node-red/hardware/images/configuration-dailog-with-one-time-code.png
rename to nuxt/content-guides/node-red/hardware/images/configuration-dailog-with-one-time-code.png
diff --git a/src/node-red/hardware/images/flowfuse-on-robustel-eg5120-installation-complete.png b/nuxt/content-guides/node-red/hardware/images/flowfuse-on-robustel-eg5120-installation-complete.png
similarity index 100%
rename from src/node-red/hardware/images/flowfuse-on-robustel-eg5120-installation-complete.png
rename to nuxt/content-guides/node-red/hardware/images/flowfuse-on-robustel-eg5120-installation-complete.png
diff --git a/src/node-red/hardware/images/flowfuse-on-robustel-eg5120-node-red.png b/nuxt/content-guides/node-red/hardware/images/flowfuse-on-robustel-eg5120-node-red.png
similarity index 100%
rename from src/node-red/hardware/images/flowfuse-on-robustel-eg5120-node-red.png
rename to nuxt/content-guides/node-red/hardware/images/flowfuse-on-robustel-eg5120-node-red.png
diff --git a/src/node-red/hardware/images/flowfuse-on-siemens-iot2050.png b/nuxt/content-guides/node-red/hardware/images/flowfuse-on-siemens-iot2050.png
similarity index 100%
rename from src/node-red/hardware/images/flowfuse-on-siemens-iot2050.png
rename to nuxt/content-guides/node-red/hardware/images/flowfuse-on-siemens-iot2050.png
diff --git a/src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-debug-node.png b/nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-debug-node.png
similarity index 100%
rename from src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-debug-node.png
rename to nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-debug-node.png
diff --git a/src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-device-configuration-dialog.png b/nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-device-configuration-dialog.png
similarity index 100%
rename from src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-device-configuration-dialog.png
rename to nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-device-configuration-dialog.png
diff --git a/src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-dht-sensor.png b/nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-dht-sensor.png
similarity index 100%
rename from src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-dht-sensor.png
rename to nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-dht-sensor.png
diff --git a/src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-inject-node.png b/nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-inject-node.png
similarity index 100%
rename from src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-inject-node.png
rename to nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-inject-node.png
diff --git a/src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-pins.png b/nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-pins.png
similarity index 100%
rename from src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-pins.png
rename to nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi-pins.png
diff --git a/src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi.png b/nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi.png
similarity index 100%
rename from src/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi.png
rename to nuxt/content-guides/node-red/hardware/images/how-to-setup-node-red-on-raspberry-pi.png
diff --git a/src/node-red/hardware/images/raspberry-4-editor.png b/nuxt/content-guides/node-red/hardware/images/raspberry-4-editor.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-4-editor.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-4-editor.png
diff --git a/src/node-red/hardware/images/raspberry-pi-4-b.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-4-b.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-4-b.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-4-b.png
diff --git a/src/node-red/hardware/images/raspberry-pi-4.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-4.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-4.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-4.png
diff --git a/src/node-red/hardware/images/raspberry-pi-5-config-before-flash.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-config-before-flash.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-5-config-before-flash.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-config-before-flash.png
diff --git a/src/node-red/hardware/images/raspberry-pi-5-device-agent.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-device-agent.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-5-device-agent.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-device-agent.png
diff --git a/src/node-red/hardware/images/raspberry-pi-5-editor.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-editor.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-5-editor.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-editor.png
diff --git a/src/node-red/hardware/images/raspberry-pi-5-flash-os.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-flash-os.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-5-flash-os.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-5-flash-os.png
diff --git a/src/node-red/hardware/images/raspberry-pi-5.png b/nuxt/content-guides/node-red/hardware/images/raspberry-pi-5.png
similarity index 100%
rename from src/node-red/hardware/images/raspberry-pi-5.png
rename to nuxt/content-guides/node-red/hardware/images/raspberry-pi-5.png
diff --git a/src/node-red/hardware/images/raspberrypi-5.png b/nuxt/content-guides/node-red/hardware/images/raspberrypi-5.png
similarity index 100%
rename from src/node-red/hardware/images/raspberrypi-5.png
rename to nuxt/content-guides/node-red/hardware/images/raspberrypi-5.png
diff --git a/src/node-red/hardware/images/robustel-eg5120.png b/nuxt/content-guides/node-red/hardware/images/robustel-eg5120.png
similarity index 100%
rename from src/node-red/hardware/images/robustel-eg5120.png
rename to nuxt/content-guides/node-red/hardware/images/robustel-eg5120.png
diff --git a/src/node-red/hardware/images/siemens-iot-2050.jpg b/nuxt/content-guides/node-red/hardware/images/siemens-iot-2050.jpg
similarity index 100%
rename from src/node-red/hardware/images/siemens-iot-2050.jpg
rename to nuxt/content-guides/node-red/hardware/images/siemens-iot-2050.jpg
diff --git a/src/node-red/hardware/images/status-flowfuse.png b/nuxt/content-guides/node-red/hardware/images/status-flowfuse.png
similarity index 100%
rename from src/node-red/hardware/images/status-flowfuse.png
rename to nuxt/content-guides/node-red/hardware/images/status-flowfuse.png
diff --git a/src/node-red/hardware/images/systemctl-status.png b/nuxt/content-guides/node-red/hardware/images/systemctl-status.png
similarity index 100%
rename from src/node-red/hardware/images/systemctl-status.png
rename to nuxt/content-guides/node-red/hardware/images/systemctl-status.png
diff --git a/src/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md b/nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
similarity index 67%
rename from src/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
rename to nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
index 613198ae38..066f10837b 100644
--- a/src/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
+++ b/nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
@@ -1,29 +1,27 @@
---
-eleventyNavigation:
- key: Opto-22 Groov Epic
- parent: Hardware
+title: Setting up Node-RED on Opto-22 Groov Rio R7
+navTitle: Opto-22 Groov Epic
+navOrder: 50
meta:
- title: Setting up Node-RED on Opto-22 Groov Rio R7
description: Learn how to install and configure Node-RED on the Opto-22 Groov Rio R7, a rugged edge I/O module for industrial applications.
keywords: node-red, flowfuse, Opto-22 Groov Rio, groov RIO, industrial IoT, edge computing
-image: "/node-red/hardware/images/GRV-R7-MM1001-10.jpeg"
-specifications:
- Model: GRV-R7-MM2001-10
- RAM: 1024 MB
- Processor: ARM Cortex-A8, 1 GHz
- I/O Channels: 10 multi-signal, multifunction channels (analog I/O, temperature, discrete I/O, mechanical relays)
- Connectivity:
- - Dual switched Gigabit Ethernet
- - USB 2.0 (host)
- - Power over Ethernet (PoE)
- - 10–32 VDC power input
- Clock Speed: 1 GHz
- Storage:
- - 4 GB eMMC (internal)
- - USB memory stick support (up to 32 GB)
-layout: layouts/hardware.njk
+image: /docs/node-red/hardware/images/GRV-R7-MM1001-10.jpeg
---
+# Setting up Node-RED on Opto-22 Groov Rio R7
+
+## Specifications
+
+| | |
+|---|---|
+| Model | GRV-R7-MM2001-10 |
+| RAM | 1024 MB |
+| Processor | ARM Cortex-A8, 1 GHz |
+| I/O Channels | 10 multi-signal, multifunction channels (analog I/O, temperature, discrete I/O, mechanical relays) |
+| Connectivity | Dual switched Gigabit Ethernet,USB 2.0 (host),Power over Ethernet (PoE),10–32 VDC power input |
+| Clock Speed | 1 GHz |
+| Storage | 4 GB eMMC (internal),USB memory stick support (up to 32 GB) |
+
The Opto-22 Groov Rio R7 is a rugged edge I/O module designed for industrial applications. Equipped with a powerful ARM Cortex-A8 processor, versatile I/O channels, and various connectivity options, it’s an ideal solution for edge computing and industrial IoT.
## Prerequisites
@@ -31,14 +29,23 @@ The Opto-22 Groov Rio R7 is a rugged edge I/O module designed for industrial app
Before proceeding with the installation, ensure you have the following:
- **Opto-22 Groov Rio R7** – A functioning device with internet access.
-- **FlowFuse Account** - You need an active FlowFuse account to access the platform and configure your instance. If you do not have one, please visit the FlowFuse website and [sign up]({% include "sign-up-url.njk" %}) for a new account before proceeding.
+- **FlowFuse Account** - You need an active FlowFuse account to access the platform and configure your instance. If you do not have one, please visit the FlowFuse website and [sign up](https://app.flowfuse.com/account/create) for a new account before proceeding.
- **Sudo Privileges** – Administrator access to install required packages.
## Getting Started
This guide will walk you through setting up Node-RED on the Groov Rio R7 using the FlowFuse Device Agent, allowing you to manage, scale, and secure your remote instances effectively.
-{% include "hardware/system/opto-22-groove-rio.md" %}
+### Installing FlowFuse Device Agent
+
+Before starting the installation, it is recommended to update your system to
+ensure that all your packages are up to date. You can use groov manage, which
+acts as the command central for your groov RIO devices. For detailed
+instructions on how to update the system, [watch this video](https://www.opto22.com/support/resources-tools/videos/playlist-what-is-groov-epic?wchannelid=61lkudfc8c&wmediaid=mxzzp2kudx).
+
+This guide is written for the firmware version of: `4.0.2-b.194`. Node.JS 20 is
+available on the device, and you should be good to go to register the edge device
+on FlowFuse.
### Registering the Device to Connect to FlowFuse
diff --git a/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md b/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md
new file mode 100644
index 0000000000..4d92daff94
--- /dev/null
+++ b/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md
@@ -0,0 +1,117 @@
+---
+title: Setting Node-RED on Raspberry Pi 4
+navTitle: Raspberry Pi 4
+navOrder: 50
+meta:
+ description: Learn how to install the FlowFuse Edge Agent on the Raspberry Pi 4 effortlessly. Manage your device with Node-RED through FlowFuse with ease.
+ keywords: node-red, flowfuse, raspberry pi, raspberry pi 4
+image: /docs/node-red/hardware/images/raspberry-pi-4-b.png
+---
+
+# Setting Node-RED on Raspberry Pi 4
+
+## Specifications
+
+| | |
+|---|---|
+| Model | Raspberry Pi 4 B 8GB |
+| RAM MB | 8192 |
+| Processor | Broadcom BCM2711, ARM Cortex-A72 (ARMv8-A), 4 (Quad-core) |
+| GPIO | (Fully backwar ds-compatible with previous boards), Standard 40-pin GPIO Header |
+| Connectivity | Dual-band Wi-Fi, Bluetooth 5.0, Gigabit Ethernet, 2x USB 3.0, 2x USB 2.0 |
+| Clock Speed | 1.5 GHz |
+| Storage | microSD |
+
+## Raspberry Pi OS Installation
+
+To set up your Raspberry Pi 4 for use with Node-RED and FlowFuse, follow these steps:
+
+### Flashing Raspberry Pi OS
+
+1. Use the [official Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the 64-bit version of Raspberry Pi OS to an SD card.
+
+
+
+2. Before writing to the SD card, configure the OS for headless mode, including Wi-Fi, SSH, and authentication settings.
+
+
+
+3. Write the OS and configuration to the SD card. This process takes about 10 minutes.
+
+4. Insert the SD card into the Raspberry Pi 4 and power it on. The device should appear on your network after a minute or so.
+
+5. Connect to the Raspberry Pi using SSH:
+
+ ```sh
+ ssh pi@raspberrypi.local
+ ```
+
+## Getting Started
+
+This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the Raspberry Pi 4, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
+
+### Installing FlowFuse Device Agent
+
+Before we start, it is recommended to update and upgrade your system to ensure all your packages are up to date:
+
+```bash
+sudo apt update && sudo apt upgrade -y
+```
+
+Next, let's install the FlowFuse device agent with the following script.
+
+```bash
+bash <(curl -sL https://raw.githubusercontent.com/FlowFuse/device-agent/main/service/raspbian-install-device-agent.sh)
+```
+
+This script installs the Node.js runtime (if not already installed), sets up the FlowFuse device agent, and configures the device to automatically run the FlowFuse agent on boot and restart it in case of a crash.
+
+To verify that the service is running, use the following command:
+
+```bash
+sudo systemctl status flowfuse-device-agent.service
+```
+
+If running, you should see a result similar to the one shown in the image below:
+
+{data-zoomable}
+
+### Registering the Device to Connect to FlowFuse
+
+Once you have installed the FlowFuse Device Agent, you need to register the hardware to connect it to your FlowFuse team.
+
+For instructions on how to register the hardware with your FlowFuse team, follow the documentation: [Register your Remote Instance](/docs/device-agent/register/).
+
+When registering your hardware, you will be presented with a dialog containing a one-time passcode command that the Device Agent uses to retrieve its configuration. **Make sure to copy it.**
+
+{data-zoomable}
+
+### Connecting Device
+
+Execute the command you have copied with sudo as shown below
+
+```bash
+sudo flowfuse-device-agent -o https://app.flowfuse.com
+```
+
+Once executed, you should see an output similar to the one below, indicating that the FlowFuse Device Agent has been successfully configured:
+
+```bash
+[AGENT] 3/21/2025 7:09:25 PM [info] Entering Device setup...
+[AGENT] 3/21/2025 7:09:27 PM [info] Device setup was successful
+[AGENT] 3/21/2025 7:09:27 PM [info] To start the Device Agent with the new configuration, run the following command:
+[AGENT] 3/21/2025 7:09:27 PM [info] flowfuse-device-agent
+```
+
+Now, you can check the remote instance in the FlowFuse platform, where its status should be displayed as **"running."**.
+
+{data-zoomable}
+
+## Accessing Node-RED Editor.
+
+1. Login into your FlowFuse account.
+2. Click on the remote instances option in the left sidebar.
+3. Click on the device and enable the developer mode by clicking on the top right-corner switch.
+4. Once Developer Mode is enabled, click on the Open Editor option located next to the that switch.
+
+For more information refer to [FlowFuse documentation](/docs/user/introduction/#working-with-devices)
diff --git a/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md b/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md
new file mode 100644
index 0000000000..89af34c7e1
--- /dev/null
+++ b/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md
@@ -0,0 +1,117 @@
+---
+title: Setting Up Node-RED on Raspberry Pi 5
+navTitle: Raspberry Pi 5
+navOrder: 50
+meta:
+ description: Learn how to install the FlowFuse Edge Agent on the Raspberry Pi 5 effortlessly. Manage your device with Node-RED through FlowFuse with ease.
+ keywords: node-red, flowfuse, raspberry pi, raspberry pi 5
+image: /docs/node-red/hardware/images/raspberrypi-5.png
+---
+
+# Setting Up Node-RED on Raspberry Pi 5
+
+## Specifications
+
+| | |
+|---|---|
+| Model | Raspberry Pi 5 Model 8GB |
+| RAM MB | 8192 |
+| Processor | Broadcom BCM2712, ARM Cortex-A76 (ARMv8.2-A), 4 (Quad-core) |
+| GPIO | Standard 40-pin GPIO Header |
+| Connectivity | 2 × USB 2.0 Ports, 2 × USB 3.0 Ports, Bluetooth 5.0, USB-C, Wi-Fi + Bluetooth® Low Energy |
+| Clock Speed | 2.4 GHz |
+| Storage | microSD |
+
+## Raspberry Pi OS Installation
+
+To set up your Raspberry Pi 5 for use with Node-RED and FlowFuse, follow these steps:
+
+### Flashing Raspberry Pi OS
+
+1. Use the [official Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the 64-bit version of Raspberry Pi OS to an SD card.
+
+
+
+2. Before writing to the SD card, configure the OS for headless mode, including Wi-Fi, SSH, and authentication settings.
+
+
+
+3. Write the OS and configuration to the SD card. This process takes about 10 minutes.
+
+4. Insert the SD card into the Raspberry Pi 5 and power it on. The device should appear on your network after a minute or so.
+
+5. Connect to the Raspberry Pi using SSH:
+
+ ```sh
+ ssh pi@raspberrypi.local
+ ```
+
+## Getting Started
+
+This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the Raspberry Pi 5, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
+
+### Installing FlowFuse Device Agent
+
+Before we start, it is recommended to update and upgrade your system to ensure all your packages are up to date:
+
+```bash
+sudo apt update && sudo apt upgrade -y
+```
+
+Next, let's install the FlowFuse device agent with the following script.
+
+```bash
+bash <(curl -sL https://raw.githubusercontent.com/FlowFuse/device-agent/main/service/raspbian-install-device-agent.sh)
+```
+
+This script installs the Node.js runtime (if not already installed), sets up the FlowFuse device agent, and configures the device to automatically run the FlowFuse agent on boot and restart it in case of a crash.
+
+To verify that the service is running, use the following command:
+
+```bash
+sudo systemctl status flowfuse-device-agent.service
+```
+
+If running, you should see a result similar to the one shown in the image below:
+
+{data-zoomable}
+
+### Registering the Device to Connect to FlowFuse
+
+Once you have installed the FlowFuse Device Agent, you need to register the hardware to connect it to your FlowFuse team.
+
+For instructions on how to register the hardware with your FlowFuse team, follow the documentation: [Register your Remote Instance](/docs/device-agent/register/).
+
+When registering your hardware, you will be presented with a dialog containing a one-time passcode command that the Device Agent uses to retrieve its configuration. **Make sure to copy it.**
+
+{data-zoomable}
+
+### Connecting Device
+
+Execute the command you have copied with sudo as shown below
+
+```bash
+sudo flowfuse-device-agent -o https://app.flowfuse.com
+```
+
+Once executed, you should see an output similar to the one below, indicating that the FlowFuse Device Agent has been successfully configured:
+
+```bash
+[AGENT] 3/21/2025 7:09:25 PM [info] Entering Device setup...
+[AGENT] 3/21/2025 7:09:27 PM [info] Device setup was successful
+[AGENT] 3/21/2025 7:09:27 PM [info] To start the Device Agent with the new configuration, run the following command:
+[AGENT] 3/21/2025 7:09:27 PM [info] flowfuse-device-agent
+```
+
+Now, you can check the remote instance in the FlowFuse platform, where its status should be displayed as **"running."**.
+
+{data-zoomable}
+
+## Accessing Node-RED Editor.
+
+1. Login into your FlowFuse account.
+2. Click on the remote instances option in the left sidebar.
+3. Click on the device and enable the developer mode by clicking on the top right-corner switch.
+4. Once Developer Mode is enabled, click on the Open Editor option located next to the that switch.
+
+For more information refer to [FlowFuse documentation](/docs/user/introduction/#working-with-devices)
diff --git a/nuxt/content-guides/node-red/hardware/robustel-eg5120.md b/nuxt/content-guides/node-red/hardware/robustel-eg5120.md
new file mode 100644
index 0000000000..48ebabaed8
--- /dev/null
+++ b/nuxt/content-guides/node-red/hardware/robustel-eg5120.md
@@ -0,0 +1,97 @@
+---
+title: Setting Node-RED on Robustel EG5120
+navTitle: Robustel EG5120
+navOrder: 50
+meta:
+ description: In this guide, we will discuss how to install FlowFuse Device agent on Robustel EG5120.
+ keywords: node-red, flowfuse, robustel eg5120
+image: /docs/node-red/hardware/images/robustel-eg5120.png
+---
+
+# Setting Node-RED on Robustel EG5120
+
+## Specifications
+
+| | |
+|---|---|
+| Model | Robustel EG5120 |
+| RAM MB | 2048 |
+| Processor | Broadcom BCM2711, ARM Cortex-A72 (ARMv8-A), 4 (Quad-core) |
+| GPIO | Standard 40-pin GPIO Header |
+| Connectivity | Dual-band Wi-Fi, Bluetooth 5.0, Gigabit Ethernet, 2x USB 3.0, 2x USB 2.0 |
+| Clock Speed | 1.5 GHz |
+| Storage | microSD |
+
+The [Robustel EG5120](https://www.robustel.com/product/eg5120-industrial-edge-computing-gateway/) is a versatile gateway that facilitates robust connectivity for industrial IoT applications. Integrating this powerful hardware with FlowFuse not only enhances its capabilities but also simplifies the management and deployment process. In this documentation, we’ll walk through the steps to integrate the Robustel EG5120 with FlowFuse.
+
+The [Robustel EG5120](https://www.robustel.com/product/eg5120-industrial-edge-computing-gateway/), equipped with Linux-based Debian 11 supporting a wide variety of programming languages including Node.js, offers robust connectivity options. When combined with FlowFuse, this gateway becomes even more powerful, enabling seamless device management and deployment.
+
+The Robustel EG5120 supports multiple connectivity options including Ethernet, Wi-Fi, and cellular networks, which are essential for flexible deployments in various industrial scenarios. Its built-in support for Bluetooth, cellular connectivity, RS232, RS485, and Modbus facilitates seamless integration with a wide array of IoT devices and services. This blog will guide you through using FlowFuse to effectively manage your Node-RED instance, enhancing both the security and scalability of your IoT applications.
+
+## Getting Started
+
+This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the Robustel EG5120, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
+
+### Installing FlowFuse Device Agent
+
+Before we start, it is recommended to update and upgrade your system to ensure all your packages are up to date:
+
+```bash
+sudo apt update && sudo apt upgrade -y
+```
+
+Next, let's install the FlowFuse device agent with the following script.
+
+```bash
+bash <(curl -sL https://raw.githubusercontent.com/FlowFuse/device-agent/main/service/raspbian-install-device-agent.sh)
+```
+
+This script installs the Node.js runtime (if not already installed), sets up the FlowFuse device agent, and configures the device to automatically run the FlowFuse agent on boot and restart it in case of a crash.
+
+To verify that the service is running, use the following command:
+
+```bash
+sudo systemctl status flowfuse-device-agent.service
+```
+
+If running, you should see a result similar to the one shown in the image below:
+
+{data-zoomable}
+
+### Registering the Device to Connect to FlowFuse
+
+Once you have installed the FlowFuse Device Agent, you need to register the hardware to connect it to your FlowFuse team.
+
+For instructions on how to register the hardware with your FlowFuse team, follow the documentation: [Register your Remote Instance](/docs/device-agent/register/).
+
+When registering your hardware, you will be presented with a dialog containing a one-time passcode command that the Device Agent uses to retrieve its configuration. **Make sure to copy it.**
+
+{data-zoomable}
+
+### Connecting Device
+
+Execute the command you have copied with sudo as shown below
+
+```bash
+sudo flowfuse-device-agent -o https://app.flowfuse.com
+```
+
+Once executed, you should see an output similar to the one below, indicating that the FlowFuse Device Agent has been successfully configured:
+
+```bash
+[AGENT] 3/21/2025 7:09:25 PM [info] Entering Device setup...
+[AGENT] 3/21/2025 7:09:27 PM [info] Device setup was successful
+[AGENT] 3/21/2025 7:09:27 PM [info] To start the Device Agent with the new configuration, run the following command:
+[AGENT] 3/21/2025 7:09:27 PM [info] flowfuse-device-agent
+```
+
+Now, you can check the remote instance in the FlowFuse platform, where its status should be displayed as **"running."**.
+
+## Accessing Node-RED Editor.
+
+1. Login into your FlowFuse account.
+2. Click on the remote instances option in the left sidebar.
+3. Click on the device and enable the developer mode by clicking on the top right-corner switch.
+4. Once Developer Mode is enabled, click on the Open Editor option located next to the that switch.
+
+For more information refer to [FlowFuse documentation](/docs/user/introduction/#working-with-devices)
diff --git a/src/node-red/hardware/siemens-iot-2050.md b/nuxt/content-guides/node-red/hardware/siemens-iot-2050.md
similarity index 91%
rename from src/node-red/hardware/siemens-iot-2050.md
rename to nuxt/content-guides/node-red/hardware/siemens-iot-2050.md
index 686a870d47..8270a1b6a2 100644
--- a/src/node-red/hardware/siemens-iot-2050.md
+++ b/nuxt/content-guides/node-red/hardware/siemens-iot-2050.md
@@ -1,23 +1,27 @@
---
-eleventyNavigation:
- key: Siemens IoT2050
- parent: Hardware
+title: Run Node-RED on Siemens IoT2050
+navTitle: Siemens IoT2050
+navOrder: 50
meta:
- title: Run Node-RED on Siemens IoT2050
description: In this guide, we will discuss how to install FlowFuse Device agent on Siemens IoT2050.
keywords: node-red, flowfuse, siemens iot2050 flowfuse
-image: "/node-red/hardware/images/siemens-iot-2050.jpg"
-specifications:
- Model: IOT2050 Basic
- RAM_MB: 1024
- Processor: ARM TI AM6528 GP
- GPIO: x20 Digital I/O
- Connectivity: 1x RS 232 / 422 / 485, Ethernet, USB 2.0, Arduino, mPCIe
- Clock Speed: 1 GHz
- Storage: SD Card
-layout: layouts/hardware.njk
+image: /docs/node-red/hardware/images/siemens-iot-2050.jpg
---
+# Run Node-RED on Siemens IoT2050
+
+## Specifications
+
+| | |
+|---|---|
+| Model | IOT2050 Basic |
+| RAM MB | 1024 |
+| Processor | ARM TI AM6528 GP |
+| GPIO | x20 Digital I/O |
+| Connectivity | 1x RS 232 / 422 / 485, Ethernet, USB 2.0, Arduino, mPCIe |
+| Clock Speed | 1 GHz |
+| Storage | SD Card |
+
Siemens [announced](https://press.siemens.com/global/en/pressrelease/new-siemens-gateway-between-cloud-company-it-and-production) the IoT2000 series in March of 2020. With this tool many have been using it to function as a gateway between their plant operations and cloud infrastructure. Onboard it came with Node-RED pre-installed. To manage Node-RED as an organization the FlowFuse agent is recommended, this documentation shows you how to do so.
Warning: Later in the documentation we will be updating Node.js. This will break MRAA library. This will prevent communication to the GPIO of the device.
diff --git a/nuxt/content-guides/node-red/integration-technologies/README.md b/nuxt/content-guides/node-red/integration-technologies/README.md
new file mode 100644
index 0000000000..590fd40c9f
--- /dev/null
+++ b/nuxt/content-guides/node-red/integration-technologies/README.md
@@ -0,0 +1,22 @@
+---
+title: Using Different Technologies for Building Applications with Node-RED.
+navTitle: Integration technologies
+navOrder: 6
+meta:
+ description: Learn how to leverage various integration technologies with Node-RED for building robust and interconnected applications.
+ keywords: node-red, flowfuse, node-red integration, integration technologies, webhook, rest api
+---
+
+# Using Different Technologies for Building Applications with Node-RED.
+
+Developing powerful and scalable applications frequently necessitates the integration of various technologies. This integration is crucial for creating seamless and efficient systems that can handle complex tasks and large volumes of data. Whether you're working with REST APIs to enable communication between different services or implementing GraphQL for more efficient data querying, the ability to blend these technologies effectively is essential for modern application development.
+
+Node-RED offers a versatile and robust platform to meet these needs. Its intuitive flow-based interface allows developers to easily design and deploy sophisticated workflows, which makes it an invaluable tool for integrating REST APIs, GraphQL, webhooks, and more.
+
+## Resources
+
+Here are some resources to help you integrate Node-RED with various different technologies:
+
+- [Creating REST API's with Node-RED](/docs/node-red/integration-technologies/rest/): Learn how to create REST APIs in Node-RED and fetch data from an API.
+- [Integrating GraphQL APIs in Node-RED](/docs/node-red/integration-technologies/graphql/): Learn how to integrate GraphQL APIs in Node-RED. This guide covers setting up endpoints, executing queries, handling variables, and using mutations for dynamic data.
+- [Using Webhook with Node-RED](/docs/node-red/integration-technologies/webhook/): Learn how to seamlessly integrate webhooks into your Node-RED applications for automating tasks and enhancing communication.
diff --git a/src/node-red/integration-technologies/graphql.md b/nuxt/content-guides/node-red/integration-technologies/graphql.md
similarity index 98%
rename from src/node-red/integration-technologies/graphql.md
rename to nuxt/content-guides/node-red/integration-technologies/graphql.md
index 2e11f318c7..e801eab9bd 100644
--- a/src/node-red/integration-technologies/graphql.md
+++ b/nuxt/content-guides/node-red/integration-technologies/graphql.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: GraphQL API
- parent: "Integration Technologies"
+title: Integrating GraphQL APIs in Node-RED
+navTitle: GraphQL API
+navOrder: 50
meta:
- title: Integrating GraphQL APIs in Node-RED
- description: Learn how to integrate GraphQL APIs in Node-RED. This guide covers setting up endpoints, executing queries, handling variables, and using mutations for dynamic data.
- keywords: node-red, flowfuse, graphql apis, queries, mutations
+ description: Learn how to integrate GraphQL APIs in Node-RED. This guide covers setting up endpoints, executing queries, handling variables, and using mutations for dynamic data.
+ keywords: node-red, flowfuse, graphql apis, queries, mutations
---
-# {{meta.title}}
+# Integrating GraphQL APIs in Node-RED
GraphQL is transforming the way APIs are designed. Unlike traditional REST APIs, which often require multiple requests to different endpoints, GraphQL provides a single, flexible endpoint that allows you to fetch exactly the data you need, nothing more, nothing less. In this article, you will learn how to integrate GraphQL with Node-RED and build APIs that efficiently serve your application's data requirements.
@@ -507,6 +506,8 @@ Notice how the response includes all fields from `DeviceBasicInfo` (id, name, ty
The following example flow demonstrates creating, reading, updating, and deleting data using GraphQL, including performing queries with fragments for reusable field selections. This flow and the GraphQL node are for demonstration purposes only and do not include a demo API.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"7cb18349cfb7f014","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Get Single Device By ID","graphql":"fd163a325aa21cdb","format":"text","template":"query GetDevice($id: ID!) {\n device(id: $id) {\n id\n name\n type\n model\n location\n lastSeenStatus\n maintenanceDue\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":490,"y":240,"wires":[["1515cbad98355c5f"],["0c4068bd1821923b"]]},{"id":"6ac3b625b7e6e954","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{\"id\":\"1\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":240,"wires":[["7cb18349cfb7f014"]]},{"id":"66d017b14ed8c60d","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Get Devices","graphql":"fd163a325aa21cdb","format":"text","template":"query GetDevices {\n devices {\n id\n name\n type\n location\n lastSeenStatus\n createdAt\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":450,"y":140,"wires":[["fdcc15f68872b4d0"],["beb63abd92f155ce"]]},{"id":"572aa9e659f5e27e","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":140,"wires":[["66d017b14ed8c60d"]]},{"id":"6d4f9a2cb159d8e5","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Create Device","graphql":"fd163a325aa21cdb","format":"text","template":"mutation CreateDevice($input: DeviceInput!) {\n createDevice(input: $input) {\n id\n name\n type\n model\n location\n createdAt\n success\n errors {\n field\n message\n }\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":460,"y":340,"wires":[["f5527e179e1c8911"],["9fc70e8f8715f994"]]},{"id":"e0b89bf0a47dbae3","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{\"input\":{\"name\":\"Smart Thermostat\",\"type\":\"Controller\",\"model\":\"Nest V3\",\"location\":\"Office Area\"}}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":340,"wires":[["6d4f9a2cb159d8e5"]]},{"id":"fdcc15f68872b4d0","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":120,"wires":[]},{"id":"beb63abd92f155ce","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":160,"wires":[]},{"id":"1515cbad98355c5f","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":220,"wires":[]},{"id":"0c4068bd1821923b","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":260,"wires":[]},{"id":"f5527e179e1c8911","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":320,"wires":[]},{"id":"9fc70e8f8715f994","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":360,"wires":[]},{"id":"e0d2739c55386644","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Create Device","graphql":"fd163a325aa21cdb","format":"text","template":"mutation CreateDevice($input: DeviceInput!) {\n createDevice(input: $input) {\n id\n name\n type\n model\n location\n createdAt\n success\n errors {\n field\n message\n }\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":460,"y":440,"wires":[["f5c2e0346b4979aa"],["597bac49938cd7bc"]]},{"id":"b45422c8bffaa52d","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{\"input\":{\"name\":\"Smart Thermostat\",\"type\":\"Controller\",\"model\":\"Nest V3\",\"location\":\"Office Area\"}}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":440,"wires":[["e0d2739c55386644"]]},{"id":"f5c2e0346b4979aa","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":420,"wires":[]},{"id":"597bac49938cd7bc","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":460,"wires":[]},{"id":"dae18c118b5a7d25","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Update Device","graphql":"fd163a325aa21cdb","format":"text","template":"mutation UpdateDevice($id: ID!, $input: DeviceUpdateInput!) {\n updateDevice(id: $id, input: $input) {\n id\n name\n location\n lastSeenStatus\n updatedAt\n success\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":460,"y":540,"wires":[["8372c53fd374ca0c"],["81ee27d5d002cdf7"]]},{"id":"06e715476d4a05a1","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{ \"id\": \"1\", \"input\": { \"location\": \"Factory Floor 3\", \"lastSeenStatus\": \"Maintenance\" } }","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":540,"wires":[["dae18c118b5a7d25"]]},{"id":"8372c53fd374ca0c","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":520,"wires":[]},{"id":"81ee27d5d002cdf7","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":560,"wires":[]},{"id":"b59476221e135579","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Deactivate Device","graphql":"fd163a325aa21cdb","format":"text","template":"mutation DeactivateDevice($id: ID!) {\n deactivateDevice(id: $id) {\n id\n isActive\n deactivatedAt\n success\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":470,"y":640,"wires":[["bc0b5067c9b64b4a"],["9bcd78c44b2d304a"]]},{"id":"2f45ba641ea8d272","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{ \"id\": \"4\" }","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":640,"wires":[["b59476221e135579"]]},{"id":"bc0b5067c9b64b4a","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":620,"wires":[]},{"id":"9bcd78c44b2d304a","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":660,"wires":[]},{"id":"7d7e6689f0c42fac","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"fragment","graphql":"fd163a325aa21cdb","format":"text","template":"fragment DeviceBasicInfo on Device {\n id\n name\n type\n location\n createdAt\n}\n\nfragment DeviceOperationalInfo on Device {\n ...DeviceBasicInfo\n lastSeenStatus\n lastSeenAt\n maintenanceDue\n}\n\nquery GetDeviceProfile($deviceId: ID!) {\n device(id: $deviceId) {\n ...DeviceOperationalInfo\n }\n}","syntax":"mustache","token":"","showDebug":false,"x":440,"y":740,"wires":[["e818060c00087930"],["621b732245671f2f"]]},{"id":"ac37b7540e536255","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{ \"deviceId\": \"4\" }","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":740,"wires":[["7d7e6689f0c42fac"]]},{"id":"e818060c00087930","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":720,"wires":[]},{"id":"621b732245671f2f","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":760,"wires":[]},{"id":"f99baa69918d2a9a","type":"graphql","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Pagination","graphql":"fd163a325aa21cdb","format":"text","template":"query GetDevicesPaginated($limit: Int = 10, $offset: Int = 0, $searchTerm: String) {\n devices(limit: $limit, offset: $offset, search: $searchTerm) {\n id\n name\n type\n location\n lastSeenStatus\n createdAt\n lastSeenAt\n }\n deviceCount(search: $searchTerm)\n}","syntax":"mustache","token":"","showDebug":false,"x":450,"y":840,"wires":[["368fbd3d4f756b6a"],["cc1ef9d881065082"]]},{"id":"509af177c480ed03","type":"inject","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"","props":[{"p":"variables","v":"{ \"deviceId\": \"4\" }","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":840,"wires":[["f99baa69918d2a9a"]]},{"id":"368fbd3d4f756b6a","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Result","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":820,"wires":[]},{"id":"cc1ef9d881065082","type":"debug","z":"98a60b6dd0896e47","g":"9a0c28902989b739","name":"Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":860,"wires":[]},{"id":"fd163a325aa21cdb","type":"graphql-server","name":"","endpoint":"none","token":""},{"id":"574504396881aa85","type":"global-config","env":[],"modules":{"node-red-contrib-graphql":"2.2.0"}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/integration-technologies/images/change-node-prepare-data-chart.png b/nuxt/content-guides/node-red/integration-technologies/images/change-node-prepare-data-chart.png
similarity index 100%
rename from src/node-red/integration-technologies/images/change-node-prepare-data-chart.png
rename to nuxt/content-guides/node-red/integration-technologies/images/change-node-prepare-data-chart.png
diff --git a/src/node-red/integration-technologies/images/change-node-set-downloads-payload.png b/nuxt/content-guides/node-red/integration-technologies/images/change-node-set-downloads-payload.png
similarity index 100%
rename from src/node-red/integration-technologies/images/change-node-set-downloads-payload.png
rename to nuxt/content-guides/node-red/integration-technologies/images/change-node-set-downloads-payload.png
diff --git a/src/node-red/integration-technologies/images/chart-with-data.png b/nuxt/content-guides/node-red/integration-technologies/images/chart-with-data.png
similarity index 100%
rename from src/node-red/integration-technologies/images/chart-with-data.png
rename to nuxt/content-guides/node-red/integration-technologies/images/chart-with-data.png
diff --git a/src/node-red/integration-technologies/images/dashboard-config-chart.png b/nuxt/content-guides/node-red/integration-technologies/images/dashboard-config-chart.png
similarity index 100%
rename from src/node-red/integration-technologies/images/dashboard-config-chart.png
rename to nuxt/content-guides/node-red/integration-technologies/images/dashboard-config-chart.png
diff --git a/src/node-red/integration-technologies/images/dashboard-config-ui-base.png b/nuxt/content-guides/node-red/integration-technologies/images/dashboard-config-ui-base.png
similarity index 100%
rename from src/node-red/integration-technologies/images/dashboard-config-ui-base.png
rename to nuxt/content-guides/node-red/integration-technologies/images/dashboard-config-ui-base.png
diff --git a/src/node-red/integration-technologies/images/dashboard-config-ui-group.png b/nuxt/content-guides/node-red/integration-technologies/images/dashboard-config-ui-group.png
similarity index 100%
rename from src/node-red/integration-technologies/images/dashboard-config-ui-group.png
rename to nuxt/content-guides/node-red/integration-technologies/images/dashboard-config-ui-group.png
diff --git a/src/node-red/integration-technologies/images/http-get-npmapi.png b/nuxt/content-guides/node-red/integration-technologies/images/http-get-npmapi.png
similarity index 100%
rename from src/node-red/integration-technologies/images/http-get-npmapi.png
rename to nuxt/content-guides/node-red/integration-technologies/images/http-get-npmapi.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-change-node-formating-sensor-data.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change-node-formating-sensor-data.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-change-node-formating-sensor-data.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change-node-formating-sensor-data.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-change-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-change-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-change1-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change1-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-change1-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change1-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-change2-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change2-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-change2-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-change2-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-dht-sensor-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-dht-sensor-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-dht-sensor-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-dht-sensor-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-diagram.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-diagram.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-diagram.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-diagram.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-flow.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-flow.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-flow.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-flow.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-function-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-function-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-function-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-function-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-how-webhook-works.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-how-webhook-works.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-how-webhook-works.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-how-webhook-works.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-http-in-node-endpoint-for-receiving-data-from-server-2.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-http-in-node-endpoint-for-receiving-data-from-server-2.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-http-in-node-endpoint-for-receiving-data-from-server-2.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-http-in-node-endpoint-for-receiving-data-from-server-2.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-request-node-sending-request-to-server3.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-request-node-sending-request-to-server3.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-request-node-sending-request-to-server3.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-request-node-sending-request-to-server3.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-scheduled-maintenance-table-dashboard-view.webm b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-scheduled-maintenance-table-dashboard-view.webm
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-scheduled-maintenance-table-dashboard-view.webm
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-scheduled-maintenance-table-dashboard-view.webm
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-sensor2-inject-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-sensor2-inject-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-sensor2-inject-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-sensor2-inject-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-server-1-instance.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-server-1-instance.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-server-1-instance.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-server-1-instance.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-server-2-instance.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-server-2-instance.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-server-2-instance.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-server-2-instance.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-server-3-instance.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-server-3-instance.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-server-3-instance.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-server-3-instance.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-switch-node.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-switch-node.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-switch-node.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-switch-node.png
diff --git a/src/node-red/integration-technologies/images/using-webhook-with-node-red-webhook-trigger.png b/nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-webhook-trigger.png
similarity index 100%
rename from src/node-red/integration-technologies/images/using-webhook-with-node-red-webhook-trigger.png
rename to nuxt/content-guides/node-red/integration-technologies/images/using-webhook-with-node-red-webhook-trigger.png
diff --git a/src/node-red/integration-technologies/images/webhooks-node-red.png b/nuxt/content-guides/node-red/integration-technologies/images/webhooks-node-red.png
similarity index 100%
rename from src/node-red/integration-technologies/images/webhooks-node-red.png
rename to nuxt/content-guides/node-red/integration-technologies/images/webhooks-node-red.png
diff --git a/src/node-red/integration-technologies/rest.md b/nuxt/content-guides/node-red/integration-technologies/rest.md
similarity index 95%
rename from src/node-red/integration-technologies/rest.md
rename to nuxt/content-guides/node-red/integration-technologies/rest.md
index ed358518b4..9066569389 100644
--- a/src/node-red/integration-technologies/rest.md
+++ b/nuxt/content-guides/node-red/integration-technologies/rest.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: REST API
- parent: "Integration Technologies"
+title: Creating REST API's with Node-RED
+navTitle: REST API
+navOrder: 50
meta:
- title: Creating REST API's with Node-RED
- description: Learn how to create REST APIs in Node-RED and fetch data from an API.
- keywords: node-red, flowfuse, rest api
+ description: Learn how to create REST APIs in Node-RED and fetch data from an API.
+ keywords: node-red, flowfuse, rest api
---
-# {{meta.title}}
+# Creating REST API's with Node-RED
REST APIs are how applications talk to each other over the web. They use standard HTTP methods (GET, POST, PUT, DELETE) to send and receive data, usually in JSON format. This guide shows you how to build your own REST APIs in Node-RED and how to pull data from existing APIs.
@@ -19,9 +18,11 @@ REST APIs are how applications talk to each other over the web. They use standar
3. Then Drag an http response node, in it and set the status code if want.
4. Connect the "http-in" node's output to the input of the function node and the function node's output to the input of the http response node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"27333f67794bdc72","type":"http in","z":"977143edb097b685","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":320,"y":220,"wires":[["f351033226953150"]]},{"id":"a7ee48616541a36a","type":"http response","z":"977143edb097b685","name":"","statusCode":"200","headers":{},"x":760,"y":220,"wires":[]},{"id":"dcfc8d1126f139d5","type":"comment","z":"977143edb097b685","name":"Http-in node created API sending todo list as response","info":"","x":540,"y":140,"wires":[]},{"id":"f351033226953150","type":"change","z":"977143edb097b685","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"todos","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":220,"wires":[["a7ee48616541a36a"]]}]
-{% endrenderFlow %}
+```
+::
## Creating a POST, PUT, and DELETE API
@@ -30,17 +31,23 @@ REST APIs are how applications talk to each other over the web. They use standar
3. Drag an "HTTP Response" node onto the workspace. Configure it and set the status code if needed.
4. Connect the output of the "HTTP In" node to the input of the node handling your application logic (e.g., Change node for DELETE operation). Then, connect the output of this node to the input of the HTTP Response node.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"8893fc84b3391b34","type":"http in","z":"977143edb097b685","name":"","url":"/todo/delete","method":"delete","upload":true,"swaggerDoc":"","x":250,"y":720,"wires":[["088808484586fdc1"]]},{"id":"04a60e7af4d6d522","type":"http response","z":"977143edb097b685","name":"","statusCode":"204","headers":{},"x":740,"y":720,"wires":[]},{"id":"088808484586fdc1","type":"function","z":"977143edb097b685","name":"Delete the todo item","func":"let todoList = global.get('todos') || [];\nlet id = msg.payload.id;\n\n// Find the index of the item to delete\nlet index = todoList.findIndex(item => item.id === id);\n\nif (index !== -1) {\n // Remove the item from the todoList array\n todoList.splice(index, 1);\n global.set('todos', todoList);\n msg.payload = \"Item deleted successfully.\";\n msg.statusCode = 204; // No Content\n} else {\n msg.payload = \"Item not found.\";\n msg.statusCode = 404; // Not Found\n}\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":720,"wires":[["04a60e7af4d6d522"]]}]
-{% endrenderFlow %}
+```
+::
-{% renderFlow %}
+::render-flow
+```json
[{"id":"825a6296456b7c27","type":"http in","z":"977143edb097b685","name":"","url":"/todo","method":"post","upload":true,"swaggerDoc":"","x":260,"y":1460,"wires":[["0133494821cf99ca","9fe48410514631f8"]]},{"id":"0133494821cf99ca","type":"debug","z":"977143edb097b685","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":540,"y":1420,"wires":[]},{"id":"5284a06365a7f8f7","type":"http response","z":"977143edb097b685","name":"","statusCode":"201","headers":{},"x":860,"y":1500,"wires":[]},{"id":"9fe48410514631f8","type":"function","z":"977143edb097b685","name":"store todo in todolist ","func":"let todoList = global.get('todos') || [];\nlet newTodo = msg.payload;\n\ntodoList.push(newTodo);\nglobal.set('todos',todoList)\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":1500,"wires":[["5284a06365a7f8f7"]]}]
-{% endrenderFlow %}
+```
+::
-{% renderFlow %}
+::render-flow
+```json
[{"id":"8893fc84b3391b34","type":"http in","z":"977143edb097b685","name":"","url":"/todo/update","method":"put","upload":true,"swaggerDoc":"","x":400,"y":280,"wires":[["088808484586fdc1"]]},{"id":"04a60e7af4d6d522","type":"http response","z":"977143edb097b685","name":"","statusCode":"200","headers":{},"x":900,"y":280,"wires":[]},{"id":"088808484586fdc1","type":"function","z":"977143edb097b685","name":"update the todo item","func":"let todoList = global.get('todos') || [];\nlet id = msg.payload.id;\nlet newTodo = msg.payload.newtodo;\n\n// Find the index of the item to update\nlet index = todoList.findIndex(item => item.id === id);\n\nif (index !== -1) {\n // Update the todo item\n todoList[index].task = newTodo;\n global.set('todos', todoList);\n msg.payload = \"Item updated successfully.\";\n msg.statusCode = 200; // OK\n} else {\n msg.payload = \"Item not found.\";\n msg.statusCode = 404; // Not Found\n}\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":280,"wires":[["04a60e7af4d6d522"]]},{"id":"ea3e7e96bbccf530","type":"comment","z":"977143edb097b685","name":"Http in node created api for updating the todo item","info":"","x":650,"y":200,"wires":[]}]
-{% endrenderFlow %}
+```
+::
For more details, refer to the [CRUD API Blueprint](/blueprints/getting-started/crud/), where we have created CRUD APIs to store, retrieve, delete, and update the data from MongoDB database.
@@ -112,9 +119,11 @@ For the example we will fetch the data of [Node-RED Dashboard 2.0](/platform/das
A simple flow to fetch data from npm registry would be:
-{% renderFlow %}
+::render-flow
+```json
[{"id":"32b083d0ca67265f","type":"inject","z":"977143edb097b685","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":240,"y":1100,"wires":[["53db14b9a848d5ce"]]},{"id":"53db14b9a848d5ce","type":"http request","z":"977143edb097b685","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://api.npmjs.org/downloads/range/last-month/@flowforge/node-red-dashboard","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":410,"y":1100,"wires":[["9e08fa8d25a19f24"]]},{"id":"9e08fa8d25a19f24","type":"debug","z":"977143edb097b685","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":580,"y":1100,"wires":[]}]
-{% endrenderFlow %}
+```
+::
Where we paste the API URL into the settings panel:
@@ -123,13 +132,13 @@ Where we paste the API URL into the settings panel:
When running this flow you'll see a blob of text in the `Debug` pane. This is a
great first start, but a blob isn't useful for the rest of the flow.
-We need to parse the data as JSON. While the [JSON node](/node-red/core-nodes/json)
+We need to parse the data as JSON. While the [JSON node](/docs/node-red/core-nodes/json)
would work, the HTTP request node can do this natively. Let `a parsed JSON object`
the `Return` settings of the HTTP request node.
So now we got the data, and a little more than we need, so let's change the
message output to keep only what we're interested in; `payload.downloads`. To
-do this, we'll use the [change node](/node-red/core-nodes/change).
+do this, we'll use the [change node](/docs/node-red/core-nodes/change).

@@ -154,7 +163,7 @@ The default theme is great, so just accept that, and save all dialogs to continu
#### Normalizing the data
The data for the chart needs to be changed before we can show it. The messages should have a `x` and `y` key. So let's prepare the data with
-a combination of the [Split](/node-red/core-nodes/split) and change node.
+a combination of the [Split](/docs/node-red/core-nodes/split) and change node.
The Split node with the default configuration allows to 30 elements of the array
to be mapped individually. The change node will set the `payload.x` and `payload.y`
@@ -170,7 +179,7 @@ Connect the change node output to a new chart node, and voila:
While we created a chart and it has some data, there's one more thing to explain.
How can the data be kept up-to-date? It's straight forward to have the `Inject`
-node [run every night](/node-red/core-nodes/inject/),
+node [run every night](/docs/node-red/core-nodes/inject/),
but the chart would now have multiple data points
for the same day. This paints multiple lines on top of each other. While that works,
the hover of the chart will display the duplication and it's wastefull.
@@ -180,6 +189,8 @@ So before we update the chart we need to send a message to the chart where the
That way the chart is emptied first, and right afterwards it will
receive the new data to write.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"da9a67e8c3ea7742","type":"inject","z":"977143edb097b685","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"00 12 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"[]","payloadType":"json","x":250,"y":960,"wires":[["efd22a89abc3c06f","a6851a41dba2c39b"]]},{"id":"a6851a41dba2c39b","type":"http request","z":"977143edb097b685","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://api.npmjs.org/downloads/range/last-month/@flowforge/node-red-dashboard","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":430,"y":960,"wires":[["7931f7457880f7c3"]]},{"id":"7931f7457880f7c3","type":"change","z":"977143edb097b685","name":"Only get the Downloads","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.downloads","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":960,"wires":[["74e3b15c7b09726a"]]},{"id":"74e3b15c7b09726a","type":"link out","z":"977143edb097b685","name":"link out 1","mode":"link","links":["43e4aff34b989e83"],"x":815,"y":960,"wires":[]},{"id":"43e4aff34b989e83","type":"link in","z":"977143edb097b685","name":"Normalize daily data","links":["74e3b15c7b09726a"],"x":195,"y":1040,"wires":[["ca8c62bfbdf75715"]]},{"id":"ca8c62bfbdf75715","type":"split","z":"977143edb097b685","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":310,"y":1040,"wires":[["3f462d2c7e3bca50"]]},{"id":"3f462d2c7e3bca50","type":"change","z":"977143edb097b685","name":"Prepare data for the chart","rules":[{"t":"set","p":"payload.x","pt":"msg","to":"$toMillis(payload.day)","tot":"jsonata"},{"t":"set","p":"payload.y","pt":"msg","to":"payload.downloads","tot":"msg"},{"t":"delete","p":"payload.day","pt":"msg"},{"t":"delete","p":"payload.downloads","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1040,"wires":[["f4e6a85b8cb8dac0"]]},{"id":"4b71fc28c2da66e7","type":"ui-chart","z":"977143edb097b685","group":"bac8effac57694e1","name":"","label":"Daily Downloads","order":9007199254740991,"chartType":"line","xAxisType":"time","removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":0,"height":0,"className":"","x":330,"y":1120,"wires":[[]]},{"id":"f4e6a85b8cb8dac0","type":"link out","z":"977143edb097b685","name":"link out 2","mode":"link","links":["6f7068445bfe4311"],"x":675,"y":1040,"wires":[]},{"id":"6f7068445bfe4311","type":"link in","z":"977143edb097b685","name":"Update the chart","links":["f4e6a85b8cb8dac0","79067215ee592ec9","efd22a89abc3c06f"],"x":195,"y":1120,"wires":[["4b71fc28c2da66e7"]]},{"id":"efd22a89abc3c06f","type":"link out","z":"977143edb097b685","name":"link out 3","mode":"link","links":["6f7068445bfe4311"],"x":415,"y":920,"wires":[]},{"id":"bac8effac57694e1","type":"ui-group","name":"NPM Downloads","page":"f10b4d0259e43aeb","width":"6","height":"1","order":-1},{"id":"f10b4d0259e43aeb","type":"ui-page","name":"Main","ui":"cb79bc4520925e32","path":"/","layout":"grid","theme":"2c5d702b11de7dd1","order":-1},{"id":"cb79bc4520925e32","type":"ui-base","name":"My UI","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false},{"id":"2c5d702b11de7dd1","type":"ui-theme","name":"Theme Name","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]
-{% endrenderFlow %}
\ No newline at end of file
+```
+::
diff --git a/src/node-red/integration-technologies/webhook.md b/nuxt/content-guides/node-red/integration-technologies/webhook.md
similarity index 96%
rename from src/node-red/integration-technologies/webhook.md
rename to nuxt/content-guides/node-red/integration-technologies/webhook.md
index 41cb0e98f4..43e4b985e3 100644
--- a/src/node-red/integration-technologies/webhook.md
+++ b/nuxt/content-guides/node-red/integration-technologies/webhook.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: Webhook
- parent: "Integration Technologies"
+title: Using Webhook with Node-RED
+navTitle: Webhook
+navOrder: 50
meta:
- title: Using Webhook with Node-RED
- description: Learn how to seamlessly integrate webhooks into your Node-RED applications for automating tasks and enhancing communication.
- keywords: node-red, flowfuse, webhooks, automation, event-driven
-image: /node-red/hardware/images/webhooks-node-red.png
+ description: Learn how to seamlessly integrate webhooks into your Node-RED applications for automating tasks and enhancing communication.
+ keywords: node-red, flowfuse, webhooks, automation, event-driven
+image: /docs/node-red/hardware/images/webhooks-node-red.png
---
-# {{meta.title}}
+# Using Webhook with Node-RED
Webhooks let different systems talk to each other automatically when something happens. Instead of constantly asking "anything new?", one system just tells the other "hey, this just happened." This guide shows you how to set up webhooks in Node-RED, using a real manufacturing example where temperature sensors trigger maintenance alerts.
@@ -173,4 +172,4 @@ return msg;
1. With your flow updated to include the above, click the "Deploy" button in the top-right corner of the Node-RED Editor in each Node-RED instance.
2. In server 3 Node-RED instance (Maintenance scheduling system), Locate the 'Open Dashboard' button at the top-right corner of the Dashboard 2.0 sidebar and click on it to navigate to the dashboard.
-
\ No newline at end of file
+
diff --git a/src/node-red/keyboard/index.md b/nuxt/content-guides/node-red/keyboard/README.md
similarity index 94%
rename from src/node-red/keyboard/index.md
rename to nuxt/content-guides/node-red/keyboard/README.md
index 5063be5d68..265bfe535d 100644
--- a/src/node-red/keyboard/index.md
+++ b/nuxt/content-guides/node-red/keyboard/README.md
@@ -1,11 +1,10 @@
---
-eleventyNavigation:
- key: Keyboard Shortcuts
- order: 3
+title: Node-RED Keyboard Shortcuts
+navTitle: Keyboard shortcuts
+navOrder: 11
meta:
- title: Node-RED Keyboard Shortcuts
- description: A comprehensive list of keyboard shortcuts for Node-RED to enhance productivity and streamline workflow.
- keywords: node-red, node-red keyboard shortcuts
+ description: A comprehensive list of keyboard shortcuts for Node-RED to enhance productivity and streamline workflow.
+ keywords: node-red, node-red keyboard shortcuts
---
# Node-RED Keyboard Shortcuts
@@ -110,4 +109,4 @@ Node-RED lets you customize keyboard shortcuts to fit your workflow, making it e
1. To set custom keyboard shortcuts, go to the keyboard settings in the user settings. Click `Shift + ?` or click the top-right menu icon and select "Settings." In the settings menu, switch to "Keyboard Settings."
2. In the Keyboard Settings, you will see actions with assigned shortcuts as well as those that are unassigned. To change or set shortcuts, click on "Unassigned" or the existing shortcut next to the action you want to modify.
-3. Enter your preferred key combination for the action. Then, select the appropriate scope and click the check icon to save your changes. Your new shortcuts will now be active.
\ No newline at end of file
+3. Enter your preferred key combination for the action. Then, select the appropriate scope and click the check icon to save your changes. Your new shortcuts will now be active.
diff --git a/nuxt/content-guides/node-red/notification/README.md b/nuxt/content-guides/node-red/notification/README.md
new file mode 100644
index 0000000000..386a913e0c
--- /dev/null
+++ b/nuxt/content-guides/node-red/notification/README.md
@@ -0,0 +1,20 @@
+---
+title: Notification Services in Node-RED
+navTitle: Notification services
+navOrder: 7
+meta:
+ description: Learn how to integrate various notification services with Node-RED for real-time alerts and messaging.
+ keywords: node-red, notification services, email notifications, Telegram notifications, Slack notifications, WhatsApp notifications
+---
+
+# Notification Services
+
+Real-time notifications are essential in our automation world, helping to keep us informed, responsive, and efficient. Whether it's a critical system alert, a customer inquiry, or a simple reminder, timely notifications can significantly enhance productivity and efficiency. However, managing and integrating such services can be challenging in traditional development environments. Node-RED simplifies this process by supporting a wide range of notification services, including email, Telegram, Slack, WhatsApp, and more.
+
+## Resources
+
+Here are some resources to help you get started with Node-RED on diffrent notification services:
+
+- [Sending and receiving Discord messages with Node-RED](/docs/node-red/notification/discord/): Learn how to send and receive Discord messages with Node-RED.
+- [Sending and receiving emails with Node-RED](/docs/node-red/notification/email/): Learn how to send and receive emails using Node-RED, along with best practices for sending email notifications.
+- [Sending and receiving Telegram messages with Node-RED](/docs/node-red/notification/telegram/): Learn to seamlessly integrate Telegram with Node-RED for messaging. Create bots, obtain chat IDs, and send/receive messages, including group messaging.
diff --git a/src/node-red/notification/discord.md b/nuxt/content-guides/node-red/notification/discord.md
similarity index 91%
rename from src/node-red/notification/discord.md
rename to nuxt/content-guides/node-red/notification/discord.md
index a95f21645e..2cd65028e0 100644
--- a/src/node-red/notification/discord.md
+++ b/nuxt/content-guides/node-red/notification/discord.md
@@ -1,21 +1,20 @@
---
-eleventyNavigation:
- key: Discord
- parent: "Notification Services"
+title: Sending and receiving Discord messages with Node-RED
+navTitle: Discord
+navOrder: 50
meta:
- title: Sending and receiving Discord messages with Node-RED
- description: Learn how to send and receive Discord messages with Node-RED.
- keywords: nodered discord, node red discord
-image: /node-red/notification/images/sending-and-receiving-discord-message-with-node-red.png
+ description: Learn how to send and receive Discord messages with Node-RED.
+ keywords: nodered discord, node red discord
+image: /docs/node-red/notification/images/sending-and-receiving-discord-message-with-node-red.png
---
-# {{meta.title}}
+# Sending and receiving Discord messages with Node-RED
This guide explains how to integrate Discord with Node-RED to send and receive messages. You'll learn how to configure a Discord bot, send messages to users and channels, and handle incoming messages.
Discord is commonly used for notifications in IoT applications. This document covers the setup process and includes troubleshooting steps for common integration issues.
-For information on integrating other notification services, see the guides on [Email](/node-red/notification/email/) and [Telegram](/node-red/notification/telegram/).
+For information on integrating other notification services, see the guides on [Email](/docs/node-red/notification/email/) and [Telegram](/docs/node-red/notification/telegram/).
@@ -69,7 +68,7 @@ To send a message to a user, you will need the ID of that user. Before copying t
3. Set the `msg.payload` to the message you want to send and the `msg.user` to the user ID of the user you want to send the message to.
4. Connect the Inject node's output to the input of the DiscordMessageManager node.
-
+
## Sending messages to the Discord server
@@ -98,7 +97,7 @@ To send a message to the Discord server, you have to make sure that your bot is
3. Connect the Inject node's output to the input of the DiscordMessageManager node.
-
+
## Receiving messages from Discord
@@ -108,13 +107,15 @@ To send a message to the Discord server, you have to make sure that your bot is
After deploying the flow, you will start receiving messages sent to your bot. In the debug panel in the sidebar, you will see the message object printed for each message, which contains different objects. Each object shows different details; for example, the author object contains details about the sender, and the channel object includes information of the channel if the message was sent in a channel.
-
+
Below, I have provided the complete flow we built throughout the guide. Make sure to replace the environment variable 'BOT_TOKEN' with your actual bot token.
-{% renderFlow %}
+::render-flow
+```json
[{"id":"7a8dd49f9614608e","type":"inject","z":"4674ed668685adf6","name":"Sending mesasge to Discord server 's channel","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, This is from Node-RED","payloadType":"str","x":570,"y":420,"wires":[["3984c20a52db033f"]]},{"id":"3984c20a52db033f","type":"discordMessageManager","z":"4674ed668685adf6","name":"","channel":"56454645657765656","token":"","x":930,"y":420,"wires":[["b99ae75425047b6b"]]},{"id":"b99ae75425047b6b","type":"debug","z":"4674ed668685adf6","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1200,"y":420,"wires":[]},{"id":"6af740e11aba6ca2","type":"inject","z":"4674ed668685adf6","name":"Sending message to Discord user","props":[{"p":"payload"},{"p":"user","v":"65454534534345365","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, Sumit","payloadType":"str","x":550,"y":320,"wires":[["f69363f341b62333"]]},{"id":"f69363f341b62333","type":"discordMessageManager","z":"4674ed668685adf6","name":"","channel":"","token":"","x":930,"y":320,"wires":[["c5001a783992ae01"]]},{"id":"c5001a783992ae01","type":"debug","z":"4674ed668685adf6","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1220,"y":320,"wires":[]},{"id":"820b70826545a401","type":"debug","z":"4674ed668685adf6","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1020,"y":540,"wires":[]},{"id":"f044d3784ef8c74a","type":"discordMessage","z":"4674ed668685adf6","name":"","channelIdFilter":"","token":"","x":720,"y":540,"wires":[["820b70826545a401"]]}]
-{% endrenderFlow %}
+```
+::
## Debugging and Troubleshooting
diff --git a/src/node-red/notification/email.md b/nuxt/content-guides/node-red/notification/email.md
similarity index 95%
rename from src/node-red/notification/email.md
rename to nuxt/content-guides/node-red/notification/email.md
index 82bc9d1ee0..a116e20e7f 100644
--- a/src/node-red/notification/email.md
+++ b/nuxt/content-guides/node-red/notification/email.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: Email
- parent: "Notification Services"
+title: Sending and receiving emails with Node-RED
+navTitle: Email
+navOrder: 50
meta:
- title: Sending and receiving emails with Node-RED
- description: Learn how to send and receive emails using Node-RED, along with best practices for sending email notifications.
- keywords: node-red, gmail, flowfuse device agent, anti-spam measures, email]
-image: /node-red/notification/images/email_node-red.png
+ description: Learn how to send and receive emails using Node-RED, along with best practices for sending email notifications.
+ keywords: node-red, gmail, flowfuse device agent, anti-spam measures, email]
+image: /docs/node-red/notification/images/email_node-red.png
---
-# {{meta.title}}
+# Sending and receiving emails with Node-RED
This guide shows you how to integrate email with Node-RED for sending and receiving messages. You'll learn how to configure email nodes, set up Gmail integration, and follow best practices to ensure your notifications reach their destination.
@@ -157,4 +156,4 @@ Even legitimate emails can sometimes trigger spam filters. Follow these practice
- **Authenticate your emails** - Implement SPF, DKIM, and DMARC protocols to verify your email's legitimacy and improve deliverability.
- **Manage your sending frequency** - Avoid sending too many emails in a short period. Maintain a consistent schedule and ensure each message provides value.
- **Keep your email list clean** - Regularly remove invalid or inactive addresses. High bounce rates and spam complaints damage your sender reputation.
-- **Monitor your reputation** - Use tools like SenderScore or Google Postmaster Tools to track your sender reputation and identify potential issues before they affect delivery.
\ No newline at end of file
+- **Monitor your reputation** - Use tools like SenderScore or Google Postmaster Tools to track your sender reputation and identify potential issues before they affect delivery.
diff --git a/src/node-red/notification/images/discord-with-node-red-bot-reset-token.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-bot-reset-token.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-bot-reset-token.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-bot-reset-token.png
diff --git a/src/node-red/notification/images/discord-with-node-red-channel-id.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-channel-id.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-channel-id.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-channel-id.png
diff --git a/src/node-red/notification/images/discord-with-node-red-conform-add-to-server.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-conform-add-to-server.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-conform-add-to-server.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-conform-add-to-server.png
diff --git a/src/node-red/notification/images/discord-with-node-red-create-app.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-create-app.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-create-app.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-create-app.png
diff --git a/src/node-red/notification/images/discord-with-node-red-developer-mode.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-developer-mode.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-developer-mode.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-developer-mode.png
diff --git a/src/node-red/notification/images/discord-with-node-red-error-1.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-error-1.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-error-1.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-error-1.png
diff --git a/src/node-red/notification/images/discord-with-node-red-error-2.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-error-2.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-error-2.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-error-2.png
diff --git a/src/node-red/notification/images/discord-with-node-red-error-3.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-error-3.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-error-3.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-error-3.png
diff --git a/src/node-red/notification/images/discord-with-node-red-new-application-button.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-new-application-button.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-new-application-button.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-new-application-button.png
diff --git a/src/node-red/notification/images/discord-with-node-red-oauth-copy-url.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-oauth-copy-url.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-oauth-copy-url.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-oauth-copy-url.png
diff --git a/src/node-red/notification/images/discord-with-node-red-oauth-scope.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-oauth-scope.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-oauth-scope.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-oauth-scope.png
diff --git a/src/node-red/notification/images/discord-with-node-red-outh-permission.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-outh-permission.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-outh-permission.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-outh-permission.png
diff --git a/src/node-red/notification/images/discord-with-node-red-privillage-itents.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-privillage-itents.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-privillage-itents.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-privillage-itents.png
diff --git a/src/node-red/notification/images/discord-with-node-red-receiving-messages.webm b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-receiving-messages.webm
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-receiving-messages.webm
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-receiving-messages.webm
diff --git a/src/node-red/notification/images/discord-with-node-red-select-the-server.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-select-the-server.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-select-the-server.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-select-the-server.png
diff --git a/src/node-red/notification/images/discord-with-node-red-sending-msg-to-server.webm b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-sending-msg-to-server.webm
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-sending-msg-to-server.webm
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-sending-msg-to-server.webm
diff --git a/src/node-red/notification/images/discord-with-node-red-sending-msg-to-user.webm b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-sending-msg-to-user.webm
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-sending-msg-to-user.webm
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-sending-msg-to-user.webm
diff --git a/src/node-red/notification/images/discord-with-node-red-user-id.png b/nuxt/content-guides/node-red/notification/images/discord-with-node-red-user-id.png
similarity index 100%
rename from src/node-red/notification/images/discord-with-node-red-user-id.png
rename to nuxt/content-guides/node-red/notification/images/discord-with-node-red-user-id.png
diff --git a/src/node-red/notification/images/email_node-red.png b/nuxt/content-guides/node-red/notification/images/email_node-red.png
similarity index 100%
rename from src/node-red/notification/images/email_node-red.png
rename to nuxt/content-guides/node-red/notification/images/email_node-red.png
diff --git a/src/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-in-node-configuration.png b/nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-in-node-configuration.png
similarity index 100%
rename from src/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-in-node-configuration.png
rename to nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-in-node-configuration.png
diff --git a/src/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-node-configuration.png b/nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-node-configuration.png
similarity index 100%
rename from src/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-node-configuration.png
rename to nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-e-mail-node-configuration.png
diff --git a/src/node-red/notification/images/sending-and-receiving-email-with-node-red-gmail-inbox.png b/nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-gmail-inbox.png
similarity index 100%
rename from src/node-red/notification/images/sending-and-receiving-email-with-node-red-gmail-inbox.png
rename to nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-gmail-inbox.png
diff --git a/src/node-red/notification/images/sending-and-receiving-email-with-node-red-inject-node.png b/nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-inject-node.png
similarity index 100%
rename from src/node-red/notification/images/sending-and-receiving-email-with-node-red-inject-node.png
rename to nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-inject-node.png
diff --git a/src/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red-flow.png b/nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red-flow.png
similarity index 100%
rename from src/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red-flow.png
rename to nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red-flow.png
diff --git a/src/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red_setting_environment_variables.png b/nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red_setting_environment_variables.png
similarity index 100%
rename from src/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red_setting_environment_variables.png
rename to nuxt/content-guides/node-red/notification/images/sending-and-receiving-email-with-node-red-node-red_setting_environment_variables.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-activating-bot.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-activating-bot.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-activating-bot.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-activating-bot.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-botfather.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-botfather.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-botfather.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-botfather.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-change-node.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-change-node.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-change-node.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-change-node.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-enabling-send-error-to-second-option.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-enabling-send-error-to-second-option.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-enabling-send-error-to-second-option.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-enabling-send-error-to-second-option.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-flow.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-flow.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-flow.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-flow.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-flowfue-instance-settings.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-flowfue-instance-settings.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-flowfue-instance-settings.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-flowfue-instance-settings.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-getmyid.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-getmyid.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-getmyid.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-getmyid.png
diff --git a/src/node-red/notification/images/sending-telegram-with-node-red-telegram-node-configuration.png b/nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-telegram-node-configuration.png
similarity index 100%
rename from src/node-red/notification/images/sending-telegram-with-node-red-telegram-node-configuration.png
rename to nuxt/content-guides/node-red/notification/images/sending-telegram-with-node-red-telegram-node-configuration.png
diff --git a/src/node-red/notification/images/telegram_node-red.png b/nuxt/content-guides/node-red/notification/images/telegram_node-red.png
similarity index 100%
rename from src/node-red/notification/images/telegram_node-red.png
rename to nuxt/content-guides/node-red/notification/images/telegram_node-red.png
diff --git a/src/node-red/notification/telegram.md b/nuxt/content-guides/node-red/notification/telegram.md
similarity index 93%
rename from src/node-red/notification/telegram.md
rename to nuxt/content-guides/node-red/notification/telegram.md
index 5915a23aa7..6e0387831d 100644
--- a/src/node-red/notification/telegram.md
+++ b/nuxt/content-guides/node-red/notification/telegram.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: Telegram
- parent: "Notification Services"
+title: Sending and receiving Telegram messages with Node-RED
+navTitle: Telegram
+navOrder: 50
meta:
- title: Sending and receiving Telegram messages with Node-RED
- description: Learn to seamlessly integrate Telegram with Node-RED for messaging. Create bots, obtain chat IDs, and send/receive messages, including group messaging.
- keywords: node-red, telegram, bot
-image: /node-red/notification/images/telegram_node-red.png
+ description: Learn to seamlessly integrate Telegram with Node-RED for messaging. Create bots, obtain chat IDs, and send/receive messages, including group messaging.
+ keywords: node-red, telegram, bot
+image: /docs/node-red/notification/images/telegram_node-red.png
---
-# {{meta.title}}
+# Sending and receiving Telegram messages with Node-RED
Telegram has become a popular choice for messaging in home automation applications. This guide shows you how to integrate Telegram with Node-RED, covering bot creation, chat ID retrieval, and both sending and receiving messages.
@@ -102,4 +101,4 @@ Environment variables keep your sensitive information secure by preventing it fr
1. Deploy the flow by clicking the Deploy button in the top-right corner.
-Your Telegram bot is now ready to use. Click the Inject button to send a message, and you'll receive a notification in Telegram. You can also check your bot's chat to see messages sent via Node-RED. To test receiving messages, send a message to your bot and watch the Debug panel display the message object containing the message content and additional information.
\ No newline at end of file
+Your Telegram bot is now ready to use. Click the Inject button to send a message, and you'll receive a notification in Telegram. You can also check your bot's chat to see messages sent via Node-RED. To test receiving messages, send a message to your bot and watch the Debug panel display the message object containing the message content and additional information.
diff --git a/src/node-red/peripheral/index.md b/nuxt/content-guides/node-red/peripheral/README.md
similarity index 56%
rename from src/node-red/peripheral/index.md
rename to nuxt/content-guides/node-red/peripheral/README.md
index 56fe7db2a9..6e2071b23a 100644
--- a/src/node-red/peripheral/index.md
+++ b/nuxt/content-guides/node-red/peripheral/README.md
@@ -1,13 +1,12 @@
---
-eleventyNavigation:
- key: "Peripheral Devices"
- order: 7
+title: Integrating Node-RED with Peripheral Devices
+navTitle: Peripheral devices
+navOrder: 9
meta:
- title: Integrating Node-RED with Peripheral Devices
- description: Learn how to integrate Node-RED with various peripheral devices, including keyboards, mice, printers, and more.
- keywords: node-red, flowfuse, peripheral devices, webcam, barcode scanner
+ description: Learn how to integrate Node-RED with various peripheral devices, including keyboards, mice, printers, and more.
+ keywords: node-red, flowfuse, peripheral devices, webcam, barcode scanner
---
# Peripheral Devices
-Node-RED supports a wide range of peripheral devices, allowing users to connect Node-RED to many external inputs and outputs. This enables the creation of interactive and automated systems where data from peripheral devices can trigger actions or responses in the digital realm. By bridging the gap between software and hardware, Node-RED expands its applicability to a wide range of use cases.
\ No newline at end of file
+Node-RED supports a wide range of peripheral devices, allowing users to connect Node-RED to many external inputs and outputs. This enables the creation of interactive and automated systems where data from peripheral devices can trigger actions or responses in the digital realm. By bridging the gap between software and hardware, Node-RED expands its applicability to a wide range of use cases.
diff --git a/src/node-red/peripheral/ardiuno.md b/nuxt/content-guides/node-red/peripheral/ardiuno.md
similarity index 91%
rename from src/node-red/peripheral/ardiuno.md
rename to nuxt/content-guides/node-red/peripheral/ardiuno.md
index ab42f332fa..5e3eafb2ce 100644
--- a/src/node-red/peripheral/ardiuno.md
+++ b/nuxt/content-guides/node-red/peripheral/ardiuno.md
@@ -1,15 +1,14 @@
---
-eleventyNavigation:
- key: "Arduino"
- parent: "Peripheral Devices"
+title: Connecting Arduino to Node-RED
+navTitle: Arduino
+navOrder: 50
meta:
- title: Connecting Arduino to Node-RED
- description: Learn how to control and monitor Arduino hardware using Node-RED
- keywords: node-red, flowfuse, ardiuno
-image: /node-red/peripheral/images/barcode-scanner.png
+ description: Learn how to control and monitor Arduino hardware using Node-RED
+ keywords: node-red, flowfuse, ardiuno
+image: /docs/node-red/peripheral/images/barcode-scanner.png
---
-# {{meta.title}}
+# Connecting Arduino to Node-RED
This documentation explains how to use Node-RED to interact with an Arduino board via serial communication using the Firmata protocol. It covers how to write to and read from digital and analog pins using the `node-red-node-arduino` package.
diff --git a/src/node-red/peripheral/barcodescanner.md b/nuxt/content-guides/node-red/peripheral/barcodescanner.md
similarity index 85%
rename from src/node-red/peripheral/barcodescanner.md
rename to nuxt/content-guides/node-red/peripheral/barcodescanner.md
index 0b39e07010..c10b7a7cb8 100644
--- a/src/node-red/peripheral/barcodescanner.md
+++ b/nuxt/content-guides/node-red/peripheral/barcodescanner.md
@@ -1,16 +1,14 @@
---
-metaTitle: "How to Connect a Barcode Scanner to Node-RED"
-eleventyNavigation:
- key: "Barcode Scanner"
- parent: "Peripheral Devices"
+title: How to connect a barcode scanner to your Node-RED application
+navTitle: Barcode Scanner
+navOrder: 50
meta:
- title: How to connect a barcode scanner to your Node-RED application
- description: Learn to seamlessly connect a barcode scanner to Node-RED for efficient data capture and automation.
- keywords: node-red, flowfuse, barcodescanner
-image: /node-red/peripheral/images/barcode-scanner.png
+ description: Learn to seamlessly connect a barcode scanner to Node-RED for efficient data capture and automation.
+ keywords: node-red, flowfuse, barcodescanner
+image: /docs/node-red/peripheral/images/barcode-scanner.png
---
-# {{meta.title}}
+# How to connect a barcode scanner to your Node-RED application
Barcode scanners, functioning as Human Interface Devices (HID) similar to keyboards, offer versatile programming options. Variations of barcode scanners can be seen used from anything from checkout counters, logistics, and to manufacturing erp systems. In our case, we kept it basic and we used one to trigger a Node-RED flow, keeping the process straightforward and efficient. Don't let that limit your imagination though, with QR codes, you can store just about anything including recipes in a JSON structure.
@@ -34,4 +32,4 @@ You could even take it a step further and create a [QR code](https://smalldev.to
## Linux Setup
-Linux users might face a slightly more complicated setup, as access to communication ports isn't always granted by default, and specific drivers are needed for optimal node functionality. This is due to the security around applications having access to specific devices connected to the system. For this, we recommend following the detailed instructions available in the project's [GitHub](https://github.com/gdziuba/node-red-contrib-usbhid) repository.
\ No newline at end of file
+Linux users might face a slightly more complicated setup, as access to communication ports isn't always granted by default, and specific drivers are needed for optimal node functionality. This is due to the security around applications having access to specific devices connected to the system. For this, we recommend following the detailed instructions available in the project's [GitHub](https://github.com/gdziuba/node-red-contrib-usbhid) repository.
diff --git a/src/node-red/peripheral/esp32.md b/nuxt/content-guides/node-red/peripheral/esp32.md
similarity index 97%
rename from src/node-red/peripheral/esp32.md
rename to nuxt/content-guides/node-red/peripheral/esp32.md
index 6741765876..b77381d55c 100644
--- a/src/node-red/peripheral/esp32.md
+++ b/nuxt/content-guides/node-red/peripheral/esp32.md
@@ -1,14 +1,13 @@
---
-eleventyNavigation:
- key: "ESP32"
- parent: "Peripheral Devices"
+title: Connect ESP32 with Node-RED using MQTT
+navTitle: ESP32
+navOrder: 50
meta:
- title: Connect ESP32 with Node-RED using MQTT
description: Learn how to send and receive MQTT messages between ESP32 and Node-RED using FlowFuse.
keywords: node-red, flowfuse, esp32, mqtt, esp32 node-red
---
-# {{meta.title}}
+# Connect ESP32 with Node-RED using MQTT
This document outlines the procedure for establishing MQTT communication between an ESP32 microcontroller and a Node-RED instance.
@@ -58,9 +57,11 @@ This section provides a practical demonstration with an importable Node-RED flow
Import the following JSON into your Node-RED editor. This flow creates a simple dashboard with ON/OFF buttons that publish to the `/esp32/led` topic. You must configure the **`mqtt out`** node with your specific broker credentials.
-{% renderFlow 300 %}
+::render-flow{:height="300"}
+```json
[{"id":"59887a8115c95eae","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"02c25e8a30f9379d","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"cfb2ab9ff30660fc","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094CE","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"d263574af6876c7a","type":"ui-page","name":"ESP32","ui":"02c25e8a30f9379d","path":"/page1","icon":"home","layout":"grid","theme":"cfb2ab9ff30660fc","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":"true","disabled":"false"},{"id":"3ae115ea7ede6827","type":"ui-group","name":"Group 1","page":"d263574af6876c7a","width":"6","height":"1","order":1,"showTitle":false,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"def97b29f5f7baab","type":"mqtt-broker","name":"","broker":"broker.flowfuse.cloud","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"5a9162986a34a4d6","type":"ui-button","z":"59887a8115c95eae","group":"3ae115ea7ede6827","name":"","label":"ON","order":1,"width":"3","height":"2","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"1","payloadType":"num","topic":"topic","topicType":"msg","buttonColor":"green","textColor":"","iconColor":"","enableClick":true,"enablePointerdown":false,"pointerdownPayload":"","pointerdownPayloadType":"str","enablePointerup":false,"pointerupPayload":"","pointerupPayloadType":"str","x":190,"y":120,"wires":[["9239f8a7cca5c858"]]},{"id":"f9c194994d9491a8","type":"ui-button","z":"59887a8115c95eae","group":"3ae115ea7ede6827","name":"","label":"OFF","order":2,"width":"3","height":"2","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"2","payloadType":"num","topic":"topic","topicType":"msg","buttonColor":"red","textColor":"","iconColor":"","enableClick":true,"enablePointerdown":false,"pointerdownPayload":"","pointerdownPayloadType":"str","enablePointerup":false,"pointerupPayload":"","pointerupPayloadType":"str","x":190,"y":160,"wires":[["9239f8a7cca5c858"]]},{"id":"9239f8a7cca5c858","type":"mqtt out","z":"59887a8115c95eae","name":"","topic":"/LedControl","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"def97b29f5f7baab","x":390,"y":140,"wires":[]}]
-{% endrenderFlow %}
+```
+::
### 2. ESP32 Demo Code
diff --git a/src/node-red/peripheral/images/barcode-scanner.png b/nuxt/content-guides/node-red/peripheral/images/barcode-scanner.png
similarity index 100%
rename from src/node-red/peripheral/images/barcode-scanner.png
rename to nuxt/content-guides/node-red/peripheral/images/barcode-scanner.png
diff --git a/src/node-red/peripheral/images/usbhid-barcode-node-red.png b/nuxt/content-guides/node-red/peripheral/images/usbhid-barcode-node-red.png
similarity index 100%
rename from src/node-red/peripheral/images/usbhid-barcode-node-red.png
rename to nuxt/content-guides/node-red/peripheral/images/usbhid-barcode-node-red.png
diff --git a/src/node-red/peripheral/images/usbhid-config-node-red.png b/nuxt/content-guides/node-red/peripheral/images/usbhid-config-node-red.png
similarity index 100%
rename from src/node-red/peripheral/images/usbhid-config-node-red.png
rename to nuxt/content-guides/node-red/peripheral/images/usbhid-config-node-red.png
diff --git a/src/node-red/peripheral/images/usbhid-qr-pizza-order.png b/nuxt/content-guides/node-red/peripheral/images/usbhid-qr-pizza-order.png
similarity index 100%
rename from src/node-red/peripheral/images/usbhid-qr-pizza-order.png
rename to nuxt/content-guides/node-red/peripheral/images/usbhid-qr-pizza-order.png
diff --git a/src/node-red/peripheral/images/usbhid-scanned-barcode.png b/nuxt/content-guides/node-red/peripheral/images/usbhid-scanned-barcode.png
similarity index 100%
rename from src/node-red/peripheral/images/usbhid-scanned-barcode.png
rename to nuxt/content-guides/node-red/peripheral/images/usbhid-scanned-barcode.png
diff --git a/src/node-red/peripheral/images/using_webcam_with_node-red.png b/nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red.png
similarity index 100%
rename from src/node-red/peripheral/images/using_webcam_with_node-red.png
rename to nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red.png
diff --git a/src/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_control.webm b/nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_control.webm
similarity index 100%
rename from src/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_control.webm
rename to nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_control.webm
diff --git a/src/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_passing_payload.webm b/nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_passing_payload.webm
similarity index 100%
rename from src/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_passing_payload.webm
rename to nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red_capturing_images_by_passing_payload.webm
diff --git a/src/node-red/peripheral/images/using_webcam_with_node-red_selecting_different_camera.webm b/nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red_selecting_different_camera.webm
similarity index 100%
rename from src/node-red/peripheral/images/using_webcam_with_node-red_selecting_different_camera.webm
rename to nuxt/content-guides/node-red/peripheral/images/using_webcam_with_node-red_selecting_different_camera.webm
diff --git a/src/node-red/peripheral/webcam.md b/nuxt/content-guides/node-red/peripheral/webcam.md
similarity index 82%
rename from src/node-red/peripheral/webcam.md
rename to nuxt/content-guides/node-red/peripheral/webcam.md
index e06e72b38d..83338b8386 100644
--- a/src/node-red/peripheral/webcam.md
+++ b/nuxt/content-guides/node-red/peripheral/webcam.md
@@ -1,21 +1,14 @@
---
-eleventyNavigation:
- key: Webcam
- parent: "Peripheral Devices"
+title: Using webcam with Node-RED
+navTitle: Webcam
+navOrder: 50
meta:
- title: Using webcam with Node-RED
- description: Learn how to seamlessly connect webcam with Node-RED
- keywords: node-red, flowfuse, webcam, capturing image
-image: /node-red/peripheral/images/using_webcam_with_node-red.png
-certified:
- - "@flowfuse-certified-nodes/rtsp"
-certifiedIntro: >
- This page covers browser webcams through the Dashboard widget. For network
- cameras that stream over RTSP, FlowFuse certifies and maintains a separate
- node:
+ description: Learn how to seamlessly connect webcam with Node-RED
+ keywords: node-red, flowfuse, webcam, capturing image
+image: /docs/node-red/peripheral/images/using_webcam_with_node-red.png
---
-# {{meta.title}}
+# Using webcam with Node-RED
Dashboard 2.0 has introduced its first third-party webcam widget, simplifying the integration of webcam features with Node-RED applications. In this documentation, you will learn how to utilize the ui-webcam widget in your Node-RED applications.
@@ -23,7 +16,9 @@ Additionally, if you are willing to develop your own third-party widget, we have
Install Node-RED Dashboard 2.0. Follow these [instructions](/blog/2024/03/dashboard-getting-started/) to get started.
-{% include "components/certified-node-callout.liquid" %}
+::callout{icon="i-lucide-badge-check"}
+**Certified nodes for this technology.** This page covers browser webcams through the Dashboard widget. For network cameras that stream over RTSP, FlowFuse certifies and maintains a separate node: [rtsp](/integrations/?certified=1)
+::
## Using a webcam custom widget
@@ -46,7 +41,7 @@ This webcam widget offers nice interactive controls that will allow you to inter
3. Once the webcam is active, you can capture images by clicking the button with the camera icon located at the bottom center of the webcam interface.
4. The widget returns a Base64 string containing the captured image in PNG format.
-
+
## Capturing images by passing payload
@@ -57,7 +52,7 @@ This webcam widget offers nice interactive controls that will allow you to inter
By passing the "capture" string as payload, the webcam widget will activate (if it's off) and capture images automatically, without requiring user interaction. This method is commonly used in industrial applications which depend on automated actions.
-
+
## Selecting different camera devices
@@ -66,11 +61,11 @@ The webcam widget also allows you to select different camera devices connected t
1. Click on the ellipsis icon located at the top-right corner of the webcam interface.
2. A dropdown menu will display the connected cameras. Select your preferred camera to use. Additionally, you can turn off the camera by selecting the "Turn camera off" option.
-
+
## Browser support and privacy
- Browser Compatibility: The webcam widget is compatible with all modern browsers, except Internet Explorer. Whether you're using Chrome, Firefox, Safari, or Edge, you can seamlessly integrate webcam features into your Node-RED applications.
- Control Limitation: It's important to note that this widget is designed to interact with webcams directly accessible to the system running Node-RED. For example, if the webcam is connected to a different device or network and not directly accessible to the Node-RED running system, the video stream from that webcam won't be displayed on a dashboard using this widget.
- HTTPS Requirement: When accessing Dashboard 2.0 remotely (not via `localhost`), it's crucial to use HTTPS. Failure to do so may result in the browser blocking access to the webcam.
-- User Permission: Before the webcam can be activated, the browser will prompt the user for permission to access the webcam device. This ensures user privacy and consent before any image capture occurs. The widget cannot capture images until the user has given their permission.
\ No newline at end of file
+- User Permission: Before the webcam can be activated, the browser will prompt the user for permission to access the webcam device. This ensures user privacy and consent before any image capture occurs. The widget cannot capture images until the user has given their permission.
diff --git a/nuxt/content-guides/node-red/protocol/README.md b/nuxt/content-guides/node-red/protocol/README.md
new file mode 100644
index 0000000000..0d6526dac3
--- /dev/null
+++ b/nuxt/content-guides/node-red/protocol/README.md
@@ -0,0 +1,29 @@
+---
+title: Using Different Protocols for Building Applications with Node-RED
+navTitle: Communication protocols
+navOrder: 4
+meta:
+ description: Learn how to leverage various communication protocols with Node-RED for building robust and interconnected applications.
+ keywords: node-red, iot, mqtt, http, coap, websockets, diffrent protocols with node-red
+---
+
+# Using Different Protocols for Building Applications with Node-RED
+
+In IoT development, effective communication between devices is essential. This communication is facilitated by various protocols like MQTT, HTTP, CoAP, and WebSockets. Each protocol brings its own set of strengths and is suited for different IoT scenarios. However, understanding which protocol is suited for what scenario and utilizing it can be quite difficult. That's why we have created this section of resources where you will find documentation on using different communication protocols with Node-RED.
+
+Node-RED, with its intuitive visual programming interface, simplifies the integration of these protocols. Whether you're publishing sensor data over MQTT, triggering HTTP requests, querying CoAP endpoints, or enabling real-time communication with WebSockets, Node-RED provides a flexible and powerful platform.
+
+## Resources
+
+Here are some resources to help you get started with integrating Node-RED with various communication protocols:
+
+- [Building Secure OPC-UA Server in Node-RED.](/docs/node-red/protocol/opc-ua/): Learn how to build Build and Deploy a custom OPC UA Server in Node-RED
+- [Using AMQP with Node-RED](/docs/node-red/protocol/amqp/): Learn how to integrate AMQP with Node-RED for reliable message delivery, advanced routing, and improved data management in your flows.
+- [Using LwM2M with Node-RED](/docs/node-red/protocol/lwm2m/): Learn how to integrate LwM2M with Node-RED for effective IoT device management. This guide covers setup, data handling, and remote commands.
+- [Using Modbus with Node-RED](/docs/node-red/protocol/modbus/): Learn to use Modbus with Node-RED, including how to build a Modbus server and how to send and read data to and from that server.
+- [Using MQTT with Node-RED](/docs/node-red/protocol/mqtt/): Learn how to use MQTT with Node-RED.
+- [Using Websocket with Node-RED](/docs/node-red/protocol/websocket/)
+
+::callout{icon="i-lucide-badge-check"}
+**Certified nodes for these protocols.** Two protocols are also covered by a FlowFuse certified node, maintained for production use. EtherNet/IP, for Rockwell and Allen-Bradley controllers, has no page of its own in this section, and is documented with its certified node: [cip-suite](/integrations/?certified=1), [opcua](/integrations/?certified=1)
+::
diff --git a/src/node-red/protocol/amqp.md b/nuxt/content-guides/node-red/protocol/amqp.md
similarity index 97%
rename from src/node-red/protocol/amqp.md
rename to nuxt/content-guides/node-red/protocol/amqp.md
index 6cdca8a1b1..13a73bc99b 100644
--- a/src/node-red/protocol/amqp.md
+++ b/nuxt/content-guides/node-red/protocol/amqp.md
@@ -1,17 +1,16 @@
---
-eleventyNavigation:
- key: AMQP
- parent: "Communication Protocols"
+title: Using AMQP with Node-RED
+navTitle: AMQP
+navOrder: 50
meta:
- title: Using AMQP with Node-RED
description: Learn how to integrate AMQP with Node-RED for reliable message delivery, advanced routing, and improved data management in your flows.
keywords: node-red amqp, node-red rabbitmq, how to use amqp with node-red, how to use rabbitmq with node-red, rabbitmq node red, amqp node red
-image: /node-red/protocol/images/amqp-with-node-red.png
+image: /docs/node-red/protocol/images/amqp-with-node-red.png
---
-# {{meta.title}}
+# Using AMQP with Node-RED
-Imagine your Node-RED flow working well, handling data from different sources, until suddenly, messages start disappearing or arriving out of order. [MQTT](/node-red/protocol/mqtt/) works fine for basic messaging, but it can struggle in more complex situations where you need delivery guarantees and advanced routing.
+Imagine your Node-RED flow working well, handling data from different sources, until suddenly, messages start disappearing or arriving out of order. [MQTT](/docs/node-red/protocol/mqtt/) works fine for basic messaging, but it can struggle in more complex situations where you need delivery guarantees and advanced routing.
That’s where AMQP comes in. AMQP solves these issues with features that MQTT doesn’t have. In this guide, we'll explain what AMQP is and how to use it with Node-RED.
@@ -108,12 +107,14 @@ Configure the node by dragging an AMQP node onto the canvas. Double-click the no
1. Add two `amqp-in` nodes on to the canvas. Configure one to listen for messages with the `routingKey` of `"zone1"` and the other with `"zone2"`. Both nodes should be set to the `"irrigation_control"` exchange.
2. Connect each `amqp-in` node to a `debug` node to see the received commands for each zone.
-
+
_Video showing the flow that uses the Direct exchange type to send messages and receive messages_
-{% renderFlow %}
+::render-flow
+```json
[{"id":"efe7a260307e6202","type":"amqp-out","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","reconnectOnError":true,"exchangeName":"irrigation_control","exchangeType":"direct","exchangeRoutingKey":"","exchangeRoutingKeyType":"str","exchangeDurable":true,"amqpProperties":"{ \"headers\": {} }","rpcTimeoutMilliseconds":3000,"outputs":0,"x":470,"y":160,"wires":[]},{"id":"538de33f548833ac","type":"inject","z":"807758ec576fbfd8","name":"Send command to zone 1","props":[{"p":"payload"},{"p":"routingKey","v":"zone1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"command\": \"start\" }","payloadType":"json","x":230,"y":100,"wires":[["efe7a260307e6202"]]},{"id":"20cfe04fab562ea9","type":"inject","z":"807758ec576fbfd8","name":"Send command to zone 2","props":[{"p":"payload"},{"p":"routingKey","v":"zone2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"command\": \"stop\" }","payloadType":"str","x":230,"y":240,"wires":[["efe7a260307e6202"]]},{"id":"7a5e0f3a66dc1ccc","type":"amqp-in","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","prefetch":0,"reconnectOnError":true,"noAck":true,"exchangeName":"irrigation_control","exchangeType":"direct","exchangeRoutingKey":"zone1","exchangeDurable":true,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{}","x":230,"y":360,"wires":[["63c5671d6f4efd07"]]},{"id":"4bf1b44b656c35c2","type":"amqp-in","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","prefetch":0,"reconnectOnError":true,"noAck":true,"exchangeName":"irrigation_control","exchangeType":"direct","exchangeRoutingKey":"zone2","exchangeDurable":true,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{}","x":230,"y":440,"wires":[["e7d4fabe9ef668fe"]]},{"id":"63c5671d6f4efd07","type":"debug","z":"807758ec576fbfd8","name":"Zone 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":500,"y":360,"wires":[]},{"id":"e7d4fabe9ef668fe","type":"debug","z":"807758ec576fbfd8","name":"Zone 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":500,"y":440,"wires":[]},{"id":"bfb1e7e97eef5e04","type":"amqp-broker","name":"AMQP Config","host":"localhost","port":"5672","vhost":"","tls":false,"credsFromSettings":false}]
-{% endrenderFlow %}
+```
+::
We configured a Direct type exchange in Node-RED to route messages to specific queues based on the routing key. We demonstrated how to send and receive commands in a smart irrigation system, ensuring that messages for different zones are delivered correctly. This setup is proper when you need precise message delivery based on an exact match with the routing key.
@@ -133,12 +134,14 @@ We configured a Direct type exchange in Node-RED to route messages to specific q
1. Add two `amqp-in` nodes on to the canvas. Configure one to listen for messages with the `routingKey` of `"temperature.*"` and the other with `"humidity.*"`. Both nodes should be set to the `"weather_data"` exchange.
2. Connect each `amqp-in` node to a `debug` node to view the sensor data received.
-
+
_Video showing the flow that uses the Topic exchange type to send messages and receive messages_
-{% renderFlow %}
+::render-flow
+```json
[{"id":"06ca737a23c93c75","type":"inject","z":"807758ec576fbfd8","name":"Temp sensor 1","props":[{"p":"payload"},{"p":"routingKey","v":"temperature.sensor1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 100\t","payloadType":"jsonata","x":180,"y":600,"wires":[["653aec372ecb68a3"]]},{"id":"653aec372ecb68a3","type":"amqp-out","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","reconnectOnError":false,"exchangeName":"weather_data","exchangeType":"topic","exchangeRoutingKey":"","exchangeRoutingKeyType":"str","exchangeDurable":true,"amqpProperties":"{ \"headers\": {} }","rpcTimeoutMilliseconds":3000,"outputs":0,"x":420,"y":660,"wires":[]},{"id":"39546cb6c75044e2","type":"inject","z":"807758ec576fbfd8","name":"Temp sensor 2","props":[{"p":"payload"},{"p":"routingKey","v":"temperature.sensor2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 100\t","payloadType":"jsonata","x":180,"y":660,"wires":[["653aec372ecb68a3"]]},{"id":"04d4056a8719343d","type":"inject","z":"807758ec576fbfd8","name":"Temp sensor 3","props":[{"p":"payload"},{"p":"routingKey","v":"temperature.sensor3","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 100\t","payloadType":"jsonata","x":180,"y":720,"wires":[["653aec372ecb68a3"]]},{"id":"a7c55eb38bb5f828","type":"amqp-in","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","prefetch":0,"reconnectOnError":false,"noAck":true,"exchangeName":"weather_data","exchangeType":"topic","exchangeRoutingKey":"temperature.*","exchangeDurable":true,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{}","x":460,"y":840,"wires":[["0052a9fab812002f"]]},{"id":"4ac0d82df137f4be","type":"amqp-in","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","prefetch":0,"reconnectOnError":false,"noAck":true,"exchangeName":"weather_data","exchangeType":"topic","exchangeRoutingKey":"humidity.*","exchangeDurable":true,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{}","x":450,"y":920,"wires":[["58f86db958f9c32d"]]},{"id":"a367734d77bd5dcd","type":"inject","z":"807758ec576fbfd8","name":"Hum sensor 1","props":[{"p":"payload"},{"p":"routingKey","v":"humidity.sensor1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 200\t","payloadType":"jsonata","x":690,"y":580,"wires":[["04248194cccf8c7a"]]},{"id":"04248194cccf8c7a","type":"amqp-out","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","reconnectOnError":false,"exchangeName":"weather_data","exchangeType":"topic","exchangeRoutingKey":"","exchangeRoutingKeyType":"str","exchangeDurable":true,"amqpProperties":"{ \"headers\": {} }","rpcTimeoutMilliseconds":3000,"outputs":0,"x":940,"y":640,"wires":[]},{"id":"3cd4806e1bb64564","type":"inject","z":"807758ec576fbfd8","name":"Hum sensor 2","props":[{"p":"payload"},{"p":"routingKey","v":"humidity.sensor2","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 100\t","payloadType":"jsonata","x":690,"y":640,"wires":[["04248194cccf8c7a"]]},{"id":"35725dfe285e0db1","type":"inject","z":"807758ec576fbfd8","name":"Hum sensor 3","props":[{"p":"payload"},{"p":"routingKey","v":"humidity.sensor3","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 100\t","payloadType":"jsonata","x":690,"y":700,"wires":[["04248194cccf8c7a"]]},{"id":"0052a9fab812002f","type":"debug","z":"807758ec576fbfd8","name":"Temperature sensors data","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":770,"y":840,"wires":[]},{"id":"58f86db958f9c32d","type":"debug","z":"807758ec576fbfd8","name":"Humidity sensors data","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":760,"y":920,"wires":[]},{"id":"bfb1e7e97eef5e04","type":"amqp-broker","name":"AMQP Config","host":"localhost","port":"5672","vhost":"","tls":false,"credsFromSettings":false}]
-{% endrenderFlow %}
+```
+::
We explored the Topic type exchange, which allows for more flexible routing using wildcard patterns in the routing key. The example involved a smart weather station where data from various sensors is published and handled based on sensor types. This setup is ideal for situations where you need to route messages based on partial matches or patterns, offering more granular control over message delivery.
@@ -157,12 +160,14 @@ Scenario: You have a smart home system with various components, such as lights,
1. Drag `amqp-in` nodes onto the canvas. Configure one to listen for messages from the `"weather_data"` exchange.
2. Connect the `amqp-in` node to a `debug` node to see the update received from all your components' data.
-
+
_Video showing the flow that uses the Fanout exchange type to send and receive messages._
-{% renderFlow %}
+::render-flow
+```json
[{"id":"7beb4237ba09010b","type":"inject","z":"807758ec576fbfd8","name":"Light update","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Light turned on","payloadType":"str","x":170,"y":1220,"wires":[["d699cd735cc8a0ae"]]},{"id":"8818f122c8937e58","type":"inject","z":"807758ec576fbfd8","name":"thermostats update","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"A new firmware update is available for your thermostat","payloadType":"str","x":190,"y":1280,"wires":[["d699cd735cc8a0ae"]]},{"id":"517aad7b163d5bde","type":"inject","z":"807758ec576fbfd8","name":"Camera update","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Movement detected","payloadType":"str","x":180,"y":1340,"wires":[["d699cd735cc8a0ae"]]},{"id":"d699cd735cc8a0ae","type":"amqp-out","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","reconnectOnError":false,"exchangeName":"system_updates","exchangeType":"fanout","exchangeRoutingKey":"","exchangeRoutingKeyType":"str","exchangeDurable":true,"amqpProperties":"{ \"headers\": {} }","rpcTimeoutMilliseconds":3000,"outputs":0,"x":460,"y":1280,"wires":[]},{"id":"1db4056b4c66cb22","type":"amqp-in","z":"807758ec576fbfd8","name":"","broker":"bfb1e7e97eef5e04","prefetch":0,"reconnectOnError":true,"noAck":false,"exchangeName":"system_updates","exchangeType":"fanout","exchangeRoutingKey":"","exchangeDurable":true,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{}","x":180,"y":1520,"wires":[["2392e7d9139813a3"]]},{"id":"2392e7d9139813a3","type":"debug","z":"807758ec576fbfd8","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":420,"y":1520,"wires":[]},{"id":"bfb1e7e97eef5e04","type":"amqp-broker","name":"AMQP Config","host":"localhost","port":"5672","vhost":"","tls":false,"credsFromSettings":false}]
-{% endrenderFlow %}
+```
+::
We used a Fanout type exchange to broadcast messages to all queues connected to the exchange. We illustrated this with a smart home system where status updates from different components are sent to all devices simultaneously. This type of exchange is perfect for scenarios where you need to send the same message to multiple recipients without concern for routing keys.
@@ -181,11 +186,13 @@ We used a Fanout type exchange to broadcast messages to all queues connected to
1. Drag two `amqp-in` nodes on to the canvas. Configure one to listen for messages with the `headers` of `{ "x-match": "all," "machine-type": "CNC," "status": "error," "priority": "high"}` and the other with `{ "x-match": "any," machine-type": "A," "status": "idle," "priority": "high"}.` Both nodes should be set to the `system_update` exchange.
2. Connect each `amqp-in` node to a `debug` node to see the updates received for each component.
-
+
_Video showing the flow that uses the Headers exchange type to send messages and receive messages_
-{% renderFlow %}
+::render-flow
+```json
[{"id":"12c8048f.4eaefb","type":"amqp-in","z":"e4fe9c44.6dee1","name":"","broker":"83e9bf71fbe099c8","prefetch":0,"reconnectOnError":true,"noAck":true,"exchangeName":"machines_update","exchangeType":"headers","exchangeRoutingKey":"","exchangeDurable":false,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{\"x-match\":\"all\",\"machine-type\":\"CNC\",\"status\":\"error\",\"priority\":\"high\"}","x":170,"y":1000,"wires":[["8ec3fa87.70c338"]]},{"id":"6eccc4f.c6a2a3c","type":"amqp-out","z":"e4fe9c44.6dee1","name":"","broker":"83e9bf71fbe099c8","reconnectOnError":true,"exchangeName":"machines_update","exchangeType":"headers","exchangeRoutingKey":"","exchangeRoutingKeyType":"str","exchangeDurable":false,"amqpProperties":"{}","rpcTimeoutMilliseconds":"","outputs":0,"x":530,"y":780,"wires":[]},{"id":"cb5093bf.1d524","type":"inject","z":"e4fe9c44.6dee1","name":"CNC machine: error occured","props":[{"p":"payload"},{"p":"properties","v":"{\"headers\":{\"machine-type\":\"CNC\",\"status\":\"error\",\"priority\":\"high\"}}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Errror occured in the CNC machine","payloadType":"str","x":200,"y":780,"wires":[["6eccc4f.c6a2a3c"]]},{"id":"8ec3fa87.70c338","type":"debug","z":"e4fe9c44.6dee1","name":"Only from CNC machines that has status error and priority high","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":610,"y":1000,"wires":[]},{"id":"d4c2f84b51d2d3cb","type":"amqp-out","z":"e4fe9c44.6dee1","name":"","broker":"83e9bf71fbe099c8","reconnectOnError":true,"exchangeName":"machines_update","exchangeType":"headers","exchangeRoutingKey":"","exchangeRoutingKeyType":"str","exchangeDurable":false,"amqpProperties":"{}","rpcTimeoutMilliseconds":"","outputs":0,"x":530,"y":840,"wires":[]},{"id":"81c21e9941e06a58","type":"inject","z":"e4fe9c44.6dee1","name":"Update from Machine A","props":[{"p":"payload"},{"p":"properties","v":"{\"headers\":{\"machine-type\":\"A\",\"status\":\"idle\",\"priority\":\"high\"}}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Machine A is currently idle, awaiting next operation.","payloadType":"str","x":180,"y":840,"wires":[["d4c2f84b51d2d3cb"]]},{"id":"abe55aafb5f65eac","type":"debug","z":"e4fe9c44.6dee1","name":"From all of the machines having status idle or priority high","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":570,"y":1120,"wires":[]},{"id":"adfcaa95122a3556","type":"amqp-in","z":"e4fe9c44.6dee1","name":"","broker":"83e9bf71fbe099c8","prefetch":0,"reconnectOnError":true,"noAck":true,"exchangeName":"machines_update","exchangeType":"headers","exchangeRoutingKey":"","exchangeDurable":false,"queueName":"","queueExclusive":true,"queueDurable":false,"queueAutoDelete":true,"headers":"{\"x-match\":\"any\",\"machine-type\":\"A\",\"status\":\"idle\",\"priority\":\"high\"}","x":170,"y":1120,"wires":[["abe55aafb5f65eac"]]},{"id":"83e9bf71fbe099c8","type":"amqp-broker","name":"","host":"localhost","port":"5672","vhost":"","tls":false,"credsFromSettings":false}]
-{% endrenderFlow %}
+```
+::
Finally, we configured a Headers type exchange, which routes messages based on attributes in the message headers. The example focused on a factory monitoring system, where updates from machines are routed based on criteria like machine type, status, and priority. This exchange type is powerful for complex routing scenarios where decisions are based on multiple attributes rather than just the routing key.
diff --git a/src/node-red/protocol/images/PLC-Information-Model-1.png b/nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-1.png
similarity index 100%
rename from src/node-red/protocol/images/PLC-Information-Model-1.png
rename to nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-1.png
diff --git a/src/node-red/protocol/images/PLC-Information-Model-2-of-6-1.png b/nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-2-of-6-1.png
similarity index 100%
rename from src/node-red/protocol/images/PLC-Information-Model-2-of-6-1.png
rename to nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-2-of-6-1.png
diff --git a/src/node-red/protocol/images/PLC-Information-Model-3-of-6-1.png b/nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-3-of-6-1.png
similarity index 100%
rename from src/node-red/protocol/images/PLC-Information-Model-3-of-6-1.png
rename to nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-3-of-6-1.png
diff --git a/src/node-red/protocol/images/PLC-Information-Model-4-of-6-1.png b/nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-4-of-6-1.png
similarity index 100%
rename from src/node-red/protocol/images/PLC-Information-Model-4-of-6-1.png
rename to nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-4-of-6-1.png
diff --git a/src/node-red/protocol/images/PLC-Information-Model-5-of-6-1.png b/nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-5-of-6-1.png
similarity index 100%
rename from src/node-red/protocol/images/PLC-Information-Model-5-of-6-1.png
rename to nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-5-of-6-1.png
diff --git a/src/node-red/protocol/images/PLC-Information-Model-6-of-6-1.png b/nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-6-of-6-1.png
similarity index 100%
rename from src/node-red/protocol/images/PLC-Information-Model-6-of-6-1.png
rename to nuxt/content-guides/node-red/protocol/images/PLC-Information-Model-6-of-6-1.png
diff --git a/src/node-red/protocol/images/amqp-with-node-red.png b/nuxt/content-guides/node-red/protocol/images/amqp-with-node-red.png
similarity index 100%
rename from src/node-red/protocol/images/amqp-with-node-red.png
rename to nuxt/content-guides/node-red/protocol/images/amqp-with-node-red.png
diff --git a/src/node-red/protocol/images/cert-list.png b/nuxt/content-guides/node-red/protocol/images/cert-list.png
similarity index 100%
rename from src/node-red/protocol/images/cert-list.png
rename to nuxt/content-guides/node-red/protocol/images/cert-list.png
diff --git a/src/node-red/protocol/images/cert-tab-filled.png b/nuxt/content-guides/node-red/protocol/images/cert-tab-filled.png
similarity index 100%
rename from src/node-red/protocol/images/cert-tab-filled.png
rename to nuxt/content-guides/node-red/protocol/images/cert-tab-filled.png
diff --git a/src/node-red/protocol/images/change-node-palette.png b/nuxt/content-guides/node-red/protocol/images/change-node-palette.png
similarity index 100%
rename from src/node-red/protocol/images/change-node-palette.png
rename to nuxt/content-guides/node-red/protocol/images/change-node-palette.png
diff --git a/src/node-red/protocol/images/change-node-properties.png b/nuxt/content-guides/node-red/protocol/images/change-node-properties.png
similarity index 100%
rename from src/node-red/protocol/images/change-node-properties.png
rename to nuxt/content-guides/node-red/protocol/images/change-node-properties.png
diff --git a/src/node-red/protocol/images/client-cert-path-filled.png b/nuxt/content-guides/node-red/protocol/images/client-cert-path-filled.png
similarity index 100%
rename from src/node-red/protocol/images/client-cert-path-filled.png
rename to nuxt/content-guides/node-red/protocol/images/client-cert-path-filled.png
diff --git a/src/node-red/protocol/images/client-cert-path.png b/nuxt/content-guides/node-red/protocol/images/client-cert-path.png
similarity index 100%
rename from src/node-red/protocol/images/client-cert-path.png
rename to nuxt/content-guides/node-red/protocol/images/client-cert-path.png
diff --git a/src/node-red/protocol/images/client-testing.webm b/nuxt/content-guides/node-red/protocol/images/client-testing.webm
similarity index 100%
rename from src/node-red/protocol/images/client-testing.webm
rename to nuxt/content-guides/node-red/protocol/images/client-testing.webm
diff --git a/src/node-red/protocol/images/compact-server-active.png b/nuxt/content-guides/node-red/protocol/images/compact-server-active.png
similarity index 100%
rename from src/node-red/protocol/images/compact-server-active.png
rename to nuxt/content-guides/node-red/protocol/images/compact-server-active.png
diff --git a/src/node-red/protocol/images/compact-server-node-address-space.png b/nuxt/content-guides/node-red/protocol/images/compact-server-node-address-space.png
similarity index 100%
rename from src/node-red/protocol/images/compact-server-node-address-space.png
rename to nuxt/content-guides/node-red/protocol/images/compact-server-node-address-space.png
diff --git a/src/node-red/protocol/images/connect-rejected.png b/nuxt/content-guides/node-red/protocol/images/connect-rejected.png
similarity index 100%
rename from src/node-red/protocol/images/connect-rejected.png
rename to nuxt/content-guides/node-red/protocol/images/connect-rejected.png
diff --git a/src/node-red/protocol/images/connected-mqtt-node.png b/nuxt/content-guides/node-red/protocol/images/connected-mqtt-node.png
similarity index 100%
rename from src/node-red/protocol/images/connected-mqtt-node.png
rename to nuxt/content-guides/node-red/protocol/images/connected-mqtt-node.png
diff --git a/src/node-red/protocol/images/context-data-1.png b/nuxt/content-guides/node-red/protocol/images/context-data-1.png
similarity index 100%
rename from src/node-red/protocol/images/context-data-1.png
rename to nuxt/content-guides/node-red/protocol/images/context-data-1.png
diff --git a/src/node-red/protocol/images/context-refresh.png b/nuxt/content-guides/node-red/protocol/images/context-refresh.png
similarity index 100%
rename from src/node-red/protocol/images/context-refresh.png
rename to nuxt/content-guides/node-red/protocol/images/context-refresh.png
diff --git a/src/node-red/protocol/images/conveyor-context-function.png b/nuxt/content-guides/node-red/protocol/images/conveyor-context-function.png
similarity index 100%
rename from src/node-red/protocol/images/conveyor-context-function.png
rename to nuxt/content-guides/node-red/protocol/images/conveyor-context-function.png
diff --git a/src/node-red/protocol/images/copied-certs-with-pubkey.png b/nuxt/content-guides/node-red/protocol/images/copied-certs-with-pubkey.png
similarity index 100%
rename from src/node-red/protocol/images/copied-certs-with-pubkey.png
rename to nuxt/content-guides/node-red/protocol/images/copied-certs-with-pubkey.png
diff --git a/src/node-red/protocol/images/copied-certs.png b/nuxt/content-guides/node-red/protocol/images/copied-certs.png
similarity index 100%
rename from src/node-red/protocol/images/copied-certs.png
rename to nuxt/content-guides/node-red/protocol/images/copied-certs.png
diff --git a/src/node-red/protocol/images/copy-certs.png b/nuxt/content-guides/node-red/protocol/images/copy-certs.png
similarity index 100%
rename from src/node-red/protocol/images/copy-certs.png
rename to nuxt/content-guides/node-red/protocol/images/copy-certs.png
diff --git a/src/node-red/protocol/images/custom-nodes-installed.png b/nuxt/content-guides/node-red/protocol/images/custom-nodes-installed.png
similarity index 100%
rename from src/node-red/protocol/images/custom-nodes-installed.png
rename to nuxt/content-guides/node-red/protocol/images/custom-nodes-installed.png
diff --git a/src/node-red/protocol/images/debug-data.png b/nuxt/content-guides/node-red/protocol/images/debug-data.png
similarity index 100%
rename from src/node-red/protocol/images/debug-data.png
rename to nuxt/content-guides/node-red/protocol/images/debug-data.png
diff --git a/src/node-red/protocol/images/deploy-flow.png b/nuxt/content-guides/node-red/protocol/images/deploy-flow.png
similarity index 100%
rename from src/node-red/protocol/images/deploy-flow.png
rename to nuxt/content-guides/node-red/protocol/images/deploy-flow.png
diff --git a/src/node-red/protocol/images/der-file.png b/nuxt/content-guides/node-red/protocol/images/der-file.png
similarity index 100%
rename from src/node-red/protocol/images/der-file.png
rename to nuxt/content-guides/node-red/protocol/images/der-file.png
diff --git a/src/node-red/protocol/images/direct.webm b/nuxt/content-guides/node-red/protocol/images/direct.webm
similarity index 100%
rename from src/node-red/protocol/images/direct.webm
rename to nuxt/content-guides/node-red/protocol/images/direct.webm
diff --git a/src/node-red/protocol/images/eth-ip-debug.png b/nuxt/content-guides/node-red/protocol/images/eth-ip-debug.png
similarity index 100%
rename from src/node-red/protocol/images/eth-ip-debug.png
rename to nuxt/content-guides/node-red/protocol/images/eth-ip-debug.png
diff --git a/src/node-red/protocol/images/eth-ip-endpoint-tags.png b/nuxt/content-guides/node-red/protocol/images/eth-ip-endpoint-tags.png
similarity index 100%
rename from src/node-red/protocol/images/eth-ip-endpoint-tags.png
rename to nuxt/content-guides/node-red/protocol/images/eth-ip-endpoint-tags.png
diff --git a/src/node-red/protocol/images/eth-ip-in-palette.png b/nuxt/content-guides/node-red/protocol/images/eth-ip-in-palette.png
similarity index 100%
rename from src/node-red/protocol/images/eth-ip-in-palette.png
rename to nuxt/content-guides/node-red/protocol/images/eth-ip-in-palette.png
diff --git a/src/node-red/protocol/images/eth-ip-in-properties.png b/nuxt/content-guides/node-red/protocol/images/eth-ip-in-properties.png
similarity index 100%
rename from src/node-red/protocol/images/eth-ip-in-properties.png
rename to nuxt/content-guides/node-red/protocol/images/eth-ip-in-properties.png
diff --git a/src/node-red/protocol/images/ethip-node-connection.png b/nuxt/content-guides/node-red/protocol/images/ethip-node-connection.png
similarity index 100%
rename from src/node-red/protocol/images/ethip-node-connection.png
rename to nuxt/content-guides/node-red/protocol/images/ethip-node-connection.png
diff --git a/src/node-red/protocol/images/example-flow.png b/nuxt/content-guides/node-red/protocol/images/example-flow.png
similarity index 100%
rename from src/node-red/protocol/images/example-flow.png
rename to nuxt/content-guides/node-red/protocol/images/example-flow.png
diff --git a/src/node-red/protocol/images/executing-command-from-server.webm b/nuxt/content-guides/node-red/protocol/images/executing-command-from-server.webm
similarity index 100%
rename from src/node-red/protocol/images/executing-command-from-server.webm
rename to nuxt/content-guides/node-red/protocol/images/executing-command-from-server.webm
diff --git a/src/node-red/protocol/images/fanout.webm b/nuxt/content-guides/node-red/protocol/images/fanout.webm
similarity index 100%
rename from src/node-red/protocol/images/fanout.webm
rename to nuxt/content-guides/node-red/protocol/images/fanout.webm
diff --git a/src/node-red/protocol/images/flow-manage-palette.png b/nuxt/content-guides/node-red/protocol/images/flow-manage-palette.png
similarity index 100%
rename from src/node-red/protocol/images/flow-manage-palette.png
rename to nuxt/content-guides/node-red/protocol/images/flow-manage-palette.png
diff --git a/src/node-red/protocol/images/flow-with-change-palette.png b/nuxt/content-guides/node-red/protocol/images/flow-with-change-palette.png
similarity index 100%
rename from src/node-red/protocol/images/flow-with-change-palette.png
rename to nuxt/content-guides/node-red/protocol/images/flow-with-change-palette.png
diff --git a/src/node-red/protocol/images/flow-with-compact-server.png b/nuxt/content-guides/node-red/protocol/images/flow-with-compact-server.png
similarity index 100%
rename from src/node-red/protocol/images/flow-with-compact-server.png
rename to nuxt/content-guides/node-red/protocol/images/flow-with-compact-server.png
diff --git a/src/node-red/protocol/images/flowforge-nodered-mqtt-hivemq.png b/nuxt/content-guides/node-red/protocol/images/flowforge-nodered-mqtt-hivemq.png
similarity index 100%
rename from src/node-red/protocol/images/flowforge-nodered-mqtt-hivemq.png
rename to nuxt/content-guides/node-red/protocol/images/flowforge-nodered-mqtt-hivemq.png
diff --git a/src/node-red/protocol/images/header.webm b/nuxt/content-guides/node-red/protocol/images/header.webm
similarity index 100%
rename from src/node-red/protocol/images/header.webm
rename to nuxt/content-guides/node-red/protocol/images/header.webm
diff --git a/src/node-red/protocol/images/image-20230717-212515.png b/nuxt/content-guides/node-red/protocol/images/image-20230717-212515.png
similarity index 100%
rename from src/node-red/protocol/images/image-20230717-212515.png
rename to nuxt/content-guides/node-red/protocol/images/image-20230717-212515.png
diff --git a/src/node-red/protocol/images/image-20230718-155245.png b/nuxt/content-guides/node-red/protocol/images/image-20230718-155245.png
similarity index 100%
rename from src/node-red/protocol/images/image-20230718-155245.png
rename to nuxt/content-guides/node-red/protocol/images/image-20230718-155245.png
diff --git a/src/node-red/protocol/images/image-20230718-164326.png b/nuxt/content-guides/node-red/protocol/images/image-20230718-164326.png
similarity index 100%
rename from src/node-red/protocol/images/image-20230718-164326.png
rename to nuxt/content-guides/node-red/protocol/images/image-20230718-164326.png
diff --git a/src/node-red/protocol/images/import-context.png b/nuxt/content-guides/node-red/protocol/images/import-context.png
similarity index 100%
rename from src/node-red/protocol/images/import-context.png
rename to nuxt/content-guides/node-red/protocol/images/import-context.png
diff --git a/src/node-red/protocol/images/import.png b/nuxt/content-guides/node-red/protocol/images/import.png
similarity index 100%
rename from src/node-red/protocol/images/import.png
rename to nuxt/content-guides/node-red/protocol/images/import.png
diff --git a/src/node-red/protocol/images/individual-cert-file-option.png b/nuxt/content-guides/node-red/protocol/images/individual-cert-file-option.png
similarity index 100%
rename from src/node-red/protocol/images/individual-cert-file-option.png
rename to nuxt/content-guides/node-red/protocol/images/individual-cert-file-option.png
diff --git a/src/node-red/protocol/images/install-eth-ip-node.png b/nuxt/content-guides/node-red/protocol/images/install-eth-ip-node.png
similarity index 100%
rename from src/node-red/protocol/images/install-eth-ip-node.png
rename to nuxt/content-guides/node-red/protocol/images/install-eth-ip-node.png
diff --git a/src/node-red/protocol/images/lwm2m-server-reading.webm b/nuxt/content-guides/node-red/protocol/images/lwm2m-server-reading.webm
similarity index 100%
rename from src/node-red/protocol/images/lwm2m-server-reading.webm
rename to nuxt/content-guides/node-red/protocol/images/lwm2m-server-reading.webm
diff --git a/src/node-red/protocol/images/modbus-1-1.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-1.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-1.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-1.png
diff --git a/src/node-red/protocol/images/modbus-1-10.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-10.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-10.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-10.png
diff --git a/src/node-red/protocol/images/modbus-1-11.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-11.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-11.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-11.png
diff --git a/src/node-red/protocol/images/modbus-1-12.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-12.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-12.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-12.png
diff --git a/src/node-red/protocol/images/modbus-1-13.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-13.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-13.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-13.png
diff --git a/src/node-red/protocol/images/modbus-1-14.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-14.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-14.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-14.png
diff --git a/src/node-red/protocol/images/modbus-1-15.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-15.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-15.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-15.png
diff --git a/src/node-red/protocol/images/modbus-1-16.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-16.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-16.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-16.png
diff --git a/src/node-red/protocol/images/modbus-1-2.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-2.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-2.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-2.png
diff --git a/src/node-red/protocol/images/modbus-1-3.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-3.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-3.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-3.png
diff --git a/src/node-red/protocol/images/modbus-1-4.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-4.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-4.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-4.png
diff --git a/src/node-red/protocol/images/modbus-1-5.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-5.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-5.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-5.png
diff --git a/src/node-red/protocol/images/modbus-1-6.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-6.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-6.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-6.png
diff --git a/src/node-red/protocol/images/modbus-1-7.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-7.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-7.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-7.png
diff --git a/src/node-red/protocol/images/modbus-1-8.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-8.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-8.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-8.png
diff --git a/src/node-red/protocol/images/modbus-1-9.png b/nuxt/content-guides/node-red/protocol/images/modbus-1-9.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-1-9.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-1-9.png
diff --git a/src/node-red/protocol/images/modbus-1.jpg b/nuxt/content-guides/node-red/protocol/images/modbus-1.jpg
similarity index 100%
rename from src/node-red/protocol/images/modbus-1.jpg
rename to nuxt/content-guides/node-red/protocol/images/modbus-1.jpg
diff --git a/src/node-red/protocol/images/modbus-node-red-cta-1.png b/nuxt/content-guides/node-red/protocol/images/modbus-node-red-cta-1.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-node-red-cta-1.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-node-red-cta-1.png
diff --git a/src/node-red/protocol/images/modbus-server.png b/nuxt/content-guides/node-red/protocol/images/modbus-server.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-server.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-server.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-change-node.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-change-node.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-change-node.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-change-node.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-combine-holding-register-data.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-combine-holding-register-data.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-combine-holding-register-data.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-combine-holding-register-data.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-combine-output-coils-data.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-combine-output-coils-data.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-combine-output-coils-data.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-combine-output-coils-data.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-dashboard.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-dashboard.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-dashboard.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-dashboard.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-send-holding-registers.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-send-holding-registers.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-send-holding-registers.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-send-holding-registers.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-send-output-coil.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-send-output-coil.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-send-output-coil.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-send-output-coil.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-template-widget-stylesheet.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-template-widget-stylesheet.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-template-widget-stylesheet.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-template-widget-stylesheet.png
diff --git a/src/node-red/protocol/images/modbus-with-node-red-text-node.png b/nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-text-node.png
similarity index 100%
rename from src/node-red/protocol/images/modbus-with-node-red-text-node.png
rename to nuxt/content-guides/node-red/protocol/images/modbus-with-node-red-text-node.png
diff --git a/src/node-red/protocol/images/mqtt-broker-config.png b/nuxt/content-guides/node-red/protocol/images/mqtt-broker-config.png
similarity index 100%
rename from src/node-red/protocol/images/mqtt-broker-config.png
rename to nuxt/content-guides/node-red/protocol/images/mqtt-broker-config.png
diff --git a/src/node-red/protocol/images/mqtt-in-config-node-red.png b/nuxt/content-guides/node-red/protocol/images/mqtt-in-config-node-red.png
similarity index 100%
rename from src/node-red/protocol/images/mqtt-in-config-node-red.png
rename to nuxt/content-guides/node-red/protocol/images/mqtt-in-config-node-red.png
diff --git a/src/node-red/protocol/images/mqtt-in-config.png b/nuxt/content-guides/node-red/protocol/images/mqtt-in-config.png
similarity index 100%
rename from src/node-red/protocol/images/mqtt-in-config.png
rename to nuxt/content-guides/node-red/protocol/images/mqtt-in-config.png
diff --git a/src/node-red/protocol/images/mqtt-out.png b/nuxt/content-guides/node-red/protocol/images/mqtt-out.png
similarity index 100%
rename from src/node-red/protocol/images/mqtt-out.png
rename to nuxt/content-guides/node-red/protocol/images/mqtt-out.png
diff --git a/src/node-red/protocol/images/msg-payload.png b/nuxt/content-guides/node-red/protocol/images/msg-payload.png
similarity index 100%
rename from src/node-red/protocol/images/msg-payload.png
rename to nuxt/content-guides/node-red/protocol/images/msg-payload.png
diff --git a/src/node-red/protocol/images/node-red-config-mqtt-server.png b/nuxt/content-guides/node-red/protocol/images/node-red-config-mqtt-server.png
similarity index 100%
rename from src/node-red/protocol/images/node-red-config-mqtt-server.png
rename to nuxt/content-guides/node-red/protocol/images/node-red-config-mqtt-server.png
diff --git a/src/node-red/protocol/images/opc-client-accept-cert.png b/nuxt/content-guides/node-red/protocol/images/opc-client-accept-cert.png
similarity index 100%
rename from src/node-red/protocol/images/opc-client-accept-cert.png
rename to nuxt/content-guides/node-red/protocol/images/opc-client-accept-cert.png
diff --git a/src/node-red/protocol/images/opc-client-connect.png b/nuxt/content-guides/node-red/protocol/images/opc-client-connect.png
similarity index 100%
rename from src/node-red/protocol/images/opc-client-connect.png
rename to nuxt/content-guides/node-red/protocol/images/opc-client-connect.png
diff --git a/src/node-red/protocol/images/opc-folder-structure.png b/nuxt/content-guides/node-red/protocol/images/opc-folder-structure.png
similarity index 100%
rename from src/node-red/protocol/images/opc-folder-structure.png
rename to nuxt/content-guides/node-red/protocol/images/opc-folder-structure.png
diff --git a/src/node-red/protocol/images/opc-nodes-install.png b/nuxt/content-guides/node-red/protocol/images/opc-nodes-install.png
similarity index 100%
rename from src/node-red/protocol/images/opc-nodes-install.png
rename to nuxt/content-guides/node-red/protocol/images/opc-nodes-install.png
diff --git a/src/node-red/protocol/images/opc-nodes.png b/nuxt/content-guides/node-red/protocol/images/opc-nodes.png
similarity index 100%
rename from src/node-red/protocol/images/opc-nodes.png
rename to nuxt/content-guides/node-red/protocol/images/opc-nodes.png
diff --git a/src/node-red/protocol/images/opc-ua-2-title-image.png b/nuxt/content-guides/node-red/protocol/images/opc-ua-2-title-image.png
similarity index 100%
rename from src/node-red/protocol/images/opc-ua-2-title-image.png
rename to nuxt/content-guides/node-red/protocol/images/opc-ua-2-title-image.png
diff --git a/src/node-red/protocol/images/reading-data-from-lwm2m-from-server.webm b/nuxt/content-guides/node-red/protocol/images/reading-data-from-lwm2m-from-server.webm
similarity index 100%
rename from src/node-red/protocol/images/reading-data-from-lwm2m-from-server.webm
rename to nuxt/content-guides/node-red/protocol/images/reading-data-from-lwm2m-from-server.webm
diff --git a/src/node-red/protocol/images/reading-data-from-server-in-nr.webm b/nuxt/content-guides/node-red/protocol/images/reading-data-from-server-in-nr.webm
similarity index 100%
rename from src/node-red/protocol/images/reading-data-from-server-in-nr.webm
rename to nuxt/content-guides/node-red/protocol/images/reading-data-from-server-in-nr.webm
diff --git a/src/node-red/protocol/images/security-tab-default.png b/nuxt/content-guides/node-red/protocol/images/security-tab-default.png
similarity index 100%
rename from src/node-red/protocol/images/security-tab-default.png
rename to nuxt/content-guides/node-red/protocol/images/security-tab-default.png
diff --git a/src/node-red/protocol/images/server-testing.webm b/nuxt/content-guides/node-red/protocol/images/server-testing.webm
similarity index 100%
rename from src/node-red/protocol/images/server-testing.webm
rename to nuxt/content-guides/node-red/protocol/images/server-testing.webm
diff --git a/src/node-red/protocol/images/sign&encrypt.png b/nuxt/content-guides/node-red/protocol/images/sign&encrypt.png
similarity index 100%
rename from src/node-red/protocol/images/sign&encrypt.png
rename to nuxt/content-guides/node-red/protocol/images/sign&encrypt.png
diff --git a/src/node-red/protocol/images/sine-wave-gen.png b/nuxt/content-guides/node-red/protocol/images/sine-wave-gen.png
similarity index 100%
rename from src/node-red/protocol/images/sine-wave-gen.png
rename to nuxt/content-guides/node-red/protocol/images/sine-wave-gen.png
diff --git a/src/node-red/protocol/images/ssl-pub-keygen.png b/nuxt/content-guides/node-red/protocol/images/ssl-pub-keygen.png
similarity index 100%
rename from src/node-red/protocol/images/ssl-pub-keygen.png
rename to nuxt/content-guides/node-red/protocol/images/ssl-pub-keygen.png
diff --git a/src/node-red/protocol/images/topic.webm b/nuxt/content-guides/node-red/protocol/images/topic.webm
similarity index 100%
rename from src/node-red/protocol/images/topic.webm
rename to nuxt/content-guides/node-red/protocol/images/topic.webm
diff --git a/src/node-red/protocol/images/using-lwm2m-with-node-red.png b/nuxt/content-guides/node-red/protocol/images/using-lwm2m-with-node-red.png
similarity index 100%
rename from src/node-red/protocol/images/using-lwm2m-with-node-red.png
rename to nuxt/content-guides/node-red/protocol/images/using-lwm2m-with-node-red.png
diff --git a/src/node-red/protocol/images/websocket-handshake.png b/nuxt/content-guides/node-red/protocol/images/websocket-handshake.png
similarity index 100%
rename from src/node-red/protocol/images/websocket-handshake.png
rename to nuxt/content-guides/node-red/protocol/images/websocket-handshake.png
diff --git a/src/node-red/protocol/images/writing-data-to-server-from-nr.webm b/nuxt/content-guides/node-red/protocol/images/writing-data-to-server-from-nr.webm
similarity index 100%
rename from src/node-red/protocol/images/writing-data-to-server-from-nr.webm
rename to nuxt/content-guides/node-red/protocol/images/writing-data-to-server-from-nr.webm
diff --git a/src/node-red/protocol/images/writing-in-server.webm b/nuxt/content-guides/node-red/protocol/images/writing-in-server.webm
similarity index 100%
rename from src/node-red/protocol/images/writing-in-server.webm
rename to nuxt/content-guides/node-red/protocol/images/writing-in-server.webm
diff --git a/src/node-red/protocol/lwm2m.md b/nuxt/content-guides/node-red/protocol/lwm2m.md
similarity index 90%
rename from src/node-red/protocol/lwm2m.md
rename to nuxt/content-guides/node-red/protocol/lwm2m.md
index e0b94b5113..e38b3fd645 100644
--- a/src/node-red/protocol/lwm2m.md
+++ b/nuxt/content-guides/node-red/protocol/lwm2m.md
@@ -1,16 +1,14 @@
---
-eleventyNavigation:
- key: LwM2M
- parent: "Communication Protocols"
+title: Using LwM2M with Node-RED
+navTitle: LwM2M
+navOrder: 50
meta:
- title: Using LwM2M with Node-RED
description: Learn how to integrate LwM2M with Node-RED for effective IoT device management. This guide covers setup, data handling, and remote commands.
- keywords: how to use lwm2m with node red, lwm2m protocol, node-red modbus, lightweight m2m with node red
- modbus
-image: /node-red/protocol/images/using-lwm2m-with-node-red.png
+ keywords: how to use lwm2m with node red, lwm2m protocol, node-red modbus, lightweight m2m with node red modbus
+image: /docs/node-red/protocol/images/using-lwm2m-with-node-red.png
---
-# {{meta.title}}
+# Using LwM2M with Node-RED
IoT devices, especially those designed for low-power operation, can be difficult to manage due to their limited resources and the need for efficient communication and control. This is where LwM2M (Lightweight Machine-to-Machine) comes in. LwM2M is designed to help you monitor, update, and control your devices with minimal overhead, making it ideal for everything from smart sensors to industrial equipment. In this post, we'll explore how you can use LwM2M with Node-RED. It is ideal for anyone starting their journey with LwM2M or Node-RED.
@@ -82,7 +80,7 @@ Once you've configured the LwM2M node with the server details, you can confirm t
3. Below this, you'll find the available objects that you can control for your devices and server.
4. Click on the "Device" object option to read device realted information. In the instance 0 section click on the "R" for each object you want. Alternatively, you can click on the top "R" next to instance 0 or "Device-v1.0" to read all values at once. Note that this may not work if any values are unavailable for your device, it will return 404 not found.
-
+
_Video showing LwM2M Server reading the device details_
You can now read information such as device battery level, available memory, device manufacturer, timezone, device type, and a lot.
@@ -95,7 +93,7 @@ You can now read information such as device battery level, available memory, dev
4. In the form that opens, enter the new value in the correct format.
5. Click on "Write" to update the value for that resource.
-
+
*Video showing how to perform write operation in the LwM2M server*
6. Drag the **lwm2m client** node onto the canvas, select the correct configuration, and enable the "Subscribe LwM2M object events" option. This setting will trigger and send an event object when commands are executed on the server.
@@ -103,7 +101,7 @@ You can now read information such as device battery level, available memory, dev
8. Connect the output of the **lwm2m client** node to the input of the **exec** node.
9. To execute the commands, click on the 'exec' option next to resources such as Reboot.
-
+
*Video showing the LwM2M server executing reboot command for device*
### Reading Data and Configuration from the LwM2M Server in Node-RED
@@ -114,7 +112,7 @@ You can now read information such as device battery level, available memory, dev
4. Connect the output of the **inject** node to the input of the LwM2M client node, and connect the output of the **lwm2m client** node to the input of the **debug** node.
5. Deploy the flow by clicking on the top-right "deploy" button.
-
+
_Video showing Node-RED flow that is reading data from LwM2M Server_
### Writing data and configuration to the LwM2M Server from Node-RED
@@ -126,7 +124,7 @@ _Video showing Node-RED flow that is reading data from LwM2M Server_
5. Connect the **inject** node's output to the input of **lwm2m client out** node.
6. Deploy the flow and click the inject button to perform the write operation.
-
+
_Video showing Node-RED flow that is writing data to LwM2M Server_
-In the same way, you can execute commands from node-red. You have to replace the notion and end that notion with `execute`, like `0/0/4/execute.` When executing the command, you will not have to specify the `msg.payload`.
\ No newline at end of file
+In the same way, you can execute commands from node-red. You have to replace the notion and end that notion with `execute`, like `0/0/4/execute.` When executing the command, you will not have to specify the `msg.payload`.
diff --git a/src/node-red/protocol/modbus.md b/nuxt/content-guides/node-red/protocol/modbus.md
similarity index 98%
rename from src/node-red/protocol/modbus.md
rename to nuxt/content-guides/node-red/protocol/modbus.md
index 9bf7a3e122..ab199ee10c 100644
--- a/src/node-red/protocol/modbus.md
+++ b/nuxt/content-guides/node-red/protocol/modbus.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: Modbus
- parent: "Communication Protocols"
+title: Using Modbus with Node-RED
+navTitle: Modbus
+navOrder: 50
meta:
- title: Using Modbus with Node-RED
description: Learn to use Modbus with Node-RED, including how to build a Modbus server and how to send and read data to and from that server.
- keywords: node red modbus, node-red-contrib-modbus, node-red modbus, modbus node red, modbus nodered, node red modbus rtu, modbus failure on state sending get more about it by logging, node red modbus server
- modbus
+ keywords: node red modbus, node-red-contrib-modbus, node-red modbus, modbus node red, modbus nodered, node red modbus rtu, modbus failure on state sending get more about it by logging, node red modbus server modbus
---
-# {{meta.title}}
+# Using Modbus with Node-RED
In manufacturing companies there is often a small set of production data, currently only available to an equipment operator through the HMI, which would be enormously valuable to a greater audience if there were some way to easily display and share it.
@@ -18,7 +16,7 @@ Node-RED, along with Modbus and Dashboard modules, can easily create a web-based
-
+
## What is Modbus
@@ -141,9 +139,11 @@ The output coil data is displayed on the dashboard using a text widget. When the
Final flow is given below:
-{% renderFlow 300 %}
+::render-flow{:height="300"}
+```json
[{"id":"4cf44f4cbc592b99","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"b48b43414657ca4e","type":"modbus-read","z":"4cf44f4cbc592b99","name":"","topic":"text","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"Coil","adr":"0","quantity":"5","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"145bc96e15c34554","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":130,"y":540,"wires":[["f6fb47f18928d28f","3bf66c619694a1a4","35bc45f58dc5c661","5b3a8b0c7cd968fa","f87af242b2287b7c"],[]]},{"id":"e28ae04240af481e","type":"modbus-read","z":"4cf44f4cbc592b99","name":"","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"HoldingRegister","adr":"0","quantity":"4","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"145bc96e15c34554","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":130,"y":800,"wires":[["9dd39216e9bbd6fd","c4389848cebd29c7","dffa222847521883","286d69415ef78e7c"],[]]},{"id":"c302edd625716894","type":"modbus-server","z":"4cf44f4cbc592b99","name":"Modbus server","logEnabled":false,"hostname":"127.0.0.1","serverPort":"10502","responseDelay":100,"delayUnit":"ms","coilsBufferSize":10000,"holdingBufferSize":10000,"inputBufferSize":10000,"discreteBufferSize":10000,"showErrors":false,"x":520,"y":260,"wires":[[],[],[],[],[]]},{"id":"7f20fcaea773cb1b","type":"inject","z":"4cf44f4cbc592b99","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":260,"wires":[["c302edd625716894"]]},{"id":"bc7bdfd9d81369af","type":"inject","z":"4cf44f4cbc592b99","name":"","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 150","payloadType":"jsonata","x":110,"y":1020,"wires":[["2c019b52b54a2e0d"]]},{"id":"2c019b52b54a2e0d","type":"join","z":"4cf44f4cbc592b99","name":"combine holiding register data","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"4","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":350,"y":1020,"wires":[["8bf7d588de60fb33"]]},{"id":"6c14cb9ab888c12d","type":"inject","z":"4cf44f4cbc592b99","name":"","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":110,"y":1140,"wires":[["1a59e443e0258194"]]},{"id":"1a59e443e0258194","type":"join","z":"4cf44f4cbc592b99","name":"combine coil output data","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"5","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":330,"y":1140,"wires":[["cdb7e5d53210cb6d"]]},{"id":"62338bd2af1d3c0d","type":"modbus-response","z":"4cf44f4cbc592b99","name":"","registerShowMax":20,"x":870,"y":1020,"wires":[]},{"id":"d1eff51eac0af203","type":"modbus-response","z":"4cf44f4cbc592b99","name":"","registerShowMax":20,"x":870,"y":1120,"wires":[]},{"id":"8bf7d588de60fb33","type":"modbus-write","z":"4cf44f4cbc592b99","name":"send holding registers","showStatusActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"MHoldingRegisters","adr":"0","quantity":"4","server":"145bc96e15c34554","emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":640,"y":1020,"wires":[["62338bd2af1d3c0d"],[]]},{"id":"cdb7e5d53210cb6d","type":"modbus-write","z":"4cf44f4cbc592b99","name":"send output coils ","showStatusActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"MCoils","adr":"0","quantity":"5","server":"145bc96e15c34554","emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":630,"y":1140,"wires":[["d1eff51eac0af203"],[]]},{"id":"405acf6300a072d6","type":"ui-text","z":"4cf44f4cbc592b99","group":"a00b86fb96b216a6","order":1,"width":0,"height":0,"name":"","label":"isEStopReleased","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":690,"y":460,"wires":[]},{"id":"52cb610e23488ba2","type":"ui-text","z":"4cf44f4cbc592b99","group":"a00b86fb96b216a6","order":3,"width":0,"height":0,"name":"","label":"isMotorSwitchedOn","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":690,"y":500,"wires":[]},{"id":"224a2c2948d7d9a5","type":"ui-text","z":"4cf44f4cbc592b99","group":"a00b86fb96b216a6","order":4,"width":0,"height":0,"name":"","label":"isMotorRunning","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":680.111083984375,"y":537.7777709960938,"wires":[]},{"id":"82fce135993ad1ab","type":"ui-text","z":"4cf44f4cbc592b99","group":"a00b86fb96b216a6","order":5,"width":0,"height":0,"name":"","label":"isTailswitchPulsing","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":690,"y":580,"wires":[]},{"id":"f6fb47f18928d28f","type":"change","z":"4cf44f4cbc592b99","name":"isEStopReleased","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[0]","tot":"msg"},{"t":"set","p":"class","pt":"msg","to":"msg.payload ? \"green\":\"red\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":460,"wires":[["405acf6300a072d6"]]},{"id":"3bf66c619694a1a4","type":"change","z":"4cf44f4cbc592b99","name":"isMotorSwitchedOn","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[1]","tot":"msg"},{"t":"set","p":"class","pt":"msg","to":"msg.payload ? \"green\":\"red\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":500,"wires":[["52cb610e23488ba2"]]},{"id":"35bc45f58dc5c661","type":"change","z":"4cf44f4cbc592b99","name":"isMotorRunning","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[2]","tot":"msg"},{"t":"set","p":"class","pt":"msg","to":"msg.payload ? \"green\":\"red\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":540,"wires":[["224a2c2948d7d9a5"]]},{"id":"5b3a8b0c7cd968fa","type":"change","z":"4cf44f4cbc592b99","name":"isTailswitchPulsing","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[3]","tot":"msg"},{"t":"set","p":"class","pt":"msg","to":"msg.payload ? \"green\":\"red\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":580,"wires":[["82fce135993ad1ab"]]},{"id":"f87af242b2287b7c","type":"change","z":"4cf44f4cbc592b99","name":"isMaterialOnBelt","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[4]","tot":"msg"},{"t":"set","p":"class","pt":"msg","to":"msg.payload ? \"green\":\"red\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":620,"wires":[["7ab41bd5d19d5f7f"]]},{"id":"7ab41bd5d19d5f7f","type":"ui-text","z":"4cf44f4cbc592b99","group":"a00b86fb96b216a6","order":2,"width":0,"height":0,"name":"","label":"isMaterialOnBelt","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":680,"y":620,"wires":[]},{"id":"cc8b32c267214e28","type":"ui-template","z":"4cf44f4cbc592b99","group":"","page":"","ui":"5ff82627d2caf841","name":"stylesheet","order":0,"width":0,"height":0,"head":"","format":".green span {\n color: green;\n}\n\n.red span {\n color: red;\n}\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"site:style","className":"","x":940,"y":560,"wires":[[]]},{"id":"c4f0157670882335","type":"ui-gauge","z":"4cf44f4cbc592b99","name":"motorAmps","group":"ef2cffe3b927bbb6","order":1,"width":"0","height":"0","gtype":"gauge-half","gstyle":"needle","title":"gauge","units":"units","icon":"","prefix":"","suffix":"","segments":[{"from":"0","color":"#ba6e26"},{"from":"100","color":"#ba6e26"}],"min":0,"max":"150","sizeThickness":16,"sizeGap":4,"sizeKeyThickness":8,"styleRounded":true,"styleGlow":false,"className":"","x":710,"y":760,"wires":[]},{"id":"df73d7c58bbf6069","type":"ui-text","z":"4cf44f4cbc592b99","group":"ef2cffe3b927bbb6","order":2,"width":0,"height":0,"name":"","label":"motorHourMeter","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":720,"y":800,"wires":[]},{"id":"4d14a50a008a4606","type":"ui-text","z":"4cf44f4cbc592b99","group":"98ab0c19d66959eb","order":2,"width":0,"height":0,"name":"","label":"beltTotalTons","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":710,"y":880,"wires":[]},{"id":"9dd39216e9bbd6fd","type":"change","z":"4cf44f4cbc592b99","name":"motorAmps","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[0]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":760,"wires":[["c4f0157670882335"]]},{"id":"c4389848cebd29c7","type":"change","z":"4cf44f4cbc592b99","name":"motorHourMeter","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[1]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":800,"wires":[["df73d7c58bbf6069"]]},{"id":"dffa222847521883","type":"change","z":"4cf44f4cbc592b99","name":"beltTonsPerHour","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[2]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":840,"wires":[["b45120d07ed4a8bb"]]},{"id":"286d69415ef78e7c","type":"change","z":"4cf44f4cbc592b99","name":"beltTotalTons","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[3]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":880,"wires":[["4d14a50a008a4606"]]},{"id":"b45120d07ed4a8bb","type":"ui-chart","z":"4cf44f4cbc592b99","group":"98ab0c19d66959eb","name":"beltTonsPerHour","label":"chart","order":1,"chartType":"line","category":"beltTonsPerHour","categoryType":"str","xAxisLabel":"","xAxisProperty":"","xAxisPropertyType":"msg","xAxisType":"time","yAxisLabel":"","yAxisProperty":"","ymin":"","ymax":"","action":"append","pointShape":"false","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":"6","height":"5","className":"","x":730,"y":840,"wires":[[]]},{"id":"af0626137803b160","type":"comment","z":"4cf44f4cbc592b99","name":"Reading holding register data from modbus server","info":"","x":510,"y":700,"wires":[]},{"id":"bf74f24e625763ff","type":"comment","z":"4cf44f4cbc592b99","name":"Sending output coil data to modbus server","info":"","x":480,"y":1080,"wires":[]},{"id":"c5ab6f8dcb96b79b","type":"comment","z":"4cf44f4cbc592b99","name":"Sending holding register data to modbus server","info":"","x":500,"y":960,"wires":[]},{"id":"8e1387959a33f353","type":"comment","z":"4cf44f4cbc592b99","name":"Reading output coil data from modbus server","info":"","x":490,"y":400,"wires":[]},{"id":"b85a4192ee8a8c38","type":"comment","z":"4cf44f4cbc592b99","name":"Modbus server ","info":"","x":300,"y":180,"wires":[]},{"id":"145bc96e15c34554","type":"modbus-client","name":"Modbus server","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"10502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","serialAsciiResponseStartDelimiter":"0x3A","unit_id":"1","commandDelay":"1","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true,"showErrors":false,"showWarnings":true,"showLogs":true},{"id":"a00b86fb96b216a6","type":"ui-group","name":"Output coils","page":"3876c3cdc50d68a5","width":"3","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"5ff82627d2caf841","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default","titleBarStyle":"default"},{"id":"ef2cffe3b927bbb6","type":"ui-group","name":"Holding registers","page":"3876c3cdc50d68a5","width":"3","height":"3","order":2,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"98ab0c19d66959eb","type":"ui-group","name":"holding register ","page":"3876c3cdc50d68a5","width":"6","height":"1","order":3,"showTitle":false,"className":"","visible":"true","disabled":"false"},{"id":"3876c3cdc50d68a5","type":"ui-page","name":"Home","ui":"5ff82627d2caf841","path":"/","icon":"home","layout":"grid","theme":"36efce4ebdf1ae91","order":1,"className":"","visible":false,"disabled":"false"},{"id":"36efce4ebdf1ae91","type":"ui-theme","name":"Default Theme","colors":{"surface":"#0094ce","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]
-{% endrenderFlow %}
+```
+::
The way that we set up this example worked well for the small sample size and quickly getting content on the dashboard, but as you work through your own needs, think about the data structures that will be the most conducive to efficiently working with your data.
diff --git a/src/node-red/protocol/mqtt.md b/nuxt/content-guides/node-red/protocol/mqtt.md
similarity index 97%
rename from src/node-red/protocol/mqtt.md
rename to nuxt/content-guides/node-red/protocol/mqtt.md
index eec5f60105..aa8dac16a1 100644
--- a/src/node-red/protocol/mqtt.md
+++ b/nuxt/content-guides/node-red/protocol/mqtt.md
@@ -1,15 +1,13 @@
---
-eleventyNavigation:
- key: MQTT
- parent: "Communication Protocols"
- title: MQTT
+title: Using MQTT with Node-RED
+navTitle: MQTT
+navOrder: 50
meta:
- title: Using MQTT with Node-RED
description: Learn how to use MQTT with Node-RED.
keywords: node red mqtt in, nodered mqtt, mqtt node red, node-red mqtt, node-red mqtt broker, node red mqtt dynamic subscription, mqtt broker node red, nodered mqtt broker, node red mqtt out, mqtt in node red
---
-# {{meta.title}}
+# Using MQTT with Node-RED
Getting devices to talk to each other in industrial environments isn't trivial. You're dealing with spotty networks, power constraints, and devices that need to share data without constant back-and-forth polling. MQTT solves these problems by keeping communication lightweight and flexible. This guide walks you through what MQTT is, why it works well for IIoT, and how to get it running with Node-RED.
diff --git a/src/node-red/protocol/opc-ua.md b/nuxt/content-guides/node-red/protocol/opc-ua.md
similarity index 98%
rename from src/node-red/protocol/opc-ua.md
rename to nuxt/content-guides/node-red/protocol/opc-ua.md
index d180ae0d28..eb65a522bf 100644
--- a/src/node-red/protocol/opc-ua.md
+++ b/nuxt/content-guides/node-red/protocol/opc-ua.md
@@ -1,22 +1,14 @@
---
-eleventyNavigation:
- key: OPC-UA
- parent: "Communication Protocols"
+title: Building Secure OPC-UA Server in Node-RED.
+navTitle: OPC-UA
+navOrder: 50
meta:
- title: Building Secure OPC-UA Server in Node-RED.
- description: Learn how to build Build and Deploy a custom OPC UA Server in Node-RED
- keywords: node-red,flowfuse, opc-ua,
-image: /node-red/protocol/images/opc-ua-2-title-image.png
-certified:
- - "@flowfuse-certified-nodes/opcua"
- - "@flowfuse-certified-nodes/cip-suite"
-certifiedIntro: >
- This guide builds the server from community packages. Both halves of it, OPC UA
- and the Allen Bradley Ethernet/IP link, also have a FlowFuse certified node
- maintained for production use:
+ description: Learn how to build Build and Deploy a custom OPC UA Server in Node-RED
+ keywords: node-red,flowfuse, opc-ua,
+image: /docs/node-red/protocol/images/opc-ua-2-title-image.png
---
-# {{meta.title}}
+# Building Secure OPC-UA Server in Node-RED.
OPC-UA (OPC Unified Architecture) is a communication protocol designed for industrial automation. It enables seamless data exchange and interoperability between various devices, systems, and software applications in the industrial domain. OPC-UA offers secure and reliable communication, making it a preferred choice for building robust industrial solutions. In this document, we will delve into the creation of a fully custom secure OPC-UA Server for PLCs in Node-RED.
@@ -62,7 +54,9 @@ Now that we have laid out a concept for our application, let’s build it.
## Install Custom Nodes
-{% include "components/certified-node-callout.liquid" %}
+::callout{icon="i-lucide-badge-check"}
+**Certified nodes for this technology.** This guide builds the server from community packages. Both halves of it, OPC UA and the Allen Bradley Ethernet/IP link, also have a FlowFuse certified node maintained for production use: [opcua](/integrations/?certified=1), [cip-suite](/integrations/?certified=1)
+::
First, we need to add three custom nodes that will allow Node-RED to read Ethernet/IP data and add OPC UA Server functionality.
@@ -631,6 +625,8 @@ OPC UA is one of several industrial protocols FlowFuse uses to connect PLCs to t
Source code for flow used in this documentation -
-{% renderFlow %}
+::render-flow
+```json
[{"id":"2e8c7f5c.ab73d","type":"tab","label":"OPC-UA Custom Context Server","disabled":false,"info":""},{"id":"38ce10de.7d8c","type":"opcua-compact-server","z":"2e8c7f5c.ab73d","port":"54845","endpoint":"","productUri":"","acceptExternalCommands":true,"maxAllowedSessionNumber":"10","maxConnectionsPerEndpoint":"10","maxAllowedSubscriptionNumber":"100","alternateHostname":"","name":"","showStatusActivities":false,"showErrors":true,"allowAnonymous":false,"individualCerts":true,"isAuditing":false,"serverDiscovery":true,"users":[],"xmlsetsOPCUA":[],"publicCertificateFile":"/root/.node-red/node_modules/node-red-contrib-opcua-server/certificates/server_selfsigned_cert_2048.pem","privateCertificateFile":"/root/.node-red/node_modules/node-red-contrib-opcua-server/certificates/server_key_2048.pem","registerServerMethod":"1","discoveryServerEndpointUrl":"opc.tcp://192.168.0.114:54845","capabilitiesForMDNS":"","maxNodesPerRead":1000,"maxNodesPerWrite":1000,"maxNodesPerHistoryReadData":100,"maxNodesPerBrowse":3000,"maxBrowseContinuationPoints":"10","maxHistoryContinuationPoints":"10","delayToInit":"1000","delayToClose":"200","serverShutdownTimeout":"100","addressSpaceScript":"function constructAlarmAddressSpace(server, addressSpace, eventObjects, done) {\n // server = the created node-opcua server\n // addressSpace = address space of the node-opcua server\n // eventObjects = add event variables here to hold them in memory from this script\n\n // internal sandbox objects are:\n // node = the compact server node,\n // coreServer = core compact server object for debug and access to NodeOPCUA\n // this.sandboxNodeContext = node context node-red\n // this.sandboxFlowContext = flow context node-red\n // this.sandboxGlobalContext = global context node-red\n // this.sandboxEnv = env variables\n // timeout and interval functions as expected from nodejs\n\n const opcua = coreServer.choreCompact.opcua;\n const LocalizedText = opcua.LocalizedText;\n const namespace = addressSpace.getOwnNamespace();\n\n const Variant = opcua.Variant;\n const DataType = opcua.DataType;\n const DataValue = opcua.DataValue;\n\n var flexServerInternals = this;\n\n this.sandboxFlowContext.set(\"conveyorData.Conveyor_RTS\", false);\n this.sandboxFlowContext.set(\"conveyorData.Robot_RTS\", false);\n this.sandboxFlowContext.set(\"conveyorData.Robot_Position\", 0);\n this.sandboxFlowContext.set(\"conveyorData.Conveyor_Running\", false);\n this.sandboxFlowContext.set(\"conveyorData.Line4_State\", 0);\n this.sandboxFlowContext.set(\"conveyorData.Line4_Fault\", false);\n\n // this.sandboxFlowContext.set(\"isoInput1\", 0);\n // this.setInterval(() => {\n // flexServerInternals.sandboxFlowContext.set(\n // \"isoInput1\",\n // Math.random() + 50.0\n // );\n // }, 500);\n // this.sandboxFlowContext.set(\"isoInput2\", 0);\n // this.sandboxFlowContext.set(\"isoInput3\", 0);\n // this.sandboxFlowContext.set(\"isoInput4\", 0);\n // this.sandboxFlowContext.set(\"isoInput5\", 0);\n // this.sandboxFlowContext.set(\"isoInput6\", 0);\n // this.sandboxFlowContext.set(\"isoInput7\", 0);\n // this.sandboxFlowContext.set(\"isoInput8\", 0);\n // this.sandboxFlowContext.set(\"isoOutput1\", 0);\n // this.setInterval(() => {\n // flexServerInternals.sandboxFlowContext.set(\n // \"isoOutput1\",\n // Math.random() + 10.0\n // );\n // }, 500);\n\n // this.sandboxFlowContext.set(\"isoOutput2\", 0);\n // this.sandboxFlowContext.set(\"isoOutput3\", 0);\n // this.sandboxFlowContext.set(\"isoOutput4\", 0);\n // this.sandboxFlowContext.set(\"isoOutput5\", 0);\n // this.sandboxFlowContext.set(\"isoOutput6\", 0);\n // this.sandboxFlowContext.set(\"isoOutput7\", 0);\n // this.sandboxFlowContext.set(\"isoOutput8\", 0);\n\n coreServer.debugLog(\"init dynamic address space\");\n const rootFolder = addressSpace.findNode(\"RootFolder\");\n\n node.warn(\"construct new address space for OPC UA\");\n\n const myDevice = namespace.addFolder(rootFolder.objects, {\n \"browseName\": \"Line 4 PLC\"\n });\n const conveyorFolder = namespace.addFolder(myDevice, { \"browseName\": \"Conveyor\" });\n const conveyorBools = namespace.addFolder(conveyorFolder, {\n \"browseName\": \"Bools\"\n });\n const conveyorDINTs = namespace.addFolder(conveyorFolder, {\n \"browseName\": \"DINTs\"\n });\n const conveyorFloats = namespace.addFolder(conveyorFolder, {\n \"browseName\": \"Floats\"\n });\n\n // Construct Nodes\n const Conveyor_RTS = namespace.addVariable({\n \"organizedBy\": conveyorBools,\n \"browseName\": \"Conveyor Ready to Start\",\n \"nodeId\": \"ns=1;s=Conveyor_RTS\",\n \"dataType\": \"Boolean\",\n \"value\": {\n \"get\": function () {\n return new Variant({\n \"dataType\": DataType.Boolean,\n \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Conveyor_RTS\")\n });\n },\n \"set\": function (variant) {\n flexServerInternals.sandboxFlowContext.set(\n \"conveyorData.Conveyor_RTS\",\n variant.value\n );\n return opcua.StatusCodes.Good;\n }\n }\n });\n\n const Robot_RTS = namespace.addVariable({\n \"organizedBy\": conveyorBools,\n \"browseName\": \"Robot Ready to Start\",\n \"nodeId\": \"ns=1;s=Robot_RTS\",\n \"dataType\": \"Boolean\",\n \"value\": {\n \"get\": function () {\n return new Variant({\n \"dataType\": DataType.Boolean,\n \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Robot_RTS\")\n });\n },\n \"set\": function (variant) {\n flexServerInternals.sandboxFlowContext.set(\n \"conveyorData.Robot_RTS\",\n variant.value\n );\n return opcua.StatusCodes.Good;\n }\n }\n });\n\n const Conveyor_Running = namespace.addVariable({\n \"organizedBy\": conveyorBools,\n \"browseName\": \"Conveyor Running\",\n \"nodeId\": \"ns=1;s=Conveyor_Running\",\n \"dataType\": \"Boolean\",\n \"value\": {\n \"get\": function () {\n return new Variant({\n \"dataType\": DataType.Boolean,\n \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Conveyor_Running\")\n });\n },\n \"set\": function (variant) {\n flexServerInternals.sandboxFlowContext.set(\n \"conveyorData.Conveyor_Running\",\n variant.value\n );\n return opcua.StatusCodes.Good;\n }\n }\n });\n\n const Line4_Fault = namespace.addVariable({\n \"organizedBy\": conveyorBools,\n \"browseName\": \"Line 4 Faulted\",\n \"nodeId\": \"ns=1;s=Line4_Fault\",\n \"dataType\": \"Boolean\",\n \"value\": {\n \"get\": function () {\n return new Variant({\n \"dataType\": DataType.Boolean,\n \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Line4_Fault\")\n });\n },\n \"set\": function (variant) {\n flexServerInternals.sandboxFlowContext.set(\n \"conveyorData.Line4_Fault\",\n variant.value\n );\n return opcua.StatusCodes.Good;\n }\n }\n });\n\n const Line4_State = namespace.addVariable({\n \"organizedBy\": conveyorDINTs,\n \"browseName\": \"Line 4 State\",\n \"nodeId\": \"ns=1;s=Line4_State\",\n \"dataType\": \"Int32\",\n \"value\": {\n \"get\": function () {\n return new Variant({\n \"dataType\": DataType.Int32,\n \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Line4_State\")\n });\n },\n \"set\": function (variant) {\n flexServerInternals.sandboxFlowContext.set(\n \"conveyorData.Line4_State\",\n variant.value\n );\n return opcua.StatusCodes.Good;\n }\n }\n });\n\n const Robot_Position = namespace.addVariable({\n \"organizedBy\": conveyorFloats,\n \"browseName\": \"Robot Axis A1 Position\",\n \"nodeId\": \"ns=1;s=Robot_Position\",\n \"dataType\": \"Float\",\n \"value\": {\n \"get\": function () {\n return new Variant({\n \"dataType\": DataType.Float,\n \"value\": flexServerInternals.sandboxFlowContext.get(\"conveyorData.Robot_Position\")\n });\n },\n \"set\": function (variant) {\n flexServerInternals.sandboxFlowContext.set(\n \"conveyorData.Robot_Position\",\n parseFloat(variant.value)\n );\n return opcua.StatusCodes.Good;\n }\n }\n });\n\n //------------------------------------------------------------------------------\n // Add a view\n //------------------------------------------------------------------------------\n const viewBools = namespace.addView({\n \"organizedBy\": rootFolder.views,\n \"browseName\": \"Line 4 Conveyor Bools\"\n });\n\n const viewDINTs = namespace.addView({\n \"organizedBy\": rootFolder.views,\n \"browseName\": \"Line4 Conveyor DINTs\"\n });\n\n const viewFloats = namespace.addView({\n \"organizedBy\": rootFolder.views,\n \"browseName\": \"Line4 Conveyor Floats\"\n });\n\n viewBools.addReference({\n \"referenceType\": \"Organizes\",\n \"nodeId\": Conveyor_RTS.nodeId\n });\n\n viewBools.addReference({\n \"referenceType\": \"Organizes\",\n \"nodeId\": Robot_RTS.nodeId\n });\n\n viewBools.addReference({\n \"referenceType\": \"Organizes\",\n \"nodeId\": Conveyor_Running.nodeId\n });\n\n viewBools.addReference({\n \"referenceType\": \"Organizes\",\n \"nodeId\": Line4_Fault.nodeId\n });\n\n\n viewDINTs.addReference({\n \"referenceType\": \"Organizes\",\n \"nodeId\": Line4_State.nodeId\n });\n\n viewFloats.addReference({\n \"referenceType\": \"Organizes\",\n \"nodeId\": Robot_Position.nodeId\n });\n coreServer.debugLog(\"create dynamic address space done\");\n node.warn(\"construction of new address space for OPC UA done\");\n\n done();\n}\n","x":960,"y":600,"wires":[]},{"id":"7ae89f134415c51e","type":"eth-ip in","z":"2e8c7f5c.ab73d","endpoint":"f012042b75173b77","mode":"all","variable":"","program":"","name":"Read Line4 Conveyor tags","x":150,"y":600,"wires":[["0c51f44baa08a3b2"]]},{"id":"a0592280baea975b","type":"comment","z":"2e8c7f5c.ab73d","name":"read data from PLC & store in conveyorData context flow memory","info":"","x":350,"y":540,"wires":[]},{"id":"b411a5ce4749fa61","type":"comment","z":"2e8c7f5c.ab73d","name":"Secure OPC UA Server Publishing PLC conveyorData","info":"","x":980,"y":540,"wires":[]},{"id":"0c51f44baa08a3b2","type":"change","z":"2e8c7f5c.ab73d","name":"store PLC Data in flow context memory","rules":[{"t":"set","p":"conveyorData.Conveyor_RTS","pt":"flow","to":"payload.Conveyor_RTS","tot":"msg"},{"t":"set","p":"conveyorData.Conveyor_Running","pt":"flow","to":"payload.Conveyor_Running","tot":"msg"},{"t":"set","p":"conveyorData.Line4_Fault","pt":"flow","to":"payload.Line4_Fault","tot":"msg"},{"t":"set","p":"conveyorData.Line4_State","pt":"flow","to":"payload.Line4_State","tot":"msg"},{"t":"set","p":"conveyorData.Robot_Position","pt":"flow","to":"payload.Robot_Position","tot":"msg"},{"t":"set","p":"conveyorData.Robot_RTS","pt":"flow","to":"payload.Robot_RTS","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":600,"wires":[[]]},{"id":"f012042b75173b77","type":"eth-ip endpoint","address":"192.168.0.5","slot":"0","cycletime":"1000","name":"Line 4 PLC","vartable":{"":{"Conveyor_RTS":{"type":"BOOL"},"Conveyor_Running":{"type":"BOOL"},"Line4_Fault":{"type":"BOOL"},"Line4_State":{"type":"DINT"},"Robot_Position":{"type":"REAL"},"Robot_RTS":{"type":"BOOL"}}}}]
-{% endrenderFlow %}
+```
+::
diff --git a/src/node-red/protocol/websocket.md b/nuxt/content-guides/node-red/protocol/websocket.md
similarity index 94%
rename from src/node-red/protocol/websocket.md
rename to nuxt/content-guides/node-red/protocol/websocket.md
index 5f16a47e93..b9cf2abf56 100644
--- a/src/node-red/protocol/websocket.md
+++ b/nuxt/content-guides/node-red/protocol/websocket.md
@@ -1,15 +1,12 @@
---
-eleventyNavigation:
- key: Websocket
- parent: "Communication Protocols"
+title: Using Websocket with Node-RED
+navTitle: Websocket
+navOrder: 50
meta:
- title: Using Websocket with Node-RED
- description:
- keywords: node-red websocket, websocket in node-red, websocket node-red
-image:
+ keywords: node-red websocket, websocket in node-red, websocket node-red
---
-# {{meta.title}}
+# Using Websocket with Node-RED
This guide covers WebSocket communication in Node-RED. You'll learn how to connect as a client and set up your own WebSocket server.
@@ -64,7 +61,7 @@ Before creating the server, it's important to understand that we will need to li
### Testing the WebSocket Server
-
+
_Testing the WebSocket server with a Websocket client_
Now that you have deployed the Node-RED flow, it is acting as a server that can both send and receive data. To test the server, you can use the [Simple WebSocket Client](https://chromewebstore.google.com/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo?hl=en) extension in your browser. Make sure to install this extension if you want to test the server.
@@ -104,9 +101,9 @@ Now, as the section states, we are going to see how you can connect to the WebSo
### Testing the WebSocket Client
-
+
_Testing Websocket Client with Websocket Server_
Now, to test the client, you can send messages from the server and see the debug window for that message in the client instance. Similarly, you can send messages from the client Instance to the server and observe the responses in the debug window of server instance.
-For more information on the advaced websocket node configuration refer to the [Websocket Node Documentation](/node-red/core-nodes/websocket/)
\ No newline at end of file
+For more information on the advaced websocket node configuration refer to the [Websocket Node Documentation](/docs/node-red/core-nodes/websocket/)
diff --git a/src/node-red/terminology/index.md b/nuxt/content-guides/node-red/terminology/README.md
similarity index 95%
rename from src/node-red/terminology/index.md
rename to nuxt/content-guides/node-red/terminology/README.md
index 40a00f8b79..c6e8ecb531 100644
--- a/src/node-red/terminology/index.md
+++ b/nuxt/content-guides/node-red/terminology/README.md
@@ -1,11 +1,10 @@
---
-eleventyNavigation:
- key: Terminology
- order: 2
+title: Node-RED Terminology
+navTitle: Terminology
+navOrder: 10
meta:
- title: Node-RED Terminology
- description: Explore key terms and concepts used within the Node-RED community, including flows, nodes, subflows, workspace, and more.
- keywords: node-red, node-red terminology, node-red concepts, node-red key terms
+ description: Explore key terms and concepts used within the Node-RED community, including flows, nodes, subflows, workspace, and more.
+ keywords: node-red, node-red terminology, node-red concepts, node-red key terms
---
# Node-RED Terminology
@@ -33,7 +32,7 @@ A Node is a fundamental building block used to create flows. Each node represent
{data-zoomable}
## Core-Node
-The core nodes are the set of nodes that are included with the Node-RED runtime by default, without the need for a node installation procedure. For more information on core nodes, refer to the [Core node docs](/node-red/core-nodes/).
+The core nodes are the set of nodes that are included with the Node-RED runtime by default, without the need for a node installation procedure. For more information on core nodes, refer to the [Core node docs](/docs/node-red/core-nodes/).
## Wires
The "wires" refer to the connections that link nodes together to define the flow of data. These wires visually represent the direction and flow of information from one node to another within a Node-RED flow.
diff --git a/src/node-red/terminology/images/editor-flow-tabs.png b/nuxt/content-guides/node-red/terminology/images/editor-flow-tabs.png
similarity index 100%
rename from src/node-red/terminology/images/editor-flow-tabs.png
rename to nuxt/content-guides/node-red/terminology/images/editor-flow-tabs.png
diff --git a/src/node-red/terminology/images/editor-workspace.png b/nuxt/content-guides/node-red/terminology/images/editor-workspace.png
similarity index 100%
rename from src/node-red/terminology/images/editor-workspace.png
rename to nuxt/content-guides/node-red/terminology/images/editor-workspace.png
diff --git a/src/node-red/terminology/images/node-input-ouput-port.png b/nuxt/content-guides/node-red/terminology/images/node-input-ouput-port.png
similarity index 100%
rename from src/node-red/terminology/images/node-input-ouput-port.png
rename to nuxt/content-guides/node-red/terminology/images/node-input-ouput-port.png
diff --git a/src/node-red/terminology/images/node-palette.png b/nuxt/content-guides/node-red/terminology/images/node-palette.png
similarity index 100%
rename from src/node-red/terminology/images/node-palette.png
rename to nuxt/content-guides/node-red/terminology/images/node-palette.png
diff --git a/src/node-red/terminology/images/node-red-editor-deploy-button.png b/nuxt/content-guides/node-red/terminology/images/node-red-editor-deploy-button.png
similarity index 100%
rename from src/node-red/terminology/images/node-red-editor-deploy-button.png
rename to nuxt/content-guides/node-red/terminology/images/node-red-editor-deploy-button.png
diff --git a/src/node-red/terminology/images/node-red-editor.png b/nuxt/content-guides/node-red/terminology/images/node-red-editor.png
similarity index 100%
rename from src/node-red/terminology/images/node-red-editor.png
rename to nuxt/content-guides/node-red/terminology/images/node-red-editor.png
diff --git a/src/node-red/terminology/images/node-red-message-object.png b/nuxt/content-guides/node-red/terminology/images/node-red-message-object.png
similarity index 100%
rename from src/node-red/terminology/images/node-red-message-object.png
rename to nuxt/content-guides/node-red/terminology/images/node-red-message-object.png
diff --git a/src/node-red/terminology/images/node-red-node.png b/nuxt/content-guides/node-red/terminology/images/node-red-node.png
similarity index 100%
rename from src/node-red/terminology/images/node-red-node.png
rename to nuxt/content-guides/node-red/terminology/images/node-red-node.png
diff --git a/src/node-red/terminology/images/node-red-palette-manager.png b/nuxt/content-guides/node-red/terminology/images/node-red-palette-manager.png
similarity index 100%
rename from src/node-red/terminology/images/node-red-palette-manager.png
rename to nuxt/content-guides/node-red/terminology/images/node-red-palette-manager.png
diff --git a/src/node-red/terminology/images/node-red-subflow.png b/nuxt/content-guides/node-red/terminology/images/node-red-subflow.png
similarity index 100%
rename from src/node-red/terminology/images/node-red-subflow.png
rename to nuxt/content-guides/node-red/terminology/images/node-red-subflow.png
diff --git a/src/node-red/terminology/images/node-wire.png b/nuxt/content-guides/node-red/terminology/images/node-wire.png
similarity index 100%
rename from src/node-red/terminology/images/node-wire.png
rename to nuxt/content-guides/node-red/terminology/images/node-wire.png
diff --git a/nuxt/lib/core-node-help.json b/nuxt/lib/core-node-help.json
new file mode 100644
index 0000000000..323a542038
--- /dev/null
+++ b/nuxt/lib/core-node-help.json
@@ -0,0 +1,40 @@
+{
+ "common/catch": "
Catch errors thrown by nodes on the same tab.
\n
Outputs
\n
\n
error.message string
\n
the error message.
\n
error.source.id string
\n
the id of the node that threw the error.
\n
error.source.type string
\n
the type of the node that threw the error.
\n
error.source.name string
\n
the name, if set, of the node that threw the error.
\n
\n
Details
\n
If a node throws an error whilst handling a message, the flow will typically\n halt. This node can be used to catch those errors and handle them with a\n dedicated flow.
\n
By default, the node will catch errors thrown by any node on the same tab. Alternatively\n it can be targetted at specific nodes, or configured to only catch errors that\n have not already been caught by a 'targeted' catch node.
\n
When an error is thrown, all matching catch nodes will receive the message.
\n
If an error is thrown within a subflow, the error will get handled by any\n catch nodes within the subflow. If none exists, the error will be propagated\n up to the tab the subflow instance is on.
\n
If the message already has a error property, it is copied to _error.
",
+ "common/comment": "
A node you can use to add comments to your flows.
\n
Details
\n
The edit panel will accept Markdown syntax. The text will be rendered into\n the information side panel.
",
+ "common/complete": "
Trigger a flow when another node completes its handling of a message.
\n
Details
\n
If a node tells the runtime when it has finished handling a message,\n this node can be used to trigger a second flow.
\n
For example, this can be used alongside a node with no output port,\n such as the Email sending node, to continue the flow.
\n
This node must be configured to handle the event for selected nodes in the\n flow. Unlike the Catch node, it does not provide a 'handle all' mode automatically\n applies to all nodes in the flow.
\n
Not all nodes will trigger this event - it will depend on whether they\n have been implemented to support this feature as introduced in Node-RED 1.0.
",
+ "common/debug": "
Displays selected message properties in the debug sidebar tab and optionally the runtime log. By default it displays msg.payload, but can be configured to display any property, the full message or the result of a JSONata expression.
\n
Details
\n
The debug sidebar provides a structured view of the messages it is sent, making it easier to understand their structure.
\n
JavaScript objects and arrays can be collapsed and expanded as required. Buffer objects can be displayed as raw data or as a string if possible.
\n
Alongside each message, the debug sidebar includes information about the time the message was received, the node that sent it and the type of the message.\n Clicking on the source node id will reveal that node within the workspace.
\n
The button on the node can be used to enable or disable its output. It is recommended to disable or remove any Debug nodes that are not being used.
\n
The node can also be configured to send all messages to the runtime log, or to send short (32 characters) to the status text under the debug node.
",
+ "common/inject": "
Injects a message into a flow either manually or at regular intervals. The message\npayload can be a variety of types, including strings, JavaScript objects or the current time.
\n
Outputs
\n
\n
payloadvarious
\n
The configured payload of the message.
\n
topic string
\n
An optional property that can be configured in the node.
\n
\n
Details
\n
The Inject node can initiate a flow with a specific payload value.\nThe default payload is a timestamp of the current time in millisecs since January 1st, 1970.
\n
The node also supports injecting strings, numbers, booleans, JavaScript objects, or flow/global context values.
\n
By default, the node is triggered manually by clicking on its button within the editor. It can also be set to\ninject at regular intervals or according to a schedule.
\n
It can also be configured to inject once each time the flows are started.
\n
The maximum Interval that can be specified is about 596 hours / 24 days. However if you are looking at intervals\ngreater than one day you should consider using a scheduler node that can cope with power outages and restarts.
\n
Note: The \"Interval between times\" and \"at a specific time\" options use the standard cron system.\nThis means that 20 minutes will be at the next hour, 20 minutes past and 40 minutes past - not in 20 minutes time.\nIf you want every 20 minutes from now - use the \"interval\" option.
\n
Note: To include a newline in a string you must use the Function or Template node to create the payload.
",
+ "common/link": "
Create virtual wires between flows.
\n
Details
\n
The node can be connected to any link out node that exists on any tab.\n Once connected, they behave as if they were wired together.
\n
The wires between link nodes are only displayed when a link node is selected.\n If there are any wires to other tabs, a virtual node is shown that can be clicked\n on to jump to the appropriate tab.
\n
Note: Links cannot be created going into, or out of, a subflow.
",
+ "common/status": "
Report status messages from other nodes on the same tab.
\n
Outputs
\n
\n
status.text string
\n
the status text.
\n
status.source.type string
\n
the type of the node that reported status.
\n
status.source.id string
\n
the id of the node that reported status.
\n
status.source.name string
\n
the name, if set, of the node that reported status.
\n
\n
Details
\n
This node does not produce a payload.
\n
By default the node reports status for all nodes on the same workspace tab.\n It can be configured to selectively report status for individual nodes.
",
+ "common/unknown": "
This node is a type unknown to your installation of Node-RED.
\n
Details
\n
If you deploy with the node in this state, its configuration will be preserved, but\n the flow will not start until the missing type is installed.
\n
Use the Menu - Manage Palette option\n to search for and install nodes, or npm install <module> to\n install, any missing modules and restart Node-RED and reimport the nodes.
\n
It is possible this node type is already installed, but is missing a dependency. Check the Node-RED start-up\n log for any error messages associated with the missing node type.
\n
Otherwise, you should contact the author of the flow to obtain a copy of the missing node type.
",
+ "function/change": "
Set, change, delete or move properties of a message, flow context or global context.
\n
The node can specify multiple rules that will be applied in the order they are defined.
\n
Details
\n
The available operations are:
\n
\n
Set
\n
set a property. The value can be a variety of different types, or\n can be taken from an existing message or context property.
\n
Change
\n
search & replace parts of the property. If regular expressions\n are enabled, the \"replace with\" property can include capture groups, for\n example $1. Replace will only change the type if there\n is a complete match.
\n
Delete
\n
delete a property.
\n
Move
\n
move or rename a property.
\n
\n
The \"expression\" type uses the JSONata\n query and expression language.\n
",
+ "function/delay": "
Delays each message passing through the node or limits the rate at which they can pass.
\n
Not all input parameters apply to all modes.
\n
Inputs
\n
\n
delay number
\n
Sets the delay, in milliseconds, to be applied to the message. This\n option only applies if the node is configured to allow the message to\n override the configured default delay interval.
\n
rate number
\n
Sets the rate value in milliseconds between messages.\n This node overwrites the existing rate value defined in the node configuration\n when it receives the message which contains msg.rate value in milliSeconds.\n This option only applies if the node is configured to allow the message to\n override the configured default rate interval.
\n
reset
\n
If the received message has this property set to any value, all\n outstanding messages held by the node are cleared without being sent.
\n
flush
\n
If the received message has this property set to a numeric value then that many messages\n will be released immediately. If set to any other type (e.g. boolean), then all\n outstanding messages held by the node are sent immediately.
\n
toFront
\n
When in rate limit mode, if the received message has this property set to boolean true,\n then the message is pushed to the front of the queue and will be released next.\n This can be used in combination with msg.flush=1 to resend immediately.\n
\n
\n
Details
\n
When configured to delay messages, the delay interval can be a fixed value,\n a random value within a range or dynamically set for each message.\n Each message is delayed independently of any other message, based on\n the time of its arrival.\n
\n
When configured to rate limit messages, the node can operate in several modes.\n By default the messages are delivered evenly spread across\n the configured time period. e.g. 3 msgs in 12 seconds means 1 message every 4 seconds.\n The status shows the number of messages currently in the queue.\n It can optionally discard intermediate messages as they arrive.\n
\n
If set to allow override of the rate, the new rate will be applied immediately,\n and will remain in effect until changed again, the node is reset, or the flow is restarted.
\n
The rate limiting can be applied to all messages, or group them according to\n their msg.topic value. When grouping, intermediate messages are\n automatically dropped. At each time interval, the node can either release\n the most recent message for all topics, or release the most recent message\n for the next topic.\n
\n
In burst mode, messages are passed through up to the configured limit immediately,\n any further messages will be dropped or sent to the second output until the time\n period has elapsed. At that point the next burst of messages can be sent.\n The status shows green when messages can be sent immediately, and red when\n messages are blocked.\n
\n
Note: In rate limit mode the maximum queue depth can be set by a property in your\n settings.js file. For example nodeMessageBufferMaxLength: 1000,
",
+ "function/exec": "
Runs a system command and returns its output.
\n
The node can be configured to either wait until the command completes, or to\n send its output as the command generates it.
\n
The command that is run can be configured in the node or provided by the received\n message.
\n\n
Inputs
\n
\n
payload string
\n
if configured to do so, will be appended to the executed command.
\n
kill string
\n
the type of kill signal to send an existing exec node process.
\n
pid number|string
\n
the process ID of an existing exec node process to kill.
\n
\n\n
Outputs
\n \n
Standard output\n
\n
payload string
\n
the standard output of the command.
\n
\n
\n
rc object
\n
exec mode only, a copy of the return code object (also available on port 3)
\n
\n
\n
Standard error\n
\n
payload string
\n
the standard error of the command.
\n
\n
\n
rc object
\n
exec mode only, a copy of the return code object (also available on port 3)
\n
\n
\n
Return code\n
\n
payload object
\n
an object containing the return code, and possibly message, signal properties.
\n
\n
\n \n
Details
\n
By default uses the exec system call which calls the command, waits for it to complete, and then\n returns the output. For example a successful command should have a return code of { code: 0 }.
\n
Optionally can use spawn instead, which returns the output from stdout and stderr\n as the command runs, usually one line at a time. On completion it then returns an object\n on the 3rd port. For example, a successful command should return { code: 0 }.
\n
Errors may return extra information on the 3rd port msg.payload, such as a message string,\n signal string.
\n
The command that is run is defined within the node, with an option to append msg.payload and a further set of parameters.
\n
Commands or parameters with spaces should be enclosed in quotes - \"This is a single parameter\"
\n
The returned payload is usually a string, unless non-UTF8 characters are detected, in which\n case it is a buffer.
\n
The node's status icon and PID will be visible while the node is active. Changes to this can be read by the Status node.
\n
The Hide console option will hide the process console normally shown on Windows systems.
\n
Killing processes
\n
Sending msg.kill will kill a single active process. msg.kill should be a string containing\n the type of signal to be sent, for example, SIGINT, SIGQUIT or SIGHUP.\n Defaults to SIGTERM if set to an empty string.
\n
If the node has more than one process running then msg.pid must also be set with the value of the PID to be killed.
\n
If a value is provided in the Timeout field then, if the process has not completed when the specified number of seconds has elapsed, the process will be killed automatically
\n
Tip: if running a Python app you may need to use the -u parameter to stop the output being buffered.
",
+ "function/filter": "
filter node - only passes on data if the payload has changed.\n It can also block unless, or ignore if the value changes by a specified amount (Dead- and Narrowband mode).
\n
Inputs
\n
\n
payload\n number | string | (object)\n
\n
RBE mode will accept numbers, strings, and simple objects.\n Other modes must provide a parseable number.
\n
topic string\n
\n
if specified the function will work on a per topic basis. This property can be set by configuration.
\n
resetany
\n
if set clears the stored value for the specified msg.topic, or\n all topics if msg.topic is not specified.
\n
\n
Outputs
\n
\n
payload\n as per input\n
\n
If triggered the output will be the same as the input.
\n
\n
Details
\n
In RBE mode this node will block until the msg.payload,\n (or selected property) value is different to the previous one.\n If required it can ignore the initial value, so as not to send anything at start.
\n
The Deadband modes will block the incoming value\n unless its change is greater or greater-equal than ± the band gap away from a previous value.
\n
The Narrowband modes will block the incoming value,\n if its change is greater or greater-equal than ± the band gap away from the previous value.\n It is useful for ignoring outliers from a faulty sensor for example.
\n
Both in Deadband and Narrowband modes the incoming value must contain a parseable number and\n both also supports % - only sends if/unless the input differs by more than x% of the original value.
\n
Both Deadband and Narrowband allow comparison against either the previous valid output value, thus\n ignoring any values out of range, or the previous input value, which resets the set point, thus allowing\n gradual drift (deadband), or a step change (narrowband).
\n
Note: This works on a per msg.topic basis, though this can be changed to another property if desired.\n This means that a single filter node can handle multiple different topics at the same time.
",
+ "function/function": "
A JavaScript function to run against the messages being received by the node.
\n
The messages are passed in as a JavaScript object called msg.
\n
By convention it will have a msg.payload property containing\n the body of the message.
\n
The function is expected to return a message object (or multiple message objects), but can choose\n to return nothing in order to halt a flow.
\n
The On Start tab contains code that will be run whenever the node is started.\n The On Stop tab contains code that will be run when the node is stopped.
\n
If the On Start code returns a Promise object, the node will not start handling messages\n until the promise is resolved.
The function can either return the messages it wants to pass on to the next nodes\n in the flow, or can call node.send(messages).
\n
It can return/send:
\n
\n
a single message object - passed to nodes connected to the first output
\n
an array of message objects - passed to nodes connected to the corresponding outputs
\n
\n
Note: The setup code is executed during the initialization of nodes. Therefore, if node.send is called in the setup tab, subsequent nodes may not be able to receive the message.
\n
If any element of the array is itself an array of messages, multiple\n messages are sent to the corresponding output.
\n
If null is returned, either by itself or as an element of the array, no\n message is passed on.
\n
Logging and Error Handling
\n
To log any information, or report an error, the following functions are available:
\n
\n
node.log(\"Log message\")
\n
node.warn(\"Warning\")
\n
node.error(\"Error\")
\n
\n
\n
The Catch node can also be used to handle errors. To invoke a Catch node,\n pass msg as a second argument to node.error:
\n
node.error(\"Error\",msg);
\n
Accessing Node Information
\n
The following properties are available to access information about the node:
\n
\n
node.id - id of the node
\n
node.name - name of the node
\n
node.outputCount - number of node outputs
\n
\n
Using environment variables
\n
Environment variables can be accessed using env.get(\"MY_ENV_VAR\").
",
+ "function/range": "
Maps a numeric value to a different range.
\n
Inputs
\n
\n
payload number
\n
The payload must be a number. Anything else will try to be\n parsed into a number and rejected if that fails.
\n
\n
Outputs
\n
\n
payload number
\n
The value mapped to the new range.
\n
\n
Details
\n
This node will linearly scale the received value. By default, the result\n is not constrained to the range defined in the node.
\n
Scale and limit to target range means that the result will never be outside\n the range specified within the target range.
\n
Scale and wrap within the target range means that the result will\n be wrapped within the target range.
\n
Scale, but drop if outside input range means that the result will\n be scaled, but any inputs outside of the inout range will be dropped.
\n
For example an input 0 - 10 mapped to 0 - 100.
\n
\n
mode
input
output
\n
scale
12
120
\n
limit
12
100
\n
wrap
12
20
\n
drop
12
(no output)
\n
",
+ "function/switch": "
Route messages based on their property values or sequence position.
\n
Details
\n
When a message arrives, the node will evaluate each of the defined rules\n and forward the message to the corresponding outputs of any matching rules.
\n
Optionally, the node can be set to stop evaluating rules once it finds one\n that matches.
\n
The rules can be evaluated against an individual message property, a flow or global\n context property, environment variable or the result of a JSONata expression.
\n
Rules
\n
There are four types of rule:
\n \n
Value rules are evaluated against the configured property
\n
Sequence rules can be used on message sequences, such as those\n generated by the Split node
\n
A JSONata Expression can be provided that will be evaluated\n against the whole message and will match if the expression returns\n a true value.
\n
An Otherwise rule can be used to match if none of the preceeding\n rules have matched.
\n \n
Notes
\n
The is true/false and is null rules perform strict\n comparisons against those types. They do not convert between types.
\n
The is empty and is not empty rules can be used to test the length of Strings, Arrays and Buffers, or the number of properties an Object has. Neither rule will pass if the property being tested has a boolean, null \n or undefined value.
\n
Handling message sequences
\n
By default, the node does not modify the msg.parts property of messages\n that are part of a sequence.
\n
The recreate message sequences option can be enabled to generate new message sequences\n for each rule that matches. In this mode, the node will buffer the entire incoming\n sequence before sending the new sequences on. The runtime setting nodeMessageBufferMaxLength\n can be used to limit how many messages nodes will buffer.
",
+ "function/template": "
Sets a property based on the provided template.
\n
Inputs
\n
\n
msg object
\n
A msg object containing information to populate the template.
\n
template string
\n
A template to be populated from msg.payload. If not configured in the edit panel,\n this can be set as a property of msg.
\n
\n
Outputs
\n
\n
msg object
\n
a msg with a property set by populating the configured template with properties from the incoming msg.
\n
\n
Details
\n
By default this uses the mustache\n format, but this can be switched off if required.
It is possible to use a property from the flow context or global context. Just use {{flow.name}} or\n {{global.name}}, or for persistable store store use {{flow[store].name}} or\n {{global[store].name}}.\n
Note: By default, mustache will escape any non-alphanumeric or HTML entities in the values it substitutes.\n To prevent this, use {{{triple}}} braces.
\n
If you need to use {{ }} within your content, you can change the characters\n used to mark the templated sections. For example, to use [[ ]]\n instead, add the following line to the top of the template:
\n
{{=[[ ]]=}}
\n
Using environment variables
\n
The template node can access environment variables using the syntax:
\n
My favourite colour is {{env.COLOUR}}.
",
+ "function/trigger": "
When triggered, can send a message, and then optionally a second message, unless extended or reset.
\n\n
Inputs
\n
\n
delay number
\n
Sets the delay, in milliseconds, to be applied to the message. This option only applies if the node is configured to allow the message to override the configured default delay interval.
\n
reset
\n
If a message is received with this property, any timeout or repeat\n currently in progress will be cleared and no message triggered.
\n
\n\n
Details
\n
This node can be used to create a timeout within a flow. By default, when\n it receives a message, it sends on a message with a payload of 1.\n It then waits 250ms before sending a second message with a payload of 0.\n This could be used, for example, to blink an LED attached to a Raspberry Pi GPIO pin.
\n
The payloads of each message sent can be configured to a variety of values, including\n the option to not send anything. For example, setting the initial message to nothing and\n selecting the option to extend the timer with each received message, the node will\n act as a watchdog timer; only sending a message if nothing is received within the\n set interval.
\n
If set to a string type, the node supports the mustache template syntax.
\n
The delay between sending messages can be overridden by msg.delay if that option is enabled in the node. The value must be provided in milliseconds.
\n
If the node receives a message with a reset property, or a payload\n that matches that configured in the node, any timeout or repeat currently in\n progress will be cleared and no message triggered.
\n
The node can be configured to resend a message at a regular interval until it\n is reset by a received message.
\n
Optionally, the node can be configured to treat messages as if they are separate streams,\n using a msg property to identify each stream. Default msg.topic.
\n
The status indicates the node is currently active. If multiple streams are used the status\n indicates the number of streams being held.
",
+ "network/http-in": "
Creates an HTTP end-point for creating web services.
\n
Outputs
\n
\n
payload
\n
For a GET request, contains an object of any query string parameters.\n Otherwise, contains the body of the HTTP request.
\n
reqobject
\n
An HTTP request object. This object contains multiple properties that\n provide information about the request.\n
\n
body - the body of the incoming request. The format\n will depend on the request.
\n
headers - an object containing the HTTP request headers.
\n
query - an object containing any query string parameters.
\n
params - an object containing any route parameters.
\n
cookies - an object containing the cookies for the request.
\n
files - if enabled within the node, an object containing\n any files uploaded as part of a POST request.
\n
\n
\n
resobject
\n
An HTTP response object. This property should not be used directly;\n the HTTP Response node documents how to respond to a request.\n This property must remain attached to the message passed to the response node.
\n
\n
Details
\n
The node will listen on the configured path for requests of a particular type.\n The path can be fully specified, such as /user, or include\n named parameters that accept any value, such as /user/:name.\n When named parameters are used, their actual value in a request can be accessed under msg.req.params.
\n
For requests that include a body, such as a POST or PUT, the contents of\n the request is made available as msg.payload.
\n
If the content type of the request can be determined, the body will be parsed to\n any appropriate type. For example, application/json will be parsed to\n its JavaScript object representation.
\n
The node can be configured to not parse the body, in which case it will be provided as a Buffer object.
\n
Note: this node does not send any response to the request. The flow\n must include an HTTP Response node to complete the request.
",
+ "network/http-proxy": "
Configuration options for HTTP proxy.
\n\n
Details
\n
When accessing to the host in the ignored host list, no proxy will be used.
",
+ "network/http-request": "
Sends HTTP requests and returns the response.
\n\n
Inputs
\n
\n
url string
\n
If not configured in the node, this optional property sets the url of the request.
\n
method string
\n
If not configured in the node, this optional property sets the HTTP method of the request.\n Must be one of GET, PUT, POST, PATCH or DELETE.
\n
headers object
\n
Sets the HTTP headers of the request. NOTE: Any headers set in the node configuration will overwrite any matching headers in msg.headers
\n
cookies object
\n
If set, can be used to send cookies with the request.
\n
payload
\n
Sent as the body of the request.
\n
rejectUnauthorized
\n
If set to false, allows requests to be made to https sites that use\n self signed certificates.
\n
followRedirects
\n
If set to false prevent following Redirect (HTTP 301).true by default
\n
requestTimeout
\n
If set to a positive number of milliseconds, will override the globally set httpRequestTimeout parameter.
\n
\n
Outputs
\n
\n
payload string | object | buffer
\n
The body of the response. The node can be configured to return the body\n as a string, attempt to parse it as a JSON string or leave it as a\n binary buffer.
\n
statusCode number
\n
The status code of the response, or the error code if the request could not be completed.
\n
headers object
\n
An object containing the response headers.
\n
responseUrl string
\n
In case any redirects occurred while processing the request, this property is the final redirected url.\n Otherwise, the url of the original request.
\n
responseCookies object
\n
If the response includes cookies, this property is an object of name/value pairs for each cookie.
\n
redirectList array
\n
If the request was redirected one or more times, the accumulated information will be added to this property. `location` is the next redirect destination. `cookies` is the cookies returned from the redirect source.
\n
\n
Details
\n
When configured within the node, the URL property can contain mustache-style tags. These allow the\n url to be constructed using values of the incoming message. For example, if the url is set to\n example.com/{{{topic}}}, it will have the value of msg.topic automatically inserted.\n Using {{{...}}} prevents mustache from escaping characters like / & etc.
\n
The node can optionally automatically encode msg.payload as query string parameters for a GET request, in which case msg.payload has to be an object.
\n
Note: If running behind a proxy, the standard http_proxy=... environment variable should be set and Node-RED restarted, or use Proxy Configuration. If Proxy Configuration was set, the configuration take precedence over environment variable.
\n
Using multiple HTTP Request nodes
\n
In order to use more than one of these nodes in the same flow, care must be taken with\n the msg.headers property. The first node will set this property with\n the response headers. The next node will then use those headers for its request - this\n is not usually the right thing to do. If msg.headers property is left unchanged\n between nodes, it will be ignored by the second node. To set custom headers, msg.headers\n should first be deleted or reset to an empty object: {}.\n
Cookie handling
\n
The cookies property passed to the node must be an object of name/value pairs.\n The value can be either a string to set the value of the cookie or it can be an\n object with a single value property.
\n
Any cookies returned by the request are passed back under the responseCookies property.
\n
Content type handling
\n
If msg.payload is an Object, the node will automatically set the content type\n of the request to application/json and encode the body as such.
\n
To encode the request as form data, msg.headers[\"content-type\"] should be set to application/x-www-form-urlencoded.
\n
File Upload
\n
To perform a file upload, msg.headers[\"content-type\"] should be set to multipart/form-data\n and the msg.payload passed to the node must be an object with the following structure:
The values of KEY, FILE_CONTENTS and FILENAME\n should be set to the appropriate values.
",
+ "network/mqtt-in": "
Connects to a MQTT broker and subscribes to messages from the specified topic.
\n
Outputs
\n
\n
payload string | buffer
\n
a string unless detected as a binary buffer.
\n
topic string
\n
the MQTT topic, uses / as a hierarchy separator.
\n
qos number
\n
0, fire and forget - 1, at least once - 2, once and once only.
\n
retain boolean
\n
true indicates the message was retained and may be old.
\n\n
responseTopic string
\n
MQTTv5: the MQTT response topic for the message
\n
correlationData Buffer
\n
MQTTv5: the correlation data for the message
\n
contentType string
\n
MQTTv5: the content-type of the payload
\n
userProperties object
\n
MQTTv5: any user properties of the message
\n
messageExpiryInterval number
\n
MQTTv5: the expiry time, in seconds, of the message
\n
\n
Details
\n The subscription topic can include MQTT wildcards, + for one level, # for multiple levels.\n
This node requires a connection to a MQTT broker to be configured. This is configured by clicking\n the pencil icon.
\n
Several MQTT nodes (in or out) can share the same broker connection if required.
\n
Dynamic Subscription
\n The node can be configured to dynamically control the MQTT connection and its subscriptions. When\n enabled, the node will have an input and can be controlled by passing it messages.\n
Inputs
\n
These only apply when the node has been configured for dynamic subscriptions.
\n
\n
action string
\n
the name of the action the node should perform. Available actions are: \"connect\",\n \"disconnect\", \"getSubscriptions\", \"subscribe\" and \n \"unsubscribe\".
\n
topic string|object|array
\n
For the \"subscribe\" and \"unsubscribe\" actions, this property\n provides the topic. It can be set as either:
\n
a String containing the topic filter
\n
an Object containing topic and qos properties
\n
an array of either strings or objects to handle multiple topics in one
\n
\n
\n
broker broker
\n
For the \"connect\" action, this property can override any\n of the individual broker configuration settings, including:
\n
broker
\n
port
\n
url - overrides broker/port to provide a complete connection url
\n
username
\n
password
\n
\n
If this property is set and the broker is already connected an error\n will be logged unless it has the force property set - in which case it will\n disconnect from the broker, apply the new settings and reconnect.
\n
\n
",
+ "network/mqtt-out": "
Connects to a MQTT broker and publishes messages.
\n
Inputs
\n
\n
payload string | buffer
\n
the payload to publish. If this property is not set, no message will be sent. To send a blank message, set this property to an empty String.
\n
topic string
\n
the MQTT topic to publish to.
\n
qos number
\n
0, fire and forget - 1, at least once - 2, once and once only. Default 0.
\n
retain boolean
\n
set to true to retain the message on the broker. Default false.
\n
responseTopic string
\n
MQTTv5: the MQTT response topic for the message
\n
correlationData Buffer
\n
MQTTv5: the correlation data for the message
\n
contentType string
\n
MQTTv5: the content-type of the payload
\n
userProperties object
\n
MQTTv5: any user properties of the message
\n
messageExpiryInterval number
\n
MQTTv5: the expiry time, in seconds, of the message
\n
topicAlias number
\n
MQTTv5: the MQTT topic alias to use
\n
\n
Details
\n msg.payload is used as the payload of the published message.\n If it contains an Object it will be converted to a JSON string before being sent.\n If it contains a binary Buffer the message will be published as-is.\n
The topic used can be configured in the node or, if left blank, can be set by msg.topic.
\n
Likewise the QoS and retain values can be configured in the node or, if left\n blank, set by msg.qos and msg.retain respectively. To clear a previously\n retained topic from the broker, send a blank message to that topic with the retain flag set.
\n
This node requires a connection to a MQTT broker to be configured. This is configured by clicking\n the pencil icon.
\n
Several MQTT nodes (in or out) can share the same broker connection if required.
\n\n
Dynamic Control
\n The connection shared by the node can be controlled dynamically. If the node receives\n one of the following control messages, it will not publish the message payload as well.\n
Inputs
\n
\n
action string
\n
the name of the action the node should perform. Available actions are: \"connect\",\n and \"disconnect\".
\n
broker broker
\n
For the \"connect\" action, this property can override any\n of the individual broker configuration settings, including:
\n
broker
\n
port
\n
url - overrides broker/port to provide a complete connection url
\n
username
\n
password
\n
\n
If this property is set and the broker is already connected an error\n will be logged unless it has the force property set - in which case it will\n disconnect from the broker, apply the new settings and reconnect.
\n
\n
",
+ "network/tcp-in": "
Provides a choice of TCP inputs. Can either connect to a remote TCP port,\n or accept incoming connections.
\n
Note: On some systems you may need root or administrator access\n to access ports below 1024.
",
+ "network/tls": "
Configuration options for TLS connections.
",
+ "network/udp-in": "
A UDP input node, that produces a msg.payload containing a\n Buffer, string, or base64 encoded string. Supports multicast.
\n
It also provides msg.ip and msg.port set to the\n ip address and port from which the message was received.
\n
Note: On some systems you may need root or administrator access to use\n ports below 1024 and/or broadcast.
",
+ "network/udp-out": "
This node sends msg.payload to the designated UDP host and port. Supports multicast.
\n
You may also use msg.ip and msg.port to set the destination values, but the statically configured values have precedence.
\n
If you select broadcast either set the address to the local broadcast ip address, or maybe try 255.255.255.255, which is the global broadcast address.
\n
Note: On some systems you may need to be root to use ports below 1024 and/or broadcast.
",
+ "network/websocket": "
WebSocket input node.
\n
By default, the data received from the WebSocket will be in msg.payload.\n The socket can be configured to expect a properly formed JSON string, in which\n case it will parse the JSON and send on the resulting object as the entire message.
",
+ "parsers/csv": "
Converts between a CSV formatted string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | array | string
\n
A JavaScript object, array or CSV string.
\n
\n
Outputs
\n
\n
payloadobject | array | string
\n
\n
\n
If the input is a string it tries to parse it as CSV and creates a JavaScript object of key/value pairs for each line.\n The node will then either send a message for each line, or a single message containing an array of objects.
\n
If the input is a JavaScript object it tries to build a CSV string.
\n
If the input is an array of simple values, it builds a single line CSV string.
\n
If the input is an array of arrays, or an array of objects, a multiple-line CSV string is created.
\n
\n
\n
\n
Details
\n
The column template can contain an ordered list of column names. When converting CSV to an object, the column names\n will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.\n
When the RFC parser is selected, the column template must be compliant with RFC4180.
\n \n
When converting to CSV, the columns template is used to identify which properties to extract from the object and in what order.
\n
If the columns template is blank then you can use a simple comma separated list of properties supplied in msg.columns to\n determine what to extract and in what order. If neither are present then all the object properties are output in the order\n in which the properties are found in the first row.
\n
If the input is an array then the columns template is only used to optionally generate a row of column titles.
\n
If 'parse numerical values' option is checked, string numerical values will be returned as numbers, ie. middle value '1,\"1.5\",2'.
\n
If 'include empty strings' option is checked, empty strings will be returned in result, ie. middle value '\"1\",\"\",3'.
\n
If 'include null values' option is checked, null values will be returned in result, ie. middle value '\"1\",,3'.
\n
The node can accept a multi-part input as long as the parts property is set correctly, for example from a file-in node or split node.
\n
If outputting multiple messages they will have their parts property set and form a complete message sequence.
\n
If the node is set to only send column headers once, then setting msg.reset to any value will cause the node to resend the headers.
\n
Note: the column template must be comma separated - even if a different separator is chosen for the data.
\n
Note: in RFC mode, catchable errors will be thrown for malformed CSV headers and invalid input payload data
",
+ "parsers/html": "
Extracts elements from an html document held in msg.payload using a CSS selector.
\n
Inputs
\n
\n
payload string
\n
the html string from which to extract elements.
\n
select string
\n
if not configured in the edit panel the selector can be set as a property of msg.
\n
\n
Output
\n
\n
payload array | string
\n
the result can be either a single message with a payload containing an array of the matched elements, or multiple\n messages that each contain a matched element. If multiple messages are sent they will also have parts set.
\n
\n
Details
\n
This node supports a combination of CSS and jQuery selectors. See the\n css-select documentation for more information\n on the supported syntax.
",
+ "parsers/json": "
Converts between a JSON string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | string
\n
A JavaScript object or JSON string.
\n
schemaobject
\n
An optional JSON Schema object to validate the payload against.\n The property will be deleted before the msg is sent to the next node.
\n
\n
Outputs
\n
\n
payloadobject | string
\n
\n
\n
If the input is a JSON string it tries to parse it to a JavaScript object.
\n
If the input is a JavaScript object it creates a JSON string. The string can optionally be well-formatted.
\n
\n
\n
schemaErrorarray
\n
If JSON schema validation fails, the catch node will have a schemaError property\n containing an array of errors.
\n
\n
Details
\n
By default, the node operates on msg.payload, but can be configured\n to convert any message property.
\n
The node can also be configured to ensure a particular encoding instead of toggling\n between the two. This can be used, for example, with the HTTP In\n node to ensure the payload is a parsed object even if an incoming request\n did not set its content-type correctly for the HTTP In node to do the conversion.
\n
If the node is configured to ensure the property is encoded as a String and it\n receives a String, no further checks will be made of the property. It will\n not check the String is valid JSON nor will it reformat it if the format option\n is selected.
\n
For more details about JSON Schema you can consult the specification\n here.
",
+ "parsers/xml": "
Converts between an XML string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | string
\n
A JavaScript object or XML string.
\n
options object
\n
This optional property can be used to pass in any of the options supported by the underlying\n library used to convert to and from XML. See the xml2js docs\n for more information.
\n
\n
Outputs
\n
\n
payloadobject | string
\n
\n
\n
If the input is a string it tries to parse it as XML and creates a JavaScript object.
\n
If the input is a JavaScript object it tries to build an XML string.
\n
\n
\n
\n
Details
\n
When converting between XML and an object, any XML attributes are added as a property named $ by default.\n Any text content is added as a property named _. These property names can be specified in the node configuration.
\n
For example, the following XML will be converted as shown:
Converts between a YAML formatted string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | string
\n
A JavaScript object or YAML string.
\n
\n
Outputs
\n
\n
payloadobject | string
\n
\n
\n
If the input is a YAML string it tries to parse it to a JavaScript object.
\n
If the input is a JavaScript object it creates a YAML string.
\n
\n
\n
",
+ "sequence/batch": "
Creates sequences of messages based on various rules.
\n
Details
\n
There are three modes for creating message sequences:
\n
\n
Number of messages
\n
groups messages into sequences of a given length. The overlap\n option specifies how many messages at the end of one sequence should be\n repeated at the start of the next sequence.
\n\n
Time interval
\n
groups messages that arrive within the specified interval. If no messages\n arrive within the interval, the node can optionally send on an empty message.
\n\n
Concatenate Sequences
\n
creates a message sequence by concatenating incoming sequences. Each message\n must have a msg.topic property and a msg.parts property\n identifying its sequence. The node is configured with a list of topic\n values to identify the order sequences are concatenated.\n
\n
\n
Storing messages
\n
This node will buffer messages internally in order to work across sequences. The\n runtime setting nodeMessageBufferMaxLength can be used to limit how many messages nodes\n will buffer.
\n
If a message is received with the msg.reset property set, the buffered messages are deleted and not sent.
",
+ "sequence/join": "
Joins sequences of messages into a single message.
\n
There are three modes available:
\n
\n
automatic
\n
When paired with the split node, it will automatically join the messages to reverse the split that was performed.
\n
manual
\n
Join sequences of messages in a variety of ways.
\n
reduce sequence
\n
Apply an expression against all messages in a sequence to reduce it to a single message.
\n
\n
Inputs
\n
\n
partsobject
\n
To automatically join a sequence of messages, they should all have\n this property set. The split node generates this property but it\n can be manually created. It has the following properties:\n
\n
id - an identifier for the group of messages
\n
index - the position within the group
\n
count - the total number of messages in the group
\n
type - the type of message - string/array/object/buffer
\n
ch - for a string or buffer, the data used to the split the message as either the string or an array of bytes
\n
key - for an object, the key of the property this message was created from
\n
len - the length of each message when split using a fixed length value
\n
\n
\n
complete
\n
If set, the node will append the payload, and then send the output message in its current state.\n If you don't wish to append the payload, delete it from the msg.
\n
reset
\n
If set, the node will clear any partially complete message and not send it.
\n
restartTimeout
\n
If set, and the node has a timeout configured, that timeout will be restarted.
\n
\n
Details
\n\n
Automatic mode
\n
Automatic mode uses the parts property of incoming messages to\n determine how the sequence should be joined. This allows it to automatically\n reverse the action of a split node.
\n\n
Manual mode
\n
When configured to join in manual mode, the node is able to join sequences\n of messages into a number of different results:
\n
\n
a string or buffer - created by joining the selected property of each message with the specified join characters or buffer.
\n
an array - created by adding each selected property, or entire message, to the output array.
\n
a key/value object - created by using a property of each message to determine the key under which\n the required value is stored.
\n
a merged object - created by merging the property of each message under a single object.
\n
\n
The other properties of the output message are taken from the last message received before the result is sent.
\n
A count can be set for how many messages should be received before generating the output message.\n For object outputs, once this count has been reached, the node can be configured to send a message for each subsequent message\n received.
\n
A timeout can be set to trigger sending the new message using whatever has been received so far.\n This timeout can be restarted by sending a message with the msg.restartTimeout property set.
\n
If a message is received with the msg.complete property set, the output message is finalised and sent.\n This resets any part counts.
\n
If a message is received with the msg.reset property set, the partly complete message is deleted and not sent.\n This resets any part counts.
\n\n
Reduce Sequence mode
\n
When configured to join in reduce mode, an expression is applied to each\n message in a sequence and the result accumulated to produce a single message.
\n\n
\n
Initial value
\n
The initial value of the accumulated value ($A).
\n
Reduce expression
\n
A JSONata expression that is called for each message in the sequence.\n The result is passed to the next call of the expression as the accumulated value.\n In the expression, the following special variables can be used:\n
\n
$A: the accumulated value,
\n
$I: index of the message in the sequence,
\n
$N: number of messages in the sequence.
\n
\n
\n
Fix-up expression
\n
An optional JSONata expression that is applied after the reduce expression\n has been applied to all messages in the sequence.\n In the expression, following special variables can be used:\n
\n
$A: the accumulated value,
\n
$N: number of messages in the sequence.
\n
\n
\n
By default, the reduce expression is applied in order, from the first\n to the last message of the sequence. It can optionally be applied in\n reverse order.
\n
$N is the number of messages that arrive - even if they are identical.
\n
\n
Example: the following settings, given a sequence of numeric values,\n calculates the average value:\n
\n
Reduce expression: $A+payload
\n
Initial value: 0
\n
Fix-up expression: $A/$N
\n
\n \n
Storing messages
\n
This node will buffer messages internally in order to work across sequences. The\n runtime setting nodeMessageBufferMaxLength can be used to limit how many messages nodes\n will buffer.
",
+ "sequence/sort": "
A function that sorts message property or a sequence of messages.
\n
When configured to sort message property, the node sorts array data pointed to by specified message property.
\n
When configured to sort a sequence of messages, it will reorder the messages.
\n
The sorting order can be:
\n
\n
ascending,
\n
descending.
\n
\n
For numbers, numerical ordering can be specified by a checkbox.
\n
Sort key can be element value or JSONata expression for sorting property value, or message property or JSONata expression for sorting a message sequence.
\n
When sorting a message sequence, the sort node relies on the received messages to have msg.parts set. The split node generates this property, but can be manually created. It has the following properties:
\n
\n
\n
id - an identifier for the group of messages
\n
index - the position within the group
\n
count - the total number of messages in the group
\n
\n \n
Note: This node internally keeps messages for its operation. In order to prevent unexpected memory usage, maximum number of messages kept can be specified. Default is no limit on number of messages.\n
\n
nodeMessageBufferMaxLength property set in settings.js.
\n
\n ",
+ "sequence/split": "
Splits a message into a sequence of messages.
\n\n
Inputs
\n
\n
payloadobject | string | array | buffer
\n
The behaviour of the node is determined by the type of msg.payload:\n
\n
string/buffer - the message is split using the specified character (default: \\n), buffer sequence or into fixed lengths.
\n
array - the message is split into either individual array elements, or arrays of a fixed-length.
\n
object - a message is sent for each key/value pair of the object.
\n
\n
\n
\n
Outputs
\n
\n
partsobject
\n
This property contains information about how the message was split from\n the original message. If passed to the join node, the sequence can be\n reassembled into a single message. The property has the following properties:\n
\n
id - an identifier for the group of messages
\n
index - the position within the group
\n
count - if known, the total number of messages in the group. See 'streaming mode' below.
\n
type - the type of message - string/array/object/buffer
\n
ch - for a string or buffer, the data used to the split the message as either the string or an array of bytes
\n
key - for an object, the key of the property this message was created from. The node can be configured to also copy this value to another message properties, such as msg.topic.
\n
len - the length of each message when split using a fixed length value
\n
\n
\n
\n
Details
\n
This node makes it easy to create a flow that performs common actions across\n a sequence of messages before, using the join node, recombining the\n sequence into a single message.
\n
It uses the msg.parts property to track the individual parts\n of a sequence.
\n
Streaming mode
\n
The node can also be used to reflow a stream of messages. For example, a\n serial device that sends newline-terminated commands may deliver a single message\n with a partial command at its end. In 'streaming mode', this node will split\n a message and send each complete segment. If there is a partial segment at the end,\n the node will hold on to it and prepend it to the next message that is received.\n
\n
When operating in this mode, the node will not set the msg.parts.count\n property as it does not know how many messages to expect in the stream. This\n means it cannot be used with the join node in its automatic mode.
",
+ "storage/read-file": "
Watches a directory or file for changes.
\n
You can enter a list of comma separated directories and/or files. You will\n need to put quotes \"...\" around any that have spaces in.
\n
On Windows you must use double back-slashes \\\\ in any directory names.
\n
The full filename of the file that actually changed is put into msg.payload and msg.filename,\n while a stringified version of the watch list is returned in msg.topic.
\n
msg.file contains just the short filename of the file that changed.\n msg.type has the type of thing changed, usually file or directory,\n while msg.size holds the file size in bytes.
\n
Of course in Linux, everything is a file and thus can be watched...
\n
Note: The directory or file must exist in order to be watched. If the file\n or directory gets deleted it may no longer be monitored even if it gets re-created.
",
+ "storage/write-file": "
Writes msg.payload to a file, either adding to the end or replacing the existing content.\n Alternatively, it can delete the file.
\n
Inputs
\n
\n
filename string
\n
The name of the file to be updated can be provided in the node configuration, or as a message property. \n By default it will use msg.filename but this can be customised in the node.\n
\n
encoding string
\n
If encoding is configured to be set by msg, then this optional property can set the encoding.\n
\n
Output
\n
On completion of write, input message is sent to output port.
\n
Details
\n
Each message payload will be added to the end of the file, optionally appending\n a newline (\\n) character between each one.
\n
If msg.filename is used the file will be closed after every write.\n For best performance use a fixed filename.
\n
It can be configured to overwrite the entire file rather than append. For example,\n when writing binary data to a file, such as an image, this option should be used\n and the option to append a newline should be disabled.
\n
Encoding of data written to a file can be specified from list of encodings.
\n
Alternatively, this node can be configured to delete the file.
"
+}
diff --git a/nuxt/lib/core-nodes-sync.mjs b/nuxt/lib/core-nodes-sync.mjs
new file mode 100644
index 0000000000..6d3edbd2e9
--- /dev/null
+++ b/nuxt/lib/core-nodes-sync.mjs
@@ -0,0 +1,214 @@
+// Generates the /docs/node-red/core-nodes/** pages.
+//
+// These are the one part of the Node-RED library that was never a file. Under Eleventy,
+// `src/node-red/core-nodes/*.njk` paginated over `src/_data/coreNodes.json` and each page
+// called a shortcode that fetched the node's help straight out of the Node-RED project's
+// repo at build time (`lib/core-node-docs.js`). @nuxt/content indexes files, so the pages
+// have to exist as markdown before it runs.
+//
+// The help text is upstream's, so it is mirrored rather than owned: `data/core-node-help.json`
+// holds a snapshot, committed, and `refreshCoreNodeHelp` re-fetches it on demand. That
+// keeps the production build hermetic. Fetching per build would put a third-party raw
+// githubusercontent URL on the critical path of every deploy and fail the site whenever
+// upstream renamed a locale file.
+//
+// The FlowFuse-authored half is `src/_includes/core-nodes/-use-case.md`: a short
+// "why you would reach for this node" intro that exists for every node. That is the part
+// worth keeping, so it leads the page and the mirrored help follows under its own heading.
+// Those fragments are Eleventy markdown and most of them embed a flow, so they go through
+// the same library-markdown transforms as the rest of the section rather than being
+// inlined raw - otherwise `{% renderFlow %}` reaches @nuxt/content as literal text.
+
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
+import { dirname, join } from 'node:path'
+
+import { processLibraryMarkdown } from './library-markdown.mjs'
+
+const UPSTREAM = 'https://raw.githubusercontent.com/node-red/node-red/master/packages/node_modules/%40node-red/nodes/locales/en-US'
+
+/** The sidebar label and ordering for each palette category. */
+export const CATEGORIES = {
+ common: { title: 'Common', order: 1 },
+ function: { title: 'Function', order: 2 },
+ network: { title: 'Network', order: 3 },
+ sequence: { title: 'Sequence', order: 4 },
+ parsers: { title: 'Parsers', order: 5 },
+ storage: { title: 'Storage', order: 6 },
+}
+
+/** `Read File` -> `read-file`, matching the URLs these pages have always had. */
+export function slugFor (name) {
+ return String(name).toLowerCase().replace(/\s+/g, '-')
+}
+
+/**
+ * Pull one node's help out of a locale file.
+ *
+ * A locale file can hold help for several nodes, each in its own
+ * `',
+ '',
+ '',
+ ].join('\n')
+
+ assert.equal(extractHelp(html, 'mqtt in'), 'IN')
+ assert.equal(extractHelp(html, 'mqtt out'), 'OUT')
+})
+
+test('a hyphenated request still finds a space-separated help name', () => {
+ // The regression this guards: coreNodes.json asked for "mqtt-in" while upstream
+ // renamed the block to "mqtt in". The Eleventy xpath found nothing and the page
+ // shipped an empty help section instead of failing.
+ const html = ''
+
+ assert.equal(extractHelp(html, 'mqtt-in'), 'IN')
+})
+
+test('a name that matches nothing returns empty rather than the wrong node', () => {
+ const html = ''
+
+ assert.equal(extractHelp(html, 'tcp in'), '')
+})
+
+test('the catalogue flattens to nodes carrying their category and slug', () => {
+ const nodes = listNodes(CATALOGUE)
+
+ assert.deepEqual(nodes.map(n => `${n.category}/${n.slug}`), ['common/inject', 'network/mqtt-in'])
+})
+
+test('a refresh that cannot find a node throws instead of storing nothing', async () => {
+ const fetchImpl = async () => ({ ok: true, text: async () => '' })
+
+ await assert.rejects(
+ () => refreshCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl }),
+ /no data-help-name/
+ )
+})
+
+test('a refresh propagates an upstream HTTP failure', async () => {
+ const fetchImpl = async () => ({ ok: false, status: 404 })
+
+ await assert.rejects(() => refreshCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl }), /404/)
+})
+
+test('a page leads with the FlowFuse use case and puts the mirrored help under its own heading', () => {
+ const out = renderCoreNodePage(
+ { name: 'Inject', description: 'Injects a message' },
+ { useCase: 'Reach for this to kick a flow off.', help: '
upstream
', navOrder: 1 }
+ )
+
+ assert.match(out, /^---\n/)
+ assert.match(out, /navTitle: "Inject"/)
+ assert.match(out, /# Inject/)
+ assert.ok(out.indexOf('Reach for this') < out.indexOf('## Node help'),
+ 'the use case must come before the mirrored help')
+ assert.match(out, /mirrored from the Node-RED project/)
+ assert.match(out, /
upstream<\/p>/)
+})
+
+test('a sync missing any node help fails loudly and names the node', () => {
+ assert.throws(
+ () => syncCoreNodes({
+ repoRoot: '/nope',
+ nuxtRoot: '/nope/nuxt',
+ coreNodes: CATALOGUE,
+ help: { 'common/inject': 'x' },
+ logger: { info () {} },
+ }),
+ /MQTT In.*refresh_core_node_help/s
+ )
+})
+
+test('a full sync writes a page per node plus the section and category indexes', () => {
+ const root = mkdtempSync(join(tmpdir(), 'core-nodes-'))
+ const help = { 'common/inject': '
i
', 'network/mqtt-in': '
m
' }
+
+ const { count } = syncCoreNodes({
+ repoRoot: root,
+ nuxtRoot: join(root, 'nuxt'),
+ coreNodes: CATALOGUE,
+ help,
+ logger: { info () {} },
+ })
+
+ const dir = join(root, 'nuxt/content/docs/node-red/core-nodes')
+ const entries = readdirSync(dir).sort()
+
+ assert.equal(count, 2)
+ assert.deepEqual(entries, ['README.md', 'common', 'inject.md', 'mqtt-in.md', 'network'])
+ assert.match(readFileSync(join(dir, 'README.md'), 'utf8'), /navGroup|navTitle: "Core nodes"/)
+ assert.match(readFileSync(join(dir, 'common/README.md'), 'utf8'), /\/docs\/node-red\/core-nodes\/inject\//)
+})
diff --git a/nuxt/modules/docs-source.ts b/nuxt/modules/docs-source.ts
index c70e2b82df..b91da456e2 100644
--- a/nuxt/modules/docs-source.ts
+++ b/nuxt/modules/docs-source.ts
@@ -1,5 +1,5 @@
import { defineNuxtModule, useLogger } from '@nuxt/kit'
-import { existsSync, readdirSync } from 'node:fs'
+import { existsSync, readdirSync, readFileSync } from 'node:fs'
import { join, basename, dirname } from 'node:path'
// Lives in nuxt/lib/, not alongside this file: Nuxt auto-registers everything in
@@ -8,6 +8,8 @@ import { join, basename, dirname } from 'node:path'
import { syncDocs } from '../lib/docs-sync.mjs'
// @ts-ignore same
import { syncGuides } from '../lib/guides-sync.mjs'
+// @ts-ignore same
+import { syncCoreNodes } from '../lib/core-nodes-sync.mjs'
const logger = useLogger('docs-source')
@@ -37,6 +39,16 @@ export default defineNuxtModule({
// authored in this repo have to be overlaid after it, not before.
await syncDocs({ repoRoot, nuxtRoot, logger })
syncGuides({ repoRoot, nuxtRoot, logger })
+ // The core-node pages were never files: under Eleventy they were paginated out of
+ // coreNodes.json. They are generated from that catalogue plus the committed help
+ // snapshot, so a deploy never reaches out to raw.githubusercontent.com.
+ syncCoreNodes({
+ repoRoot,
+ nuxtRoot,
+ coreNodes: JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8')),
+ help: JSON.parse(readFileSync(join(nuxtRoot, 'lib', 'core-node-help.json'), 'utf8')),
+ logger,
+ })
if (!existsSync(contentDocsDir)) return
diff --git a/nuxt/redirects-node-red.ts b/nuxt/redirects-node-red.ts
new file mode 100644
index 0000000000..52e3949744
--- /dev/null
+++ b/nuxt/redirects-node-red.ts
@@ -0,0 +1,144 @@
+// The Node-RED library moved from /node-red/** into /docs/**.
+//
+// Written out one path at a time rather than as a `/node-red/**` splat, because
+// `/node-red/` itself did NOT move: it is a marketing page ("What is Node-RED?", with FAQ
+// structured data) and a splat would swallow it along with the documentation below it.
+// An explicit map also means a path that was never published cannot silently 301 into a
+// 404, which is what a path-preserving splat would do for every typo and stale deep link.
+//
+// The core-node entries are derived from src/_data/coreNodes.json, since those pages were
+// paginated out of that catalogue and never existed as files.
+import type { NitroRouteRules } from 'nitropack'
+
+const moved: Record = {
+ '/node-red/core-nodes/': '/docs/node-red/core-nodes/',
+ '/node-red/core-nodes/batch/': '/docs/node-red/core-nodes/batch/',
+ '/node-red/core-nodes/catch/': '/docs/node-red/core-nodes/catch/',
+ '/node-red/core-nodes/change/': '/docs/node-red/core-nodes/change/',
+ '/node-red/core-nodes/comment/': '/docs/node-red/core-nodes/comment/',
+ '/node-red/core-nodes/common/': '/docs/node-red/core-nodes/common/',
+ '/node-red/core-nodes/complete/': '/docs/node-red/core-nodes/complete/',
+ '/node-red/core-nodes/csv/': '/docs/node-red/core-nodes/csv/',
+ '/node-red/core-nodes/debug/': '/docs/node-red/core-nodes/debug/',
+ '/node-red/core-nodes/delay/': '/docs/node-red/core-nodes/delay/',
+ '/node-red/core-nodes/exec/': '/docs/node-red/core-nodes/exec/',
+ '/node-red/core-nodes/filter/': '/docs/node-red/core-nodes/filter/',
+ '/node-red/core-nodes/function/': '/docs/node-red/core-nodes/function/',
+ '/node-red/core-nodes/html/': '/docs/node-red/core-nodes/html/',
+ '/node-red/core-nodes/http-in/': '/docs/node-red/core-nodes/http-in/',
+ '/node-red/core-nodes/http-proxy/': '/docs/node-red/core-nodes/http-proxy/',
+ '/node-red/core-nodes/http-request/': '/docs/node-red/core-nodes/http-request/',
+ '/node-red/core-nodes/inject/': '/docs/node-red/core-nodes/inject/',
+ '/node-red/core-nodes/join/': '/docs/node-red/core-nodes/join/',
+ '/node-red/core-nodes/json/': '/docs/node-red/core-nodes/json/',
+ '/node-red/core-nodes/link/': '/docs/node-red/core-nodes/link/',
+ '/node-red/core-nodes/mqtt-in/': '/docs/node-red/core-nodes/mqtt-in/',
+ '/node-red/core-nodes/mqtt-out/': '/docs/node-red/core-nodes/mqtt-out/',
+ '/node-red/core-nodes/network/': '/docs/node-red/core-nodes/network/',
+ '/node-red/core-nodes/parsers/': '/docs/node-red/core-nodes/parsers/',
+ '/node-red/core-nodes/range/': '/docs/node-red/core-nodes/range/',
+ '/node-red/core-nodes/read-file/': '/docs/node-red/core-nodes/read-file/',
+ '/node-red/core-nodes/sequence/': '/docs/node-red/core-nodes/sequence/',
+ '/node-red/core-nodes/sort/': '/docs/node-red/core-nodes/sort/',
+ '/node-red/core-nodes/split/': '/docs/node-red/core-nodes/split/',
+ '/node-red/core-nodes/status/': '/docs/node-red/core-nodes/status/',
+ '/node-red/core-nodes/storage/': '/docs/node-red/core-nodes/storage/',
+ '/node-red/core-nodes/switch/': '/docs/node-red/core-nodes/switch/',
+ '/node-red/core-nodes/tcp-in/': '/docs/node-red/core-nodes/tcp-in/',
+ '/node-red/core-nodes/template/': '/docs/node-red/core-nodes/template/',
+ '/node-red/core-nodes/tls/': '/docs/node-red/core-nodes/tls/',
+ '/node-red/core-nodes/trigger/': '/docs/node-red/core-nodes/trigger/',
+ '/node-red/core-nodes/udp-in/': '/docs/node-red/core-nodes/udp-in/',
+ '/node-red/core-nodes/udp-out/': '/docs/node-red/core-nodes/udp-out/',
+ '/node-red/core-nodes/unknown/': '/docs/node-red/core-nodes/unknown/',
+ '/node-red/core-nodes/websocket/': '/docs/node-red/core-nodes/websocket/',
+ '/node-red/core-nodes/write-file/': '/docs/node-red/core-nodes/write-file/',
+ '/node-red/core-nodes/xml/': '/docs/node-red/core-nodes/xml/',
+ '/node-red/core-nodes/yaml/': '/docs/node-red/core-nodes/yaml/',
+ '/node-red/database/': '/docs/node-red/database/',
+ '/node-red/database/dynamodb/': '/docs/node-red/database/dynamodb/',
+ '/node-red/database/firebase/': '/docs/node-red/database/firebase/',
+ '/node-red/database/influxdb/': '/docs/node-red/database/influxdb/',
+ '/node-red/database/mongodb/': '/docs/node-red/database/mongodb/',
+ '/node-red/database/mysql/': '/docs/node-red/database/mysql/',
+ '/node-red/database/postgresql/': '/docs/node-red/database/postgresql/',
+ '/node-red/database/redis/': '/docs/node-red/database/redis/',
+ '/node-red/database/sqlite/': '/docs/node-red/database/sqlite/',
+ '/node-red/database/timescaledb/': '/docs/node-red/database/timescaledb/',
+ '/node-red/flowfuse/': '/docs/flowfuse-nodes/',
+ '/node-red/flowfuse/ai/': '/docs/flowfuse-nodes/ai/',
+ '/node-red/flowfuse/ai/depth-estimation/': '/docs/flowfuse-nodes/ai/depth-estimation/',
+ '/node-red/flowfuse/ai/image-classification/': '/docs/flowfuse-nodes/ai/image-classification/',
+ '/node-red/flowfuse/ai/llm-nodes/': '/docs/flowfuse-nodes/ai/llm-nodes/',
+ '/node-red/flowfuse/ai/object-detection/': '/docs/flowfuse-nodes/ai/object-detection/',
+ '/node-red/flowfuse/ai/onxx/': '/docs/flowfuse-nodes/ai/onxx/',
+ '/node-red/flowfuse/edge/': '/docs/flowfuse-nodes/edge/',
+ '/node-red/flowfuse/edge/cip-suite/': '/docs/flowfuse-nodes/edge/cip-suite/',
+ '/node-red/flowfuse/edge/modbus/': '/docs/flowfuse-nodes/edge/modbus/',
+ '/node-red/flowfuse/edge/opcua/': '/docs/flowfuse-nodes/edge/opcua/',
+ '/node-red/flowfuse/edge/rtsp/': '/docs/flowfuse-nodes/edge/rtsp/',
+ '/node-red/flowfuse/flowfuse-tables/': '/docs/flowfuse-nodes/flowfuse-tables/',
+ '/node-red/flowfuse/flowfuse-tables/query/': '/docs/flowfuse-nodes/flowfuse-tables/query/',
+ '/node-red/flowfuse/hub/': '/docs/flowfuse-nodes/hub/',
+ '/node-red/flowfuse/hub/redis/': '/docs/flowfuse-nodes/hub/redis/',
+ '/node-red/flowfuse/mcp/': '/docs/flowfuse-nodes/mcp/',
+ '/node-red/flowfuse/mcp/mcp-prompt/': '/docs/flowfuse-nodes/mcp/mcp-prompt/',
+ '/node-red/flowfuse/mcp/mcp-resource/': '/docs/flowfuse-nodes/mcp/mcp-resource/',
+ '/node-red/flowfuse/mcp/mcp-response/': '/docs/flowfuse-nodes/mcp/mcp-response/',
+ '/node-red/flowfuse/mcp/mcp-tool/': '/docs/flowfuse-nodes/mcp/mcp-tool/',
+ '/node-red/flowfuse/mqtt/': '/docs/flowfuse-nodes/mqtt/',
+ '/node-red/flowfuse/mqtt/mqtt-in/': '/docs/flowfuse-nodes/mqtt/mqtt-in/',
+ '/node-red/flowfuse/mqtt/mqtt-out/': '/docs/flowfuse-nodes/mqtt/mqtt-out/',
+ '/node-red/getting-started/': '/docs/node-red/getting-started/',
+ '/node-red/getting-started/date-and-time/': '/docs/node-red/getting-started/date-and-time/',
+ '/node-red/getting-started/editor/': '/docs/node-red/getting-started/editor/',
+ '/node-red/getting-started/editor/header/': '/docs/node-red/getting-started/editor/header/',
+ '/node-red/getting-started/editor/palette/': '/docs/node-red/getting-started/editor/palette/',
+ '/node-red/getting-started/editor/sidebar/': '/docs/node-red/getting-started/editor/sidebar/',
+ '/node-red/getting-started/editor/workspace/': '/docs/node-red/getting-started/editor/workspace/',
+ '/node-red/getting-started/library/': '/docs/node-red/getting-started/library/',
+ '/node-red/getting-started/node-red-android/': '/docs/node-red/getting-started/node-red-android/',
+ '/node-red/getting-started/node-red-messages/': '/docs/node-red/getting-started/node-red-messages/',
+ '/node-red/getting-started/node-red-port/': '/docs/node-red/getting-started/node-red-port/',
+ '/node-red/getting-started/programming/': '/docs/node-red/getting-started/programming/',
+ '/node-red/getting-started/programming/data-tranformation/': '/docs/node-red/getting-started/programming/data-tranformation/',
+ '/node-red/getting-started/programming/debugging-flows/': '/docs/node-red/getting-started/programming/debugging-flows/',
+ '/node-red/getting-started/programming/if-else/': '/docs/node-red/getting-started/programming/if-else/',
+ '/node-red/getting-started/programming/loop/': '/docs/node-red/getting-started/programming/loop/',
+ '/node-red/getting-started/string/': '/docs/node-red/getting-started/string/',
+ '/node-red/getting-started/update-node-red/': '/docs/node-red/getting-started/update-node-red/',
+ '/node-red/hardware/': '/docs/node-red/hardware/',
+ '/node-red/hardware/armxy-bl340/': '/docs/node-red/hardware/armxy-bl340/',
+ '/node-red/hardware/opto-22-groove-rio-7-mm2001-10/': '/docs/node-red/hardware/opto-22-groove-rio-7-mm2001-10/',
+ '/node-red/hardware/raspberry-pi-4/': '/docs/node-red/hardware/raspberry-pi-4/',
+ '/node-red/hardware/raspberry-pi-5/': '/docs/node-red/hardware/raspberry-pi-5/',
+ '/node-red/hardware/robustel-eg5120/': '/docs/node-red/hardware/robustel-eg5120/',
+ '/node-red/hardware/siemens-iot-2050/': '/docs/node-red/hardware/siemens-iot-2050/',
+ '/node-red/integration-technologies/': '/docs/node-red/integration-technologies/',
+ '/node-red/integration-technologies/graphql/': '/docs/node-red/integration-technologies/graphql/',
+ '/node-red/integration-technologies/rest/': '/docs/node-red/integration-technologies/rest/',
+ '/node-red/integration-technologies/webhook/': '/docs/node-red/integration-technologies/webhook/',
+ '/node-red/keyboard/': '/docs/node-red/keyboard/',
+ '/node-red/learn/': '/docs/node-red/',
+ '/node-red/notification/': '/docs/node-red/notification/',
+ '/node-red/notification/discord/': '/docs/node-red/notification/discord/',
+ '/node-red/notification/email/': '/docs/node-red/notification/email/',
+ '/node-red/notification/telegram/': '/docs/node-red/notification/telegram/',
+ '/node-red/peripheral/': '/docs/node-red/peripheral/',
+ '/node-red/peripheral/ardiuno/': '/docs/node-red/peripheral/ardiuno/',
+ '/node-red/peripheral/barcodescanner/': '/docs/node-red/peripheral/barcodescanner/',
+ '/node-red/peripheral/esp32/': '/docs/node-red/peripheral/esp32/',
+ '/node-red/peripheral/webcam/': '/docs/node-red/peripheral/webcam/',
+ '/node-red/protocol/': '/docs/node-red/protocol/',
+ '/node-red/protocol/amqp/': '/docs/node-red/protocol/amqp/',
+ '/node-red/protocol/lwm2m/': '/docs/node-red/protocol/lwm2m/',
+ '/node-red/protocol/modbus/': '/docs/node-red/protocol/modbus/',
+ '/node-red/protocol/mqtt/': '/docs/node-red/protocol/mqtt/',
+ '/node-red/protocol/opc-ua/': '/docs/node-red/protocol/opc-ua/',
+ '/node-red/protocol/websocket/': '/docs/node-red/protocol/websocket/',
+ '/node-red/terminology/': '/docs/node-red/terminology/',
+}
+
+export const nodeRedRedirects: Record = Object.fromEntries(
+ Object.entries(moved).map(([from, to]) => [from, { redirect: { to, statusCode: 301 } }])
+)
diff --git a/nuxt/redirects.ts b/nuxt/redirects.ts
index 4313d6c671..928764e01c 100644
--- a/nuxt/redirects.ts
+++ b/nuxt/redirects.ts
@@ -8,7 +8,10 @@
// domain redirects already there for the pattern.
import type { NitroRouteRules } from 'nitropack'
+import { nodeRedRedirects } from './redirects-node-red'
+
export const redirects: Record = {
+ ...nodeRedRedirects,
'/handbook/product/': { redirect: { to: '/handbook/engineering/product/', statusCode: 301 } },
'/handbook/product/blueprints/': { redirect: { to: '/handbook/engineering/blueprints/', statusCode: 301 } },
'/handbook/engineering/product/blueprints/': { redirect: { to: '/handbook/engineering/blueprints/', statusCode: 301 } },
diff --git a/nuxt/server/middleware/legacy.ts b/nuxt/server/middleware/legacy.ts
index 483ce77e39..6c04f45035 100644
--- a/nuxt/server/middleware/legacy.ts
+++ b/nuxt/server/middleware/legacy.ts
@@ -50,6 +50,14 @@ export default defineEventHandler(async (event) => {
// from still-11ty pages like use-cases/uns.njk) - /resources is otherwise a Nuxt prefix.
if (normalised.startsWith('/resources/images/')) return proxyRequest(event, `http://localhost:8080${path}`)
+ // The documentation below /node-red/ moved into /docs/, and every old URL now 301s
+ // from nuxt/redirects-node-red.ts. Those are Nitro route rules, so the request has to
+ // reach Nitro in dev rather than being proxied to 11ty, which no longer has the pages.
+ // /node-red itself is deliberately excluded and keeps falling through to 11ty: it is
+ // still a marketing page there, which is why the redirect map is explicit paths rather
+ // than a splat.
+ if (normalised !== '/node-red' && normalised.startsWith('/node-red/')) return
+
// Let Nuxt handle migrated path prefixes
if (NUXT_PREFIXES.some(prefix => normalised === prefix || normalised.startsWith(prefix + '/'))) return
diff --git a/scripts/refresh_core_node_help.mjs b/scripts/refresh_core_node_help.mjs
new file mode 100644
index 0000000000..400234cf99
--- /dev/null
+++ b/scripts/refresh_core_node_help.mjs
@@ -0,0 +1,28 @@
+#!/usr/bin/env node
+// Re-fetches every core node's built-in help from the Node-RED project and rewrites the
+// committed snapshot at nuxt/lib/core-node-help.json.
+//
+// Run this deliberately, when upstream's help has changed. It is NOT part of the build:
+// the /docs/node-red/core-nodes/** pages are generated from the snapshot so a deploy
+// never depends on raw.githubusercontent.com being reachable.
+//
+// node scripts/refresh_core_node_help.mjs
+//
+// Then commit the diff to nuxt/lib/core-node-help.json alongside whatever prompted it.
+
+import { readFileSync, writeFileSync } from 'node:fs'
+import { dirname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+import { refreshCoreNodeHelp } from '../nuxt/lib/core-nodes-sync.mjs'
+
+const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
+const coreNodes = JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8'))
+const dest = join(repoRoot, 'nuxt/lib/core-node-help.json')
+
+const help = await refreshCoreNodeHelp({ coreNodes })
+const keys = Object.keys(help).sort()
+const sorted = Object.fromEntries(keys.map(k => [k, help[k]]))
+
+writeFileSync(dest, `${JSON.stringify(sorted, null, 2)}\n`, 'utf8')
+console.log(`Wrote help for ${keys.length} nodes to nuxt/lib/core-node-help.json`)
diff --git a/scripts/sync_docs.mjs b/scripts/sync_docs.mjs
index 6bc44c7ca4..9223a0fbd6 100644
--- a/scripts/sync_docs.mjs
+++ b/scripts/sync_docs.mjs
@@ -2,11 +2,13 @@
// Populates nuxt/content/docs outside of a Nuxt build, so CI can resolve the docs before
// installing dependencies. Uses only node builtins: this runs before `npm install`.
+import { readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { syncDocs } from '../nuxt/lib/docs-sync.mjs'
import { syncGuides } from '../nuxt/lib/guides-sync.mjs'
+import { syncCoreNodes } from '../nuxt/lib/core-nodes-sync.mjs'
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
@@ -15,3 +17,9 @@ const nuxtRoot = join(repoRoot, 'nuxt')
await syncDocs({ repoRoot, nuxtRoot })
// After syncDocs, which wipes the tree it writes into.
syncGuides({ repoRoot, nuxtRoot })
+syncCoreNodes({
+ repoRoot,
+ nuxtRoot,
+ coreNodes: JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8')),
+ help: JSON.parse(readFileSync(join(nuxtRoot, 'lib', 'core-node-help.json'), 'utf8')),
+})
diff --git a/src/_data/chrome.json b/src/_data/chrome.json
index 237c0b8531..404a8dad41 100644
--- a/src/_data/chrome.json
+++ b/src/_data/chrome.json
@@ -210,46 +210,11 @@
"titleGrid": "md:col-start-3 md:row-start-1 xl:col-start-4",
"listClasses": "row-span-[9] md:col-start-3 md:row-start-2 xl:col-start-4",
"links": [
- {
- "label": "Redis",
- "href": "/node-red/flowfuse/hub/redis/",
- "icon": "circle-stack"
- },
{
"label": "OPC UA",
"href": "/integrations/opcua/",
"icon": "server-stack"
},
- {
- "label": "RTSP",
- "href": "/node-red/flowfuse/edge/rtsp/",
- "icon": "camera"
- },
- {
- "label": "EtherNet/IP",
- "href": "/node-red/flowfuse/edge/cip-suite/",
- "icon": "share"
- },
- {
- "label": "MQTT",
- "href": "/node-red/flowfuse/mqtt/",
- "icon": "wifi"
- },
- {
- "label": "ONNX",
- "href": "/node-red/flowfuse/ai/onxx/",
- "icon": "chip"
- },
- {
- "label": "LLM Nodes",
- "href": "/node-red/flowfuse/ai/llm-nodes/",
- "icon": "sparkles"
- },
- {
- "label": "MCP",
- "href": "/node-red/flowfuse/mcp/",
- "icon": "puzzle-piece"
- },
{
"label": "See all integrations",
"href": "/integrations/",
@@ -605,38 +570,10 @@
{
"title": "By Integration",
"links": [
- {
- "label": "Redis",
- "href": "/node-red/flowfuse/hub/redis/"
- },
{
"label": "OPC UA",
"href": "/integrations/opcua/"
},
- {
- "label": "RTSP",
- "href": "/node-red/flowfuse/edge/rtsp/"
- },
- {
- "label": "EtherNet/IP",
- "href": "/node-red/flowfuse/edge/cip-suite/"
- },
- {
- "label": "MQTT",
- "href": "/node-red/flowfuse/mqtt/"
- },
- {
- "label": "ONNX",
- "href": "/node-red/flowfuse/ai/onxx/"
- },
- {
- "label": "LLM Nodes",
- "href": "/node-red/flowfuse/ai/llm-nodes/"
- },
- {
- "label": "MCP",
- "href": "/node-red/flowfuse/mcp/"
- },
{
"label": "See all integrations",
"href": "/integrations/"
diff --git a/src/_data/coreNodes.json b/src/_data/coreNodes.json
index bf0b7e5139..fbabe4b424 100644
--- a/src/_data/coreNodes.json
+++ b/src/_data/coreNodes.json
@@ -102,16 +102,15 @@
"file": "06-httpproxy"
},
{
- "xpath": "mqtt-in",
+ "xpath": "mqtt in",
"name": "MQTT In",
"file": "10-mqtt"
},
{
- "xpath": "mqtt-out",
+ "xpath": "mqtt out",
"name": "MQTT Out",
"file": "10-mqtt"
- }
- ,
+ },
{
"xpath": "http in",
"name": "HTTP in",
@@ -133,12 +132,12 @@
"file": "31-tcpin"
},
{
- "xpath": "upd",
+ "xpath": "udp in",
"name": "UDP In",
"file": "32-udp"
},
{
- "xpath": "upd",
+ "xpath": "udp out",
"name": "UDP Out",
"file": "32-udp"
}
@@ -204,4 +203,4 @@
"file": "23-watch"
}
]
-}
\ No newline at end of file
+}
diff --git a/src/_includes/components/docs-banner.njk b/src/_includes/components/docs-banner.njk
deleted file mode 100644
index 5f8ed3c9d2..0000000000
--- a/src/_includes/components/docs-banner.njk
+++ /dev/null
@@ -1,43 +0,0 @@
-{% set hasExpireDate = false %}
-{% set hasContent = false %}
-
-{% for ad in docs_banner %}
- {% if ad.expire %}
- {% if ad.expire | isFutureDate %}
- {% set hasExpireDate = true %}
- {% endif %}
- {% else %}
- {% set hasContent = true %}
- {% endif %}
-{% endfor %}
-
-{% if hasExpireDate or hasContent %}
-
In Node-RED, the core nodes are the set of nodes that are included with the
-Node-RED runtime by default without the node install procedure. These nodes are
-maintained and supported by the Node-RED development team and are intended to
-provide the basic building blocks for creating Node-RED flows.
-
-
Core nodes include nodes for basic functionality like input/output, processing,
-and control flow. They are the foundation upon which more complex workflows can
-be built, and they are essential to the operation of Node-RED.
-
-
The nodes are grouped in a few sections in the nodes sidebar:
-
-
common - miscellaneous nodes
-
function - Control flow, blocking or sending additional messages.
-
network - Receive and send messages over the network on different protocols.
-
sequence - Working with collections of messages, or creating them.
-
parser - transforming data from one format to another.
-
storage - for reading or writing files.
-
diff --git a/src/node-red/core-nodes/nav.njk b/src/node-red/core-nodes/nav.njk
deleted file mode 100644
index b0f9f74bfb..0000000000
--- a/src/node-red/core-nodes/nav.njk
+++ /dev/null
@@ -1,15 +0,0 @@
----
-pagination:
- data: coreNodesArray
- size: 1
- alias: category
- addAllPagesToCollections: true
-permalink: "node-red/core-nodes/{{ category.key | lower | replace(' ', '-') }}-nav/"
-eleventyComputed:
- eleventyNavigation:
- key: "{{ category.key }}"
- title: "{{ category.key | capitalize }}"
- parent: "Core Nodes"
- url: "/node-red/core-nodes/{{ category.nodes[0].name | lower | replace(' ', '-') }}/"
- permalink: false
----
\ No newline at end of file
diff --git a/src/node-red/core-nodes/network.njk b/src/node-red/core-nodes/network.njk
deleted file mode 100644
index f1aa4d1962..0000000000
--- a/src/node-red/core-nodes/network.njk
+++ /dev/null
@@ -1,18 +0,0 @@
----
-pagination:
- data: coreNodes.network
- size: 1
- alias: node
- addAllPagesToCollections: true
-coreNodeCategory: "network"
-permalink: "node-red/core-nodes/{{ node.name | lower | replace(' ', '-') }}/"
-eleventyComputed:
- eleventyNavigation:
- key: "{{ node.name }}"
- parent: "{{ coreNodeCategory }}"
- title: "Node-RED - {{ node.name }} Node"
- description: "{{ node.description }}"
- keywords: "{{ node.keywords }}"
----
-
-{% include "core-node-docs.njk" %}
diff --git a/src/node-red/core-nodes/parsers.njk b/src/node-red/core-nodes/parsers.njk
deleted file mode 100644
index a3d95fbada..0000000000
--- a/src/node-red/core-nodes/parsers.njk
+++ /dev/null
@@ -1,18 +0,0 @@
----
-pagination:
- data: coreNodes.parsers
- size: 1
- alias: node
- addAllPagesToCollections: true
-coreNodeCategory: "parsers"
-permalink: "node-red/core-nodes/{{ node.name | lower | replace(' ', '-') }}/"
-eleventyComputed:
- eleventyNavigation:
- key: "{{ node.name }}"
- parent: "{{ coreNodeCategory }}"
- title: "Node-RED - {{ node.name }} Node"
- description: "{{ node.description }}"
- keywords: "{{ node.keywords }}"
----
-
-{% include "core-node-docs.njk" %}
diff --git a/src/node-red/core-nodes/sequence.njk b/src/node-red/core-nodes/sequence.njk
deleted file mode 100644
index a53a0353ae..0000000000
--- a/src/node-red/core-nodes/sequence.njk
+++ /dev/null
@@ -1,18 +0,0 @@
----
-pagination:
- data: coreNodes.sequence
- size: 1
- alias: node
- addAllPagesToCollections: true
-coreNodeCategory: "sequence"
-permalink: "node-red/core-nodes/{{ node.name | lower | replace(' ', '-') }}/"
-eleventyComputed:
- eleventyNavigation:
- key: "{{ node.name }}"
- parent: "{{ coreNodeCategory }}"
- title: "Node-RED - {{ node.name }} Node"
- description: "{{ node.description }}"
- keywords: "{{ node.keywords }}"
----
-
-{% include "core-node-docs.njk" %}
diff --git a/src/node-red/core-nodes/storage.njk b/src/node-red/core-nodes/storage.njk
deleted file mode 100644
index ce6fc25b14..0000000000
--- a/src/node-red/core-nodes/storage.njk
+++ /dev/null
@@ -1,18 +0,0 @@
----
-pagination:
- data: coreNodes.storage
- size: 1
- alias: node
- addAllPagesToCollections: true
-coreNodeCategory: "storage"
-permalink: "node-red/core-nodes/{{ node.name | lower | replace(' ', '-') }}/"
-eleventyComputed:
- eleventyNavigation:
- key: "{{ node.name }}"
- parent: "{{ coreNodeCategory }}"
- title: "Node-RED - {{ node.name }} Node"
- description: "{{ node.description }}"
- keywords: "{{ node.keywords }}"
----
-
-{% include "core-node-docs.njk" %}
diff --git a/src/node-red/database/index.md b/src/node-red/database/index.md
deleted file mode 100644
index 979ff96745..0000000000
--- a/src/node-red/database/index.md
+++ /dev/null
@@ -1,27 +0,0 @@
----
-eleventyNavigation:
- key: Database
- order: 7
-meta:
- title: Node-RED Database Integration Guides
- description: Explore database integration guides for Node-RED, including PostgreSQL, MongoDB, InfluxDB, DynamoDB, and TimescaleDB
- keywords: node-red, databases, integration, PostgreSQL, MongoDB, InfluxDB, DynamoDB, TimescaleDB
-certified:
- - "@flowfuse-certified-nodes/redis"
-certifiedTitle: "A certified node for these databases"
-certifiedIntro: >
- The guides in this section use community packages. One database here is also
- covered by a FlowFuse certified node, maintained for production use:
----
-
-# {{ meta.title }}
-
-Node-RED is highly versatile and can be set up to work with a variety of databases, whether it is SQL (e.g., PostgreSQL, MySQL), NoSQL (e.g., MongoDB), or time-series databases (e.g., InfluxDB). This flexibility allows you to store and manage IoT data effectively, enabling the creation of interactive and data-driven applications for IoT environments.
-
-## Resources
-
-Here are some resources to help you get started with Node-RED on diffrent types of databases. Each guide provides step-by-step instructions to help you get started, along with advanced techniques for optimizing performance and handling complex data operations.:
-
-{% include "navigation-items-list.njk" %}
-
-{% include "components/certified-node-callout.liquid" %}
diff --git a/src/node-red/flowfuse/ai/index.md b/src/node-red/flowfuse/ai/index.md
deleted file mode 100644
index 6aaefb68d1..0000000000
--- a/src/node-red/flowfuse/ai/index.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-eleventyNavigation:
- key: AI
- parent: FlowFuse Nodes
- order: 1
-meta:
- title: FlowFuse AI Nodes
- description: A set of Node-RED nodes for AI and machine learning, including ONNX model inference and LLM nodes for OpenAI, Anthropic, Google Gemini, and Ollama.
----
-
-# {{ meta.title }}
-
-The **FlowFuse AI** Nodes package adds AI capabilities to Node-RED. It includes nodes for running local ONNX models for image classification, object detection, depth estimation, and custom inference, as well as LLM nodes for sending text prompts to hosted and local large language models from OpenAI, Anthropic, Google Gemini, and Ollama.
-
-## Nodes
-
-{% include "navigation-items-list.njk" %}
-
diff --git a/src/node-red/flowfuse/edge/index.md b/src/node-red/flowfuse/edge/index.md
deleted file mode 100644
index 53788dd161..0000000000
--- a/src/node-red/flowfuse/edge/index.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-eleventyNavigation:
- key: Edge
- parent: FlowFuse Nodes
- order: 4
- title: Edge
-meta:
- title: Edge Certified Nodes
- description: Documentation for FlowFuse Edge Certified Nodes, including nodes for connecting FlowFuse to industrial protocols, PLCs, SCADA systems, and factory-floor equipment.
----
-
-# {{ meta.title }}
-
-This section contains documentation for **FlowFuse Edge Certified Nodes** that connect your FlowFuse instances to industrial protocols, PLCs, SCADA systems, and factory-floor equipment.
-
-FlowFuse Certified Nodes are packages that FlowFuse has vetted for quality, security, and support, and maintains on an ongoing basis. To learn more about what certification means and how these nodes are delivered, [read the FlowFuse Certified Nodes blog post](/blog/2025/07/certified-nodes-v2/).
-
-{% note %}
-The FlowFuse Edge Certified Nodes catalogue is part of the **FlowFuse Edge** offering. [Contact us](/contact-us/) to get access or to learn more.
-{% endnote %}
-
-{% note %}
-This section is expanding. We are actively working to bring more Edge Certified Nodes to FlowFuse, and additional documentation will be added here over time.
-{% endnote %}
-
-## Nodes
-
-This section lists the **Edge Certified Nodes** documented in FlowFuse:
-
-{% include "navigation-items-list.njk" %}
-
-{% note %}
-Newly installed nodes are picked up automatically, no restart needed. Restart is only required when you update a node that's already installed: restart any remote instance or hosted instance running the previous version.
-{% endnote %}
diff --git a/src/node-red/flowfuse/index.md b/src/node-red/flowfuse/index.md
deleted file mode 100644
index f615d3bc13..0000000000
--- a/src/node-red/flowfuse/index.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-eleventyNavigation:
- key: FlowFuse Nodes
- order: 5
-meta:
- title: FlowFuse Nodes
- description: An overview of the Node-RED nodes provided by FlowFuse, explaining their purpose, usage, and benefits for building integrations and automations.
----
-
-# {{ meta.title }}
-
-In Node-RED on the FlowFuse platform, you have access to additional Node-RED nodes that are provided by FlowFuse. These are documented in the below sections as a reference you can use when building integrations and automations.
-
-## Nodes
-
-The following documents provide details about the FlowFuse nodes:
-
-{% include "navigation-items-list.njk" %}
diff --git a/src/node-red/getting-started/index.md b/src/node-red/getting-started/index.md
deleted file mode 100644
index c43ecd44b8..0000000000
--- a/src/node-red/getting-started/index.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-eleventyNavigation:
- key: Getting Started
- order: 1
-meta:
- title: Getting Started with Node-RED
- description: Learn the basics of Node-RED, a powerful tool for IoT integration and workflow automation.
- keywords: node-red, node-red basics, node-red foundation, node-red editor
----
-
-# {{ meta.title }}
-
-This section provides an overview of Node-RED, a robust visual tool designed for integrating IoT devices and automating workflows without needing extensive programming knowledge.
-
-{% include "navigation-items-list.njk" %}
\ No newline at end of file
diff --git a/src/node-red/getting-started/programming/index.md b/src/node-red/getting-started/programming/index.md
deleted file mode 100644
index cd2cd2be1a..0000000000
--- a/src/node-red/getting-started/programming/index.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-eleventyNavigation:
- key: Programming
- order: 7
- parent: Getting Started
-meta:
- title: Node-RED Programming
- description: Master Node-RED programming fundamentals including flows, nodes, messages, conditional logic, and data manipulation. Learn essential concepts for building sophisticated visual programming solutions.
- keywords: Node-RED programming, visual programming, Node-RED flows, Node-RED nodes, message passing, conditional logic, data manipulation, flow-based programming, Node-RED tutorial, IoT programming
----
-
-# {{ meta.title }}
-
-Learn the core programming concepts you'll use every day in Node-RED. This section covers the fundamental building blocks that will help you create more sophisticated and reliable flows.
-
-## What's Covered
-
-Programming in Node-RED means working with flows, nodes, and messages. Even though you're working visually, you'll still need to understand key programming concepts like conditional logic, loops, and data manipulation. This section teaches you how to implement these concepts using Node-RED's visual approach.
-
-{% include "navigation-items-list.njk" %}
diff --git a/src/node-red/hardware/armxy-bl340.md b/src/node-red/hardware/armxy-bl340.md
deleted file mode 100644
index 540c7b3739..0000000000
--- a/src/node-red/hardware/armxy-bl340.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-eleventyNavigation:
- key: BLIIOT ARMxy BL340
- parent: Hardware
-meta:
- title: Setting Node-RED on BLIIOT ARMxy BL340
- description: Guide to setting up Node-RED on BLIIOT ARMxy BL340, including installation and configuration steps.
- keywords: node-red, flowfuse, BLIIOT ARMxy BL340
-image: "/node-red/hardware/images/armxy-bl340.jpg"
-specifications:
- Model: BLIIoT ARMxy BL340 Series
- RAM_MB: 2048
- Processor: ARM Cortex-A53
- GPU: G31 MP2
- IO Points: Optional (GPIO, RS485,CAN,RS232,DI/DO/AI/AO etc.,)
- Connectivity: Dual-band Wi-Fi 6, Bluetooth 5.2, Gigabit Ethernet, 4x USB 3.0, 1x USB-C
- Clock Speed: Up to 1.4 GHz
- Storage: SD, SDHC and SDXC(UHS-I) card
- Display Output: HDMI 2.0, DisplayPort
- Power Supply: 9~36V DC
-layout: layouts/hardware.njk
----
-
-The BLIIOT ARMxy BL340 is a high-performance single-board computer designed for demanding applications, including edge computing, automation, and embedded systems. Featuring an octa-core ARM processor, advanced connectivity options, and support for high-speed storage, it provides a powerful platform for developers and engineers.
-
-Integrating this powerful hardware with FlowFuse not only enhances its capabilities but also simplifies the management and deployment process.
-
-{% note %}
-Exciting Update! FlowFuse is now available as three different products: [FlowFuse Edge for OT teams](/product/edge/), [FlowFuse Hub for IT teams](/product/hub/), and [FlowFuse Fleet](/product/fleet/) for managing devices at scale. Visit the [FlowFuse Product Page](/product/) to learn how the platform enables you to build and manage industrial apps at scale.
-{% endnote %}
-
-## Prerequisites
-
-Before proceeding with the installation, ensure you have the following:
-
-- **BLIIOT ARMxy BL340** – A functioning device with internet access.
-- **FlowFuse Account** - Ensure you have a FlowFuse account. If not, you can create a free account that allows you to manage up to two edge devices for free. For more information, refer to [FlowFuse Free Tier](/blog/2024/12/flowfuse-release-2-12/)
-- **Sudo Privileges** – Administrator access to install required packages.
-
-## Getting Started
-
-This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the BLIIOT ARMxy BL340, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
-
-{% include "hardware/system/debian-ff-install.md" %}
-
-{% include "hardware/device-registration.md" %}
-
-{data-zoomable}
-
-Now, when your device reboots, the FlowFuse Device Agent will automatically start, ensuring that your BLIIOT ARMxy BL340 remains connected to the FlowFuse platform.
-
-{% include "hardware/accessing-node-red-editor.md" %}
diff --git a/src/node-red/hardware/index.md b/src/node-red/hardware/index.md
deleted file mode 100644
index e0f9335c52..0000000000
--- a/src/node-red/hardware/index.md
+++ /dev/null
@@ -1,21 +0,0 @@
----
-eleventyNavigation:
- key: Hardware
- order: 6
-meta:
- title: Setting Up Node-RED on Different Hardware
- description: Learn how to set up Node-RED on various hardware platforms, such as Raspberry Pi, Arduino, and more.
- keywords: node-red, node-red hardware setup, raspberry pi, arduino, siemens iot2050,
----
-
-# {{ meta.title }}
-
-Node-RED is highly versatile and can be set up on a wide range of hardware devices, including popular choices like Raspberry Pi, Ardiuno, Siemens 2050, and more. This flexibility allows you to connect Node-RED to diverse devices and sensors, which enables the creation of interactive and automated systems.
-
-By setting up Node-RED on different hardware, you can easily integrate physical inputs with digital systems. This capability is essential for IoT (Internet of Things) applications, where data from sensors and devices can trigger actions or responses in real-time.
-
-## Resources
-
-Here are some resources to help you get started with Node-RED on diffrent hardware devices:
-
-{% include "navigation-items-list.njk" %}
diff --git a/src/node-red/hardware/raspberry-pi-4.md b/src/node-red/hardware/raspberry-pi-4.md
deleted file mode 100644
index ddebd20bfc..0000000000
--- a/src/node-red/hardware/raspberry-pi-4.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-eleventyNavigation:
- key: Raspberry Pi 4
- parent: Hardware
-meta:
- title: Setting Node-RED on Raspberry Pi 4
- description: Learn how to install the FlowFuse Edge Agent on the Raspberry Pi 4 effortlessly. Manage your device with Node-RED through FlowFuse with ease.
- keywords: node-red, flowfuse, raspberry pi, raspberry pi 4
-image: "/node-red/hardware/images/raspberry-pi-4-b.png"
-specifications:
- Model: Raspberry Pi 4 B 8GB
- RAM_MB: 8192
- Processor: Broadcom BCM2711, ARM Cortex-A72 (ARMv8-A), 4 (Quad-core)
- GPIO: (Fully backwar ds-compatible with previous boards), Standard 40-pin GPIO Header
- Connectivity: Dual-band Wi-Fi, Bluetooth 5.0, Gigabit Ethernet, 2x USB 3.0, 2x USB 2.0
- Clock Speed: 1.5 GHz
- Storage: microSD
-layout: layouts/hardware.njk
----
-
-## Raspberry Pi OS Installation
-
-To set up your Raspberry Pi 4 for use with Node-RED and FlowFuse, follow these steps:
-
-### Flashing Raspberry Pi OS
-
-1. Use the [official Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the 64-bit version of Raspberry Pi OS to an SD card.
-
-
-
-2. Before writing to the SD card, configure the OS for headless mode, including Wi-Fi, SSH, and authentication settings.
-
-
-
-3. Write the OS and configuration to the SD card. This process takes about 10 minutes.
-
-4. Insert the SD card into the Raspberry Pi 4 and power it on. The device should appear on your network after a minute or so.
-
-5. Connect to the Raspberry Pi using SSH:
-
- ```sh
- ssh pi@raspberrypi.local
- ```
-
-## Getting Started
-
-This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the Raspberry Pi 4, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
-
-{% include "hardware/system/debian-ff-install.md" %}
-
-{% include "hardware/device-registration.md" %}
-
-{data-zoomable}
-
-{% include "hardware/accessing-node-red-editor.md" %}
\ No newline at end of file
diff --git a/src/node-red/hardware/raspberry-pi-5.md b/src/node-red/hardware/raspberry-pi-5.md
deleted file mode 100644
index 0a532a4a0e..0000000000
--- a/src/node-red/hardware/raspberry-pi-5.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-eleventyNavigation:
- key: Raspberry Pi 5
- parent: Hardware
-meta:
- title: Setting Up Node-RED on Raspberry Pi 5
- description: Learn how to install the FlowFuse Edge Agent on the Raspberry Pi 5 effortlessly. Manage your device with Node-RED through FlowFuse with ease.
- keywords: node-red, flowfuse, raspberry pi, raspberry pi 5
-image: "/node-red/hardware/images/raspberrypi-5.png"
-specifications:
- Model: Raspberry Pi 5 Model 8GB
- RAM_MB: 8192
- Processor: Broadcom BCM2712, ARM Cortex-A76 (ARMv8.2-A), 4 (Quad-core)
- GPIO: Standard 40-pin GPIO Header
- Connectivity: 2 × USB 2.0 Ports, 2 × USB 3.0 Ports, Bluetooth 5.0, USB-C, Wi-Fi + Bluetooth® Low Energy
- Clock Speed: 2.4 GHz
- Storage: microSD
-layout: layouts/hardware.njk
----
-
-## Raspberry Pi OS Installation
-
-To set up your Raspberry Pi 5 for use with Node-RED and FlowFuse, follow these steps:
-
-### Flashing Raspberry Pi OS
-
-1. Use the [official Raspberry Pi Imager](https://www.raspberrypi.com/software/) to flash the 64-bit version of Raspberry Pi OS to an SD card.
-
-
-
-2. Before writing to the SD card, configure the OS for headless mode, including Wi-Fi, SSH, and authentication settings.
-
-
-
-3. Write the OS and configuration to the SD card. This process takes about 10 minutes.
-
-4. Insert the SD card into the Raspberry Pi 5 and power it on. The device should appear on your network after a minute or so.
-
-5. Connect to the Raspberry Pi using SSH:
-
- ```sh
- ssh pi@raspberrypi.local
- ```
-
-## Getting Started
-
-This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the Raspberry Pi 5, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
-
-{% include "hardware/system/debian-ff-install.md" %}
-
-{% include "hardware/device-registration.md" %}
-
-{data-zoomable}
-
-{% include "hardware/accessing-node-red-editor.md" %}
\ No newline at end of file
diff --git a/src/node-red/hardware/robustel-eg5120.md b/src/node-red/hardware/robustel-eg5120.md
deleted file mode 100644
index 64bbb7a35c..0000000000
--- a/src/node-red/hardware/robustel-eg5120.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-eleventyNavigation:
- key: Robustel EG5120
- parent: Hardware
-meta:
- title: Setting Node-RED on Robustel EG5120
- description: In this guide, we will discuss how to install FlowFuse Device agent on Robustel EG5120.
- keywords: node-red, flowfuse, robustel eg5120
-image: "/node-red/hardware/images/robustel-eg5120.png"
-specifications:
- Model: Robustel EG5120
- RAM_MB: 2048
- Processor: Broadcom BCM2711, ARM Cortex-A72 (ARMv8-A), 4 (Quad-core)
- GPIO: Standard 40-pin GPIO Header
- Connectivity: Dual-band Wi-Fi, Bluetooth 5.0, Gigabit Ethernet, 2x USB 3.0, 2x USB 2.0
- Clock Speed: 1.5 GHz
- Storage: microSD
-layout: layouts/hardware.njk
----
-
-The [Robustel EG5120](https://www.robustel.com/product/eg5120-industrial-edge-computing-gateway/) is a versatile gateway that facilitates robust connectivity for industrial IoT applications. Integrating this powerful hardware with FlowFuse not only enhances its capabilities but also simplifies the management and deployment process. In this documentation, we’ll walk through the steps to integrate the Robustel EG5120 with FlowFuse.
-
-The [Robustel EG5120](https://www.robustel.com/product/eg5120-industrial-edge-computing-gateway/), equipped with Linux-based Debian 11 supporting a wide variety of programming languages including Node.js, offers robust connectivity options. When combined with FlowFuse, this gateway becomes even more powerful, enabling seamless device management and deployment.
-
-The Robustel EG5120 supports multiple connectivity options including Ethernet, Wi-Fi, and cellular networks, which are essential for flexible deployments in various industrial scenarios. Its built-in support for Bluetooth, cellular connectivity, RS232, RS485, and Modbus facilitates seamless integration with a wide array of IoT devices and services. This blog will guide you through using FlowFuse to effectively manage your Node-RED instance, enhancing both the security and scalability of your IoT applications.
-
-## Getting Started
-
-This guide explores how to install and run Node-RED through the FlowFuse Device Agent on the Robustel EG5120, enabling you to build, manage, and scale Node-RED flows efficiently from a remote location.
-
-{% include "hardware/system/debian-ff-install.md" %}
-
-{% include "hardware/device-registration.md" %}
-
-{% include "hardware/accessing-node-red-editor.md" %}
\ No newline at end of file
diff --git a/src/node-red/integration-technologies/index.md b/src/node-red/integration-technologies/index.md
deleted file mode 100644
index a42dba234b..0000000000
--- a/src/node-red/integration-technologies/index.md
+++ /dev/null
@@ -1,22 +0,0 @@
----
-metaTitle: "Building Applications with Node-RED"
-eleventyNavigation:
- key: "Integration Technologies"
- order: 10
-meta:
- title: Using Different Technologies for Building Applications with Node-RED.
- description: Learn how to leverage various integration technologies with Node-RED for building robust and interconnected applications.
- keywords: node-red, flowfuse, node-red integration, integration technologies, webhook, rest api
----
-
-# {{meta.title}}
-
-Developing powerful and scalable applications frequently necessitates the integration of various technologies. This integration is crucial for creating seamless and efficient systems that can handle complex tasks and large volumes of data. Whether you're working with REST APIs to enable communication between different services or implementing GraphQL for more efficient data querying, the ability to blend these technologies effectively is essential for modern application development.
-
-Node-RED offers a versatile and robust platform to meet these needs. Its intuitive flow-based interface allows developers to easily design and deploy sophisticated workflows, which makes it an invaluable tool for integrating REST APIs, GraphQL, webhooks, and more.
-
-## Resources
-
-Here are some resources to help you integrate Node-RED with various different technologies:
-
-{% include "navigation-items-list.njk" %}
\ No newline at end of file
diff --git a/src/node-red/learn.md b/src/node-red/learn.md
deleted file mode 100644
index 664ab80f21..0000000000
--- a/src/node-red/learn.md
+++ /dev/null
@@ -1,63 +0,0 @@
----
-eleventyNavigation:
- key: Node-RED Documentation
- order: 0
-meta:
- title: "Node-RED Documentation: 100+ Tutorials (2026)"
- description: "Master Node-RED visual programming with comprehensive documentation covering core nodes, database integration, hardware setup, communication protocols, and real-world industrial IoT examples."
- keywords: node-red, node red, documentation, guide, core nodes, database integration, hardware setup, protocols, industrial iot
----
-
-# Node-RED Documentation: 100+ Tutorials (2026)
-
-[Node-RED](/node-red/), the low-code visual programming platform, is becoming increasingly popular for optimizing and automating industrial processes. Enabling the integration of different hardware devices, API services, and technologies, it's exceptionally suited for a variety of industrial IoT applications.
-
-This documentation provides comprehensive guidance for working with Node-RED, from foundational concepts to advanced implementations. You'll find detailed explanations of core nodes, terminology references, integration guides for databases and communication protocols, and platform-specific setup instructions for various hardware devices.
-
-## Common Terminology
-
-A good starting point is understanding the common terms used in Node-RED. You should be able to confidently navigate through most Node-RED-related resources and participate in Node-RED forum discussions.
-
-[Node-RED Common Terminologies](/node-red/terminology/)
-
-## Core Nodes
-
-This section includes detailed documentation for each Node-RED core node, from configuration to use in real-world applications with examples.
-
-[Core Nodes](/node-red/core-nodes/)
-
-## Hardware Devices
-
-In this section, you will find tutorials on setting up Node-RED on different hardware devices. Each tutorial covers configuration, practical demos and troubleshooting tips.
-
-[Setting Up Node-RED on Different Hardware](/node-red/hardware/)
-
-## Peripheral Devices
-
-This section includes step-by-step documentation on integrating Node-RED with various peripheral devices such as webcams, barcode scanners, printer, mic and more.
-
-[Integrating Node-RED with Peripheral Devices](/node-red/peripheral/)
-
-## Databases
-
-This section includes tutorials on how to use different databases with Node-RED. Each tutorial provides step-by-step instructions along with practical examples. We cover a variety of databases such as SQL, NoSQL, and time-series databases.
-
-[Node-RED Database Integration Guides](/node-red/database/)
-
-## Communication Protocols
-
-This section includes comprehensive tutorials on how to use different protocols with Node-RED for building applications, communicating with edge devices, and more. Each tutorial provides step-by-step instructions along with practical demos.
-
-[Using Different Protocols for Building Applications with Node-RED](/node-red/protocol/)
-
-## Integration Technologies
-
-In this section you will find tutorials on how to utilize various technologies with Node-RED, such as implementing webhooks for task automation, creating REST APIs, GraphQL integrations, and more.
-
-[Using Different Technologies for Building Applications with Node-RED](/node-red/integration-technologies/)
-
-## Notification Services
-
-In this section, we cover how to integrate various notification services with Node-RED to send real-time alerts and notifications. It includes step-by-step guides along with troubleshooting tips.
-
-[Notification Services in Node-RED](/node-red/notification/)
diff --git a/src/node-red/node-red.json b/src/node-red/node-red.json
deleted file mode 100644
index 5dfbbb1e3e..0000000000
--- a/src/node-red/node-red.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "layout": "layouts/documentation.njk",
- "hideTitle": "true",
- "nav": "node-red",
- "searchTitle": "Node-RED",
- "tags": [
- "learningResources"
- ]
-}
diff --git a/src/node-red/notification/index.md b/src/node-red/notification/index.md
deleted file mode 100644
index b54da184c0..0000000000
--- a/src/node-red/notification/index.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-eleventyNavigation:
- key: "Notification Services"
- order: 11
-meta:
- title: Notification Services in Node-RED
- description: Learn how to integrate various notification services with Node-RED for real-time alerts and messaging.
- keywords: node-red, notification services, email notifications, Telegram notifications, Slack notifications, WhatsApp notifications
----
-
-# Notification Services
-
-Real-time notifications are essential in our automation world, helping to keep us informed, responsive, and efficient. Whether it's a critical system alert, a customer inquiry, or a simple reminder, timely notifications can significantly enhance productivity and efficiency. However, managing and integrating such services can be challenging in traditional development environments. Node-RED simplifies this process by supporting a wide range of notification services, including email, Telegram, Slack, WhatsApp, and more.
-
-## Resources
-
-Here are some resources to help you get started with Node-RED on diffrent notification services:
-
-{% include "navigation-items-list.njk" %}
\ No newline at end of file
diff --git a/src/node-red/protocol/index.md b/src/node-red/protocol/index.md
deleted file mode 100644
index 592ca23281..0000000000
--- a/src/node-red/protocol/index.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-metaTitle: "Using Different Protocols with Node-RED"
-eleventyNavigation:
- key: "Communication Protocols"
- order: 9
-meta:
- title: Using Different Protocols for Building Applications with Node-RED
- description: Learn how to leverage various communication protocols with Node-RED for building robust and interconnected applications.
- keywords: node-red, iot, mqtt, http, coap, websockets, diffrent protocols with node-red
-certified:
- - "@flowfuse-certified-nodes/cip-suite"
- - "@flowfuse-certified-nodes/opcua"
-certifiedTitle: "Certified nodes for these protocols"
-certifiedIntro: >
- Two protocols are also covered by a FlowFuse certified node, maintained for
- production use. EtherNet/IP, for Rockwell and Allen-Bradley controllers, has no
- page of its own in this section, and is documented with its certified node:
----
-
-# {{meta.title}}
-
-In IoT development, effective communication between devices is essential. This communication is facilitated by various protocols like MQTT, HTTP, CoAP, and WebSockets. Each protocol brings its own set of strengths and is suited for different IoT scenarios. However, understanding which protocol is suited for what scenario and utilizing it can be quite difficult. That's why we have created this section of resources where you will find documentation on using different communication protocols with Node-RED.
-
-Node-RED, with its intuitive visual programming interface, simplifies the integration of these protocols. Whether you're publishing sensor data over MQTT, triggering HTTP requests, querying CoAP endpoints, or enabling real-time communication with WebSockets, Node-RED provides a flexible and powerful platform.
-
-## Resources
-
-Here are some resources to help you get started with integrating Node-RED with various communication protocols:
-
-{% include "navigation-items-list.njk" %}
-
-{% include "components/certified-node-callout.liquid" %}
\ No newline at end of file
From 574dd71e421f86ab68d2f02325bd1f51edaffeab Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 17:56:47 +0200
Subject: [PATCH 04/17] docs: fetch core node help per build, and fail the
build when it is missing
Replaces the committed help snapshot. A copy cannot be 1:1 with upstream and
somebody has to remember to refresh it, so the pages fetch Node-RED's help on
every build as they always did. What changes is the failure mode.
lib/core-node-docs.js selected the help with an xpath, got an empty node-set when
upstream renamed a block, joined it to an empty string and rendered the page
anyway. That is how four pages came to ship an empty "Node Documentation"
section. Now a help name the catalogue asks for and upstream does not have stops
the build and names every miss at once, because when a locale file is
reorganised several nodes move together and one error per run takes several runs
to work through.
Transport failures are told apart from mismatches. A 5xx or a dropped connection
is retried with backoff, since one bad minute at GitHub should not fail a deploy.
A 404, or a file that parses but lacks the requested help name, is a real
mismatch no retry will fix and throws immediately.
Responses are cached per locale file rather than per node, so the 38 pages cost
21 requests, about three seconds.
Also fixes the prerender break from the previous commit: the generator writes
straight into nuxt/content/docs rather than through guides-sync, so it has to
emit index.md itself. destinationFor is what renames README.md on the way in, and
writing README.md here produced routes like /docs/node-red/core-nodes/README/
that prerendered as 404s.
---
nuxt/lib/core-node-help.json | 40 -------------
nuxt/lib/core-nodes-sync.mjs | 90 +++++++++++++++++++++++-------
nuxt/lib/core-nodes-sync.test.mjs | 77 +++++++++++++++++++++----
nuxt/modules/docs-source.ts | 16 ++----
scripts/refresh_core_node_help.mjs | 28 ----------
scripts/sync_docs.mjs | 10 +---
6 files changed, 144 insertions(+), 117 deletions(-)
delete mode 100644 nuxt/lib/core-node-help.json
delete mode 100644 scripts/refresh_core_node_help.mjs
diff --git a/nuxt/lib/core-node-help.json b/nuxt/lib/core-node-help.json
deleted file mode 100644
index 323a542038..0000000000
--- a/nuxt/lib/core-node-help.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "common/catch": "
Catch errors thrown by nodes on the same tab.
\n
Outputs
\n
\n
error.message string
\n
the error message.
\n
error.source.id string
\n
the id of the node that threw the error.
\n
error.source.type string
\n
the type of the node that threw the error.
\n
error.source.name string
\n
the name, if set, of the node that threw the error.
\n
\n
Details
\n
If a node throws an error whilst handling a message, the flow will typically\n halt. This node can be used to catch those errors and handle them with a\n dedicated flow.
\n
By default, the node will catch errors thrown by any node on the same tab. Alternatively\n it can be targetted at specific nodes, or configured to only catch errors that\n have not already been caught by a 'targeted' catch node.
\n
When an error is thrown, all matching catch nodes will receive the message.
\n
If an error is thrown within a subflow, the error will get handled by any\n catch nodes within the subflow. If none exists, the error will be propagated\n up to the tab the subflow instance is on.
\n
If the message already has a error property, it is copied to _error.
",
- "common/comment": "
A node you can use to add comments to your flows.
\n
Details
\n
The edit panel will accept Markdown syntax. The text will be rendered into\n the information side panel.
",
- "common/complete": "
Trigger a flow when another node completes its handling of a message.
\n
Details
\n
If a node tells the runtime when it has finished handling a message,\n this node can be used to trigger a second flow.
\n
For example, this can be used alongside a node with no output port,\n such as the Email sending node, to continue the flow.
\n
This node must be configured to handle the event for selected nodes in the\n flow. Unlike the Catch node, it does not provide a 'handle all' mode automatically\n applies to all nodes in the flow.
\n
Not all nodes will trigger this event - it will depend on whether they\n have been implemented to support this feature as introduced in Node-RED 1.0.
",
- "common/debug": "
Displays selected message properties in the debug sidebar tab and optionally the runtime log. By default it displays msg.payload, but can be configured to display any property, the full message or the result of a JSONata expression.
\n
Details
\n
The debug sidebar provides a structured view of the messages it is sent, making it easier to understand their structure.
\n
JavaScript objects and arrays can be collapsed and expanded as required. Buffer objects can be displayed as raw data or as a string if possible.
\n
Alongside each message, the debug sidebar includes information about the time the message was received, the node that sent it and the type of the message.\n Clicking on the source node id will reveal that node within the workspace.
\n
The button on the node can be used to enable or disable its output. It is recommended to disable or remove any Debug nodes that are not being used.
\n
The node can also be configured to send all messages to the runtime log, or to send short (32 characters) to the status text under the debug node.
",
- "common/inject": "
Injects a message into a flow either manually or at regular intervals. The message\npayload can be a variety of types, including strings, JavaScript objects or the current time.
\n
Outputs
\n
\n
payloadvarious
\n
The configured payload of the message.
\n
topic string
\n
An optional property that can be configured in the node.
\n
\n
Details
\n
The Inject node can initiate a flow with a specific payload value.\nThe default payload is a timestamp of the current time in millisecs since January 1st, 1970.
\n
The node also supports injecting strings, numbers, booleans, JavaScript objects, or flow/global context values.
\n
By default, the node is triggered manually by clicking on its button within the editor. It can also be set to\ninject at regular intervals or according to a schedule.
\n
It can also be configured to inject once each time the flows are started.
\n
The maximum Interval that can be specified is about 596 hours / 24 days. However if you are looking at intervals\ngreater than one day you should consider using a scheduler node that can cope with power outages and restarts.
\n
Note: The \"Interval between times\" and \"at a specific time\" options use the standard cron system.\nThis means that 20 minutes will be at the next hour, 20 minutes past and 40 minutes past - not in 20 minutes time.\nIf you want every 20 minutes from now - use the \"interval\" option.
\n
Note: To include a newline in a string you must use the Function or Template node to create the payload.
",
- "common/link": "
Create virtual wires between flows.
\n
Details
\n
The node can be connected to any link out node that exists on any tab.\n Once connected, they behave as if they were wired together.
\n
The wires between link nodes are only displayed when a link node is selected.\n If there are any wires to other tabs, a virtual node is shown that can be clicked\n on to jump to the appropriate tab.
\n
Note: Links cannot be created going into, or out of, a subflow.
",
- "common/status": "
Report status messages from other nodes on the same tab.
\n
Outputs
\n
\n
status.text string
\n
the status text.
\n
status.source.type string
\n
the type of the node that reported status.
\n
status.source.id string
\n
the id of the node that reported status.
\n
status.source.name string
\n
the name, if set, of the node that reported status.
\n
\n
Details
\n
This node does not produce a payload.
\n
By default the node reports status for all nodes on the same workspace tab.\n It can be configured to selectively report status for individual nodes.
",
- "common/unknown": "
This node is a type unknown to your installation of Node-RED.
\n
Details
\n
If you deploy with the node in this state, its configuration will be preserved, but\n the flow will not start until the missing type is installed.
\n
Use the Menu - Manage Palette option\n to search for and install nodes, or npm install <module> to\n install, any missing modules and restart Node-RED and reimport the nodes.
\n
It is possible this node type is already installed, but is missing a dependency. Check the Node-RED start-up\n log for any error messages associated with the missing node type.
\n
Otherwise, you should contact the author of the flow to obtain a copy of the missing node type.
",
- "function/change": "
Set, change, delete or move properties of a message, flow context or global context.
\n
The node can specify multiple rules that will be applied in the order they are defined.
\n
Details
\n
The available operations are:
\n
\n
Set
\n
set a property. The value can be a variety of different types, or\n can be taken from an existing message or context property.
\n
Change
\n
search & replace parts of the property. If regular expressions\n are enabled, the \"replace with\" property can include capture groups, for\n example $1. Replace will only change the type if there\n is a complete match.
\n
Delete
\n
delete a property.
\n
Move
\n
move or rename a property.
\n
\n
The \"expression\" type uses the JSONata\n query and expression language.\n
",
- "function/delay": "
Delays each message passing through the node or limits the rate at which they can pass.
\n
Not all input parameters apply to all modes.
\n
Inputs
\n
\n
delay number
\n
Sets the delay, in milliseconds, to be applied to the message. This\n option only applies if the node is configured to allow the message to\n override the configured default delay interval.
\n
rate number
\n
Sets the rate value in milliseconds between messages.\n This node overwrites the existing rate value defined in the node configuration\n when it receives the message which contains msg.rate value in milliSeconds.\n This option only applies if the node is configured to allow the message to\n override the configured default rate interval.
\n
reset
\n
If the received message has this property set to any value, all\n outstanding messages held by the node are cleared without being sent.
\n
flush
\n
If the received message has this property set to a numeric value then that many messages\n will be released immediately. If set to any other type (e.g. boolean), then all\n outstanding messages held by the node are sent immediately.
\n
toFront
\n
When in rate limit mode, if the received message has this property set to boolean true,\n then the message is pushed to the front of the queue and will be released next.\n This can be used in combination with msg.flush=1 to resend immediately.\n
\n
\n
Details
\n
When configured to delay messages, the delay interval can be a fixed value,\n a random value within a range or dynamically set for each message.\n Each message is delayed independently of any other message, based on\n the time of its arrival.\n
\n
When configured to rate limit messages, the node can operate in several modes.\n By default the messages are delivered evenly spread across\n the configured time period. e.g. 3 msgs in 12 seconds means 1 message every 4 seconds.\n The status shows the number of messages currently in the queue.\n It can optionally discard intermediate messages as they arrive.\n
\n
If set to allow override of the rate, the new rate will be applied immediately,\n and will remain in effect until changed again, the node is reset, or the flow is restarted.
\n
The rate limiting can be applied to all messages, or group them according to\n their msg.topic value. When grouping, intermediate messages are\n automatically dropped. At each time interval, the node can either release\n the most recent message for all topics, or release the most recent message\n for the next topic.\n
\n
In burst mode, messages are passed through up to the configured limit immediately,\n any further messages will be dropped or sent to the second output until the time\n period has elapsed. At that point the next burst of messages can be sent.\n The status shows green when messages can be sent immediately, and red when\n messages are blocked.\n
\n
Note: In rate limit mode the maximum queue depth can be set by a property in your\n settings.js file. For example nodeMessageBufferMaxLength: 1000,
",
- "function/exec": "
Runs a system command and returns its output.
\n
The node can be configured to either wait until the command completes, or to\n send its output as the command generates it.
\n
The command that is run can be configured in the node or provided by the received\n message.
\n\n
Inputs
\n
\n
payload string
\n
if configured to do so, will be appended to the executed command.
\n
kill string
\n
the type of kill signal to send an existing exec node process.
\n
pid number|string
\n
the process ID of an existing exec node process to kill.
\n
\n\n
Outputs
\n \n
Standard output\n
\n
payload string
\n
the standard output of the command.
\n
\n
\n
rc object
\n
exec mode only, a copy of the return code object (also available on port 3)
\n
\n
\n
Standard error\n
\n
payload string
\n
the standard error of the command.
\n
\n
\n
rc object
\n
exec mode only, a copy of the return code object (also available on port 3)
\n
\n
\n
Return code\n
\n
payload object
\n
an object containing the return code, and possibly message, signal properties.
\n
\n
\n \n
Details
\n
By default uses the exec system call which calls the command, waits for it to complete, and then\n returns the output. For example a successful command should have a return code of { code: 0 }.
\n
Optionally can use spawn instead, which returns the output from stdout and stderr\n as the command runs, usually one line at a time. On completion it then returns an object\n on the 3rd port. For example, a successful command should return { code: 0 }.
\n
Errors may return extra information on the 3rd port msg.payload, such as a message string,\n signal string.
\n
The command that is run is defined within the node, with an option to append msg.payload and a further set of parameters.
\n
Commands or parameters with spaces should be enclosed in quotes - \"This is a single parameter\"
\n
The returned payload is usually a string, unless non-UTF8 characters are detected, in which\n case it is a buffer.
\n
The node's status icon and PID will be visible while the node is active. Changes to this can be read by the Status node.
\n
The Hide console option will hide the process console normally shown on Windows systems.
\n
Killing processes
\n
Sending msg.kill will kill a single active process. msg.kill should be a string containing\n the type of signal to be sent, for example, SIGINT, SIGQUIT or SIGHUP.\n Defaults to SIGTERM if set to an empty string.
\n
If the node has more than one process running then msg.pid must also be set with the value of the PID to be killed.
\n
If a value is provided in the Timeout field then, if the process has not completed when the specified number of seconds has elapsed, the process will be killed automatically
\n
Tip: if running a Python app you may need to use the -u parameter to stop the output being buffered.
",
- "function/filter": "
filter node - only passes on data if the payload has changed.\n It can also block unless, or ignore if the value changes by a specified amount (Dead- and Narrowband mode).
\n
Inputs
\n
\n
payload\n number | string | (object)\n
\n
RBE mode will accept numbers, strings, and simple objects.\n Other modes must provide a parseable number.
\n
topic string\n
\n
if specified the function will work on a per topic basis. This property can be set by configuration.
\n
resetany
\n
if set clears the stored value for the specified msg.topic, or\n all topics if msg.topic is not specified.
\n
\n
Outputs
\n
\n
payload\n as per input\n
\n
If triggered the output will be the same as the input.
\n
\n
Details
\n
In RBE mode this node will block until the msg.payload,\n (or selected property) value is different to the previous one.\n If required it can ignore the initial value, so as not to send anything at start.
\n
The Deadband modes will block the incoming value\n unless its change is greater or greater-equal than ± the band gap away from a previous value.
\n
The Narrowband modes will block the incoming value,\n if its change is greater or greater-equal than ± the band gap away from the previous value.\n It is useful for ignoring outliers from a faulty sensor for example.
\n
Both in Deadband and Narrowband modes the incoming value must contain a parseable number and\n both also supports % - only sends if/unless the input differs by more than x% of the original value.
\n
Both Deadband and Narrowband allow comparison against either the previous valid output value, thus\n ignoring any values out of range, or the previous input value, which resets the set point, thus allowing\n gradual drift (deadband), or a step change (narrowband).
\n
Note: This works on a per msg.topic basis, though this can be changed to another property if desired.\n This means that a single filter node can handle multiple different topics at the same time.
",
- "function/function": "
A JavaScript function to run against the messages being received by the node.
\n
The messages are passed in as a JavaScript object called msg.
\n
By convention it will have a msg.payload property containing\n the body of the message.
\n
The function is expected to return a message object (or multiple message objects), but can choose\n to return nothing in order to halt a flow.
\n
The On Start tab contains code that will be run whenever the node is started.\n The On Stop tab contains code that will be run when the node is stopped.
\n
If the On Start code returns a Promise object, the node will not start handling messages\n until the promise is resolved.
The function can either return the messages it wants to pass on to the next nodes\n in the flow, or can call node.send(messages).
\n
It can return/send:
\n
\n
a single message object - passed to nodes connected to the first output
\n
an array of message objects - passed to nodes connected to the corresponding outputs
\n
\n
Note: The setup code is executed during the initialization of nodes. Therefore, if node.send is called in the setup tab, subsequent nodes may not be able to receive the message.
\n
If any element of the array is itself an array of messages, multiple\n messages are sent to the corresponding output.
\n
If null is returned, either by itself or as an element of the array, no\n message is passed on.
\n
Logging and Error Handling
\n
To log any information, or report an error, the following functions are available:
\n
\n
node.log(\"Log message\")
\n
node.warn(\"Warning\")
\n
node.error(\"Error\")
\n
\n \n
The Catch node can also be used to handle errors. To invoke a Catch node,\n pass msg as a second argument to node.error:
\n
node.error(\"Error\",msg);
\n
Accessing Node Information
\n
The following properties are available to access information about the node:
\n
\n
node.id - id of the node
\n
node.name - name of the node
\n
node.outputCount - number of node outputs
\n
\n
Using environment variables
\n
Environment variables can be accessed using env.get(\"MY_ENV_VAR\").
",
- "function/range": "
Maps a numeric value to a different range.
\n
Inputs
\n
\n
payload number
\n
The payload must be a number. Anything else will try to be\n parsed into a number and rejected if that fails.
\n
\n
Outputs
\n
\n
payload number
\n
The value mapped to the new range.
\n
\n
Details
\n
This node will linearly scale the received value. By default, the result\n is not constrained to the range defined in the node.
\n
Scale and limit to target range means that the result will never be outside\n the range specified within the target range.
\n
Scale and wrap within the target range means that the result will\n be wrapped within the target range.
\n
Scale, but drop if outside input range means that the result will\n be scaled, but any inputs outside of the inout range will be dropped.
\n
For example an input 0 - 10 mapped to 0 - 100.
\n
\n
mode
input
output
\n
scale
12
120
\n
limit
12
100
\n
wrap
12
20
\n
drop
12
(no output)
\n
",
- "function/switch": "
Route messages based on their property values or sequence position.
\n
Details
\n
When a message arrives, the node will evaluate each of the defined rules\n and forward the message to the corresponding outputs of any matching rules.
\n
Optionally, the node can be set to stop evaluating rules once it finds one\n that matches.
\n
The rules can be evaluated against an individual message property, a flow or global\n context property, environment variable or the result of a JSONata expression.
\n
Rules
\n
There are four types of rule:
\n \n
Value rules are evaluated against the configured property
\n
Sequence rules can be used on message sequences, such as those\n generated by the Split node
\n
A JSONata Expression can be provided that will be evaluated\n against the whole message and will match if the expression returns\n a true value.
\n
An Otherwise rule can be used to match if none of the preceeding\n rules have matched.
\n \n
Notes
\n
The is true/false and is null rules perform strict\n comparisons against those types. They do not convert between types.
\n
The is empty and is not empty rules can be used to test the length of Strings, Arrays and Buffers, or the number of properties an Object has. Neither rule will pass if the property being tested has a boolean, null \n or undefined value.
\n
Handling message sequences
\n
By default, the node does not modify the msg.parts property of messages\n that are part of a sequence.
\n
The recreate message sequences option can be enabled to generate new message sequences\n for each rule that matches. In this mode, the node will buffer the entire incoming\n sequence before sending the new sequences on. The runtime setting nodeMessageBufferMaxLength\n can be used to limit how many messages nodes will buffer.
",
- "function/template": "
Sets a property based on the provided template.
\n
Inputs
\n
\n
msg object
\n
A msg object containing information to populate the template.
\n
template string
\n
A template to be populated from msg.payload. If not configured in the edit panel,\n this can be set as a property of msg.
\n
\n
Outputs
\n
\n
msg object
\n
a msg with a property set by populating the configured template with properties from the incoming msg.
\n
\n
Details
\n
By default this uses the mustache\n format, but this can be switched off if required.
It is possible to use a property from the flow context or global context. Just use {{flow.name}} or\n {{global.name}}, or for persistable store store use {{flow[store].name}} or\n {{global[store].name}}.\n
Note: By default, mustache will escape any non-alphanumeric or HTML entities in the values it substitutes.\n To prevent this, use {{{triple}}} braces.
\n
If you need to use {{ }} within your content, you can change the characters\n used to mark the templated sections. For example, to use [[ ]]\n instead, add the following line to the top of the template:
\n
{{=[[ ]]=}}
\n
Using environment variables
\n
The template node can access environment variables using the syntax:
\n
My favourite colour is {{env.COLOUR}}.
",
- "function/trigger": "
When triggered, can send a message, and then optionally a second message, unless extended or reset.
\n\n
Inputs
\n
\n
delay number
\n
Sets the delay, in milliseconds, to be applied to the message. This option only applies if the node is configured to allow the message to override the configured default delay interval.
\n
reset
\n
If a message is received with this property, any timeout or repeat\n currently in progress will be cleared and no message triggered.
\n
\n\n
Details
\n
This node can be used to create a timeout within a flow. By default, when\n it receives a message, it sends on a message with a payload of 1.\n It then waits 250ms before sending a second message with a payload of 0.\n This could be used, for example, to blink an LED attached to a Raspberry Pi GPIO pin.
\n
The payloads of each message sent can be configured to a variety of values, including\n the option to not send anything. For example, setting the initial message to nothing and\n selecting the option to extend the timer with each received message, the node will\n act as a watchdog timer; only sending a message if nothing is received within the\n set interval.
\n
If set to a string type, the node supports the mustache template syntax.
\n
The delay between sending messages can be overridden by msg.delay if that option is enabled in the node. The value must be provided in milliseconds.
\n
If the node receives a message with a reset property, or a payload\n that matches that configured in the node, any timeout or repeat currently in\n progress will be cleared and no message triggered.
\n
The node can be configured to resend a message at a regular interval until it\n is reset by a received message.
\n
Optionally, the node can be configured to treat messages as if they are separate streams,\n using a msg property to identify each stream. Default msg.topic.
\n
The status indicates the node is currently active. If multiple streams are used the status\n indicates the number of streams being held.
",
- "network/http-in": "
Creates an HTTP end-point for creating web services.
\n
Outputs
\n
\n
payload
\n
For a GET request, contains an object of any query string parameters.\n Otherwise, contains the body of the HTTP request.
\n
reqobject
\n
An HTTP request object. This object contains multiple properties that\n provide information about the request.\n
\n
body - the body of the incoming request. The format\n will depend on the request.
\n
headers - an object containing the HTTP request headers.
\n
query - an object containing any query string parameters.
\n
params - an object containing any route parameters.
\n
cookies - an object containing the cookies for the request.
\n
files - if enabled within the node, an object containing\n any files uploaded as part of a POST request.
\n
\n
\n
resobject
\n
An HTTP response object. This property should not be used directly;\n the HTTP Response node documents how to respond to a request.\n This property must remain attached to the message passed to the response node.
\n
\n
Details
\n
The node will listen on the configured path for requests of a particular type.\n The path can be fully specified, such as /user, or include\n named parameters that accept any value, such as /user/:name.\n When named parameters are used, their actual value in a request can be accessed under msg.req.params.
\n
For requests that include a body, such as a POST or PUT, the contents of\n the request is made available as msg.payload.
\n
If the content type of the request can be determined, the body will be parsed to\n any appropriate type. For example, application/json will be parsed to\n its JavaScript object representation.
\n
The node can be configured to not parse the body, in which case it will be provided as a Buffer object.
\n
Note: this node does not send any response to the request. The flow\n must include an HTTP Response node to complete the request.
",
- "network/http-proxy": "
Configuration options for HTTP proxy.
\n\n
Details
\n
When accessing to the host in the ignored host list, no proxy will be used.
",
- "network/http-request": "
Sends HTTP requests and returns the response.
\n\n
Inputs
\n
\n
url string
\n
If not configured in the node, this optional property sets the url of the request.
\n
method string
\n
If not configured in the node, this optional property sets the HTTP method of the request.\n Must be one of GET, PUT, POST, PATCH or DELETE.
\n
headers object
\n
Sets the HTTP headers of the request. NOTE: Any headers set in the node configuration will overwrite any matching headers in msg.headers
\n
cookies object
\n
If set, can be used to send cookies with the request.
\n
payload
\n
Sent as the body of the request.
\n
rejectUnauthorized
\n
If set to false, allows requests to be made to https sites that use\n self signed certificates.
\n
followRedirects
\n
If set to false prevent following Redirect (HTTP 301).true by default
\n
requestTimeout
\n
If set to a positive number of milliseconds, will override the globally set httpRequestTimeout parameter.
\n
\n
Outputs
\n
\n
payload string | object | buffer
\n
The body of the response. The node can be configured to return the body\n as a string, attempt to parse it as a JSON string or leave it as a\n binary buffer.
\n
statusCode number
\n
The status code of the response, or the error code if the request could not be completed.
\n
headers object
\n
An object containing the response headers.
\n
responseUrl string
\n
In case any redirects occurred while processing the request, this property is the final redirected url.\n Otherwise, the url of the original request.
\n
responseCookies object
\n
If the response includes cookies, this property is an object of name/value pairs for each cookie.
\n
redirectList array
\n
If the request was redirected one or more times, the accumulated information will be added to this property. `location` is the next redirect destination. `cookies` is the cookies returned from the redirect source.
\n
\n
Details
\n
When configured within the node, the URL property can contain mustache-style tags. These allow the\n url to be constructed using values of the incoming message. For example, if the url is set to\n example.com/{{{topic}}}, it will have the value of msg.topic automatically inserted.\n Using {{{...}}} prevents mustache from escaping characters like / & etc.
\n
The node can optionally automatically encode msg.payload as query string parameters for a GET request, in which case msg.payload has to be an object.
\n
Note: If running behind a proxy, the standard http_proxy=... environment variable should be set and Node-RED restarted, or use Proxy Configuration. If Proxy Configuration was set, the configuration take precedence over environment variable.
\n
Using multiple HTTP Request nodes
\n
In order to use more than one of these nodes in the same flow, care must be taken with\n the msg.headers property. The first node will set this property with\n the response headers. The next node will then use those headers for its request - this\n is not usually the right thing to do. If msg.headers property is left unchanged\n between nodes, it will be ignored by the second node. To set custom headers, msg.headers\n should first be deleted or reset to an empty object: {}.\n
Cookie handling
\n
The cookies property passed to the node must be an object of name/value pairs.\n The value can be either a string to set the value of the cookie or it can be an\n object with a single value property.
\n
Any cookies returned by the request are passed back under the responseCookies property.
\n
Content type handling
\n
If msg.payload is an Object, the node will automatically set the content type\n of the request to application/json and encode the body as such.
\n
To encode the request as form data, msg.headers[\"content-type\"] should be set to application/x-www-form-urlencoded.
\n
File Upload
\n
To perform a file upload, msg.headers[\"content-type\"] should be set to multipart/form-data\n and the msg.payload passed to the node must be an object with the following structure:
The values of KEY, FILE_CONTENTS and FILENAME\n should be set to the appropriate values.
",
- "network/mqtt-in": "
Connects to a MQTT broker and subscribes to messages from the specified topic.
\n
Outputs
\n
\n
payload string | buffer
\n
a string unless detected as a binary buffer.
\n
topic string
\n
the MQTT topic, uses / as a hierarchy separator.
\n
qos number
\n
0, fire and forget - 1, at least once - 2, once and once only.
\n
retain boolean
\n
true indicates the message was retained and may be old.
\n\n
responseTopic string
\n
MQTTv5: the MQTT response topic for the message
\n
correlationData Buffer
\n
MQTTv5: the correlation data for the message
\n
contentType string
\n
MQTTv5: the content-type of the payload
\n
userProperties object
\n
MQTTv5: any user properties of the message
\n
messageExpiryInterval number
\n
MQTTv5: the expiry time, in seconds, of the message
\n
\n
Details
\n The subscription topic can include MQTT wildcards, + for one level, # for multiple levels.\n
This node requires a connection to a MQTT broker to be configured. This is configured by clicking\n the pencil icon.
\n
Several MQTT nodes (in or out) can share the same broker connection if required.
\n
Dynamic Subscription
\n The node can be configured to dynamically control the MQTT connection and its subscriptions. When\n enabled, the node will have an input and can be controlled by passing it messages.\n
Inputs
\n
These only apply when the node has been configured for dynamic subscriptions.
\n
\n
action string
\n
the name of the action the node should perform. Available actions are: \"connect\",\n \"disconnect\", \"getSubscriptions\", \"subscribe\" and \n \"unsubscribe\".
\n
topic string|object|array
\n
For the \"subscribe\" and \"unsubscribe\" actions, this property\n provides the topic. It can be set as either:
\n
a String containing the topic filter
\n
an Object containing topic and qos properties
\n
an array of either strings or objects to handle multiple topics in one
\n
\n
\n
broker broker
\n
For the \"connect\" action, this property can override any\n of the individual broker configuration settings, including:
\n
broker
\n
port
\n
url - overrides broker/port to provide a complete connection url
\n
username
\n
password
\n
\n
If this property is set and the broker is already connected an error\n will be logged unless it has the force property set - in which case it will\n disconnect from the broker, apply the new settings and reconnect.
\n
\n
",
- "network/mqtt-out": "
Connects to a MQTT broker and publishes messages.
\n
Inputs
\n
\n
payload string | buffer
\n
the payload to publish. If this property is not set, no message will be sent. To send a blank message, set this property to an empty String.
\n
topic string
\n
the MQTT topic to publish to.
\n
qos number
\n
0, fire and forget - 1, at least once - 2, once and once only. Default 0.
\n
retain boolean
\n
set to true to retain the message on the broker. Default false.
\n
responseTopic string
\n
MQTTv5: the MQTT response topic for the message
\n
correlationData Buffer
\n
MQTTv5: the correlation data for the message
\n
contentType string
\n
MQTTv5: the content-type of the payload
\n
userProperties object
\n
MQTTv5: any user properties of the message
\n
messageExpiryInterval number
\n
MQTTv5: the expiry time, in seconds, of the message
\n
topicAlias number
\n
MQTTv5: the MQTT topic alias to use
\n
\n
Details
\n msg.payload is used as the payload of the published message.\n If it contains an Object it will be converted to a JSON string before being sent.\n If it contains a binary Buffer the message will be published as-is.\n
The topic used can be configured in the node or, if left blank, can be set by msg.topic.
\n
Likewise the QoS and retain values can be configured in the node or, if left\n blank, set by msg.qos and msg.retain respectively. To clear a previously\n retained topic from the broker, send a blank message to that topic with the retain flag set.
\n
This node requires a connection to a MQTT broker to be configured. This is configured by clicking\n the pencil icon.
\n
Several MQTT nodes (in or out) can share the same broker connection if required.
\n\n
Dynamic Control
\n The connection shared by the node can be controlled dynamically. If the node receives\n one of the following control messages, it will not publish the message payload as well.\n
Inputs
\n
\n
action string
\n
the name of the action the node should perform. Available actions are: \"connect\",\n and \"disconnect\".
\n
broker broker
\n
For the \"connect\" action, this property can override any\n of the individual broker configuration settings, including:
\n
broker
\n
port
\n
url - overrides broker/port to provide a complete connection url
\n
username
\n
password
\n
\n
If this property is set and the broker is already connected an error\n will be logged unless it has the force property set - in which case it will\n disconnect from the broker, apply the new settings and reconnect.
\n
\n
",
- "network/tcp-in": "
Provides a choice of TCP inputs. Can either connect to a remote TCP port,\n or accept incoming connections.
\n
Note: On some systems you may need root or administrator access\n to access ports below 1024.
",
- "network/tls": "
Configuration options for TLS connections.
",
- "network/udp-in": "
A UDP input node, that produces a msg.payload containing a\n Buffer, string, or base64 encoded string. Supports multicast.
\n
It also provides msg.ip and msg.port set to the\n ip address and port from which the message was received.
\n
Note: On some systems you may need root or administrator access to use\n ports below 1024 and/or broadcast.
",
- "network/udp-out": "
This node sends msg.payload to the designated UDP host and port. Supports multicast.
\n
You may also use msg.ip and msg.port to set the destination values, but the statically configured values have precedence.
\n
If you select broadcast either set the address to the local broadcast ip address, or maybe try 255.255.255.255, which is the global broadcast address.
\n
Note: On some systems you may need to be root to use ports below 1024 and/or broadcast.
",
- "network/websocket": "
WebSocket input node.
\n
By default, the data received from the WebSocket will be in msg.payload.\n The socket can be configured to expect a properly formed JSON string, in which\n case it will parse the JSON and send on the resulting object as the entire message.
",
- "parsers/csv": "
Converts between a CSV formatted string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | array | string
\n
A JavaScript object, array or CSV string.
\n
\n
Outputs
\n
\n
payloadobject | array | string
\n
\n
\n
If the input is a string it tries to parse it as CSV and creates a JavaScript object of key/value pairs for each line.\n The node will then either send a message for each line, or a single message containing an array of objects.
\n
If the input is a JavaScript object it tries to build a CSV string.
\n
If the input is an array of simple values, it builds a single line CSV string.
\n
If the input is an array of arrays, or an array of objects, a multiple-line CSV string is created.
\n
\n
\n
\n
Details
\n
The column template can contain an ordered list of column names. When converting CSV to an object, the column names\n will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.\n
When the RFC parser is selected, the column template must be compliant with RFC4180.
\n \n
When converting to CSV, the columns template is used to identify which properties to extract from the object and in what order.
\n
If the columns template is blank then you can use a simple comma separated list of properties supplied in msg.columns to\n determine what to extract and in what order. If neither are present then all the object properties are output in the order\n in which the properties are found in the first row.
\n
If the input is an array then the columns template is only used to optionally generate a row of column titles.
\n
If 'parse numerical values' option is checked, string numerical values will be returned as numbers, ie. middle value '1,\"1.5\",2'.
\n
If 'include empty strings' option is checked, empty strings will be returned in result, ie. middle value '\"1\",\"\",3'.
\n
If 'include null values' option is checked, null values will be returned in result, ie. middle value '\"1\",,3'.
\n
The node can accept a multi-part input as long as the parts property is set correctly, for example from a file-in node or split node.
\n
If outputting multiple messages they will have their parts property set and form a complete message sequence.
\n
If the node is set to only send column headers once, then setting msg.reset to any value will cause the node to resend the headers.
\n
Note: the column template must be comma separated - even if a different separator is chosen for the data.
\n
Note: in RFC mode, catchable errors will be thrown for malformed CSV headers and invalid input payload data
",
- "parsers/html": "
Extracts elements from an html document held in msg.payload using a CSS selector.
\n
Inputs
\n
\n
payload string
\n
the html string from which to extract elements.
\n
select string
\n
if not configured in the edit panel the selector can be set as a property of msg.
\n
\n
Output
\n
\n
payload array | string
\n
the result can be either a single message with a payload containing an array of the matched elements, or multiple\n messages that each contain a matched element. If multiple messages are sent they will also have parts set.
\n
\n
Details
\n
This node supports a combination of CSS and jQuery selectors. See the\n css-select documentation for more information\n on the supported syntax.
",
- "parsers/json": "
Converts between a JSON string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | string
\n
A JavaScript object or JSON string.
\n
schemaobject
\n
An optional JSON Schema object to validate the payload against.\n The property will be deleted before the msg is sent to the next node.
\n
\n
Outputs
\n
\n
payloadobject | string
\n
\n
\n
If the input is a JSON string it tries to parse it to a JavaScript object.
\n
If the input is a JavaScript object it creates a JSON string. The string can optionally be well-formatted.
\n
\n
\n
schemaErrorarray
\n
If JSON schema validation fails, the catch node will have a schemaError property\n containing an array of errors.
\n
\n
Details
\n
By default, the node operates on msg.payload, but can be configured\n to convert any message property.
\n
The node can also be configured to ensure a particular encoding instead of toggling\n between the two. This can be used, for example, with the HTTP In\n node to ensure the payload is a parsed object even if an incoming request\n did not set its content-type correctly for the HTTP In node to do the conversion.
\n
If the node is configured to ensure the property is encoded as a String and it\n receives a String, no further checks will be made of the property. It will\n not check the String is valid JSON nor will it reformat it if the format option\n is selected.
\n
For more details about JSON Schema you can consult the specification\n here.
",
- "parsers/xml": "
Converts between an XML string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | string
\n
A JavaScript object or XML string.
\n
options object
\n
This optional property can be used to pass in any of the options supported by the underlying\n library used to convert to and from XML. See the xml2js docs\n for more information.
\n
\n
Outputs
\n
\n
payloadobject | string
\n
\n
\n
If the input is a string it tries to parse it as XML and creates a JavaScript object.
\n
If the input is a JavaScript object it tries to build an XML string.
\n
\n
\n
\n
Details
\n
When converting between XML and an object, any XML attributes are added as a property named $ by default.\n Any text content is added as a property named _. These property names can be specified in the node configuration.
\n
For example, the following XML will be converted as shown:
Converts between a YAML formatted string and its JavaScript object representation, in either direction.
\n
Inputs
\n
\n
payloadobject | string
\n
A JavaScript object or YAML string.
\n
\n
Outputs
\n
\n
payloadobject | string
\n
\n
\n
If the input is a YAML string it tries to parse it to a JavaScript object.
\n
If the input is a JavaScript object it creates a YAML string.
\n
\n
\n
",
- "sequence/batch": "
Creates sequences of messages based on various rules.
\n
Details
\n
There are three modes for creating message sequences:
\n
\n
Number of messages
\n
groups messages into sequences of a given length. The overlap\n option specifies how many messages at the end of one sequence should be\n repeated at the start of the next sequence.
\n\n
Time interval
\n
groups messages that arrive within the specified interval. If no messages\n arrive within the interval, the node can optionally send on an empty message.
\n\n
Concatenate Sequences
\n
creates a message sequence by concatenating incoming sequences. Each message\n must have a msg.topic property and a msg.parts property\n identifying its sequence. The node is configured with a list of topic\n values to identify the order sequences are concatenated.\n
\n
\n
Storing messages
\n
This node will buffer messages internally in order to work across sequences. The\n runtime setting nodeMessageBufferMaxLength can be used to limit how many messages nodes\n will buffer.
\n
If a message is received with the msg.reset property set, the buffered messages are deleted and not sent.
",
- "sequence/join": "
Joins sequences of messages into a single message.
\n
There are three modes available:
\n
\n
automatic
\n
When paired with the split node, it will automatically join the messages to reverse the split that was performed.
\n
manual
\n
Join sequences of messages in a variety of ways.
\n
reduce sequence
\n
Apply an expression against all messages in a sequence to reduce it to a single message.
\n
\n
Inputs
\n
\n
partsobject
\n
To automatically join a sequence of messages, they should all have\n this property set. The split node generates this property but it\n can be manually created. It has the following properties:\n
\n
id - an identifier for the group of messages
\n
index - the position within the group
\n
count - the total number of messages in the group
\n
type - the type of message - string/array/object/buffer
\n
ch - for a string or buffer, the data used to the split the message as either the string or an array of bytes
\n
key - for an object, the key of the property this message was created from
\n
len - the length of each message when split using a fixed length value
\n
\n
\n
complete
\n
If set, the node will append the payload, and then send the output message in its current state.\n If you don't wish to append the payload, delete it from the msg.
\n
reset
\n
If set, the node will clear any partially complete message and not send it.
\n
restartTimeout
\n
If set, and the node has a timeout configured, that timeout will be restarted.
\n
\n
Details
\n\n
Automatic mode
\n
Automatic mode uses the parts property of incoming messages to\n determine how the sequence should be joined. This allows it to automatically\n reverse the action of a split node.
\n\n
Manual mode
\n
When configured to join in manual mode, the node is able to join sequences\n of messages into a number of different results:
\n
\n
a string or buffer - created by joining the selected property of each message with the specified join characters or buffer.
\n
an array - created by adding each selected property, or entire message, to the output array.
\n
a key/value object - created by using a property of each message to determine the key under which\n the required value is stored.
\n
a merged object - created by merging the property of each message under a single object.
\n
\n
The other properties of the output message are taken from the last message received before the result is sent.
\n
A count can be set for how many messages should be received before generating the output message.\n For object outputs, once this count has been reached, the node can be configured to send a message for each subsequent message\n received.
\n
A timeout can be set to trigger sending the new message using whatever has been received so far.\n This timeout can be restarted by sending a message with the msg.restartTimeout property set.
\n
If a message is received with the msg.complete property set, the output message is finalised and sent.\n This resets any part counts.
\n
If a message is received with the msg.reset property set, the partly complete message is deleted and not sent.\n This resets any part counts.
\n\n
Reduce Sequence mode
\n
When configured to join in reduce mode, an expression is applied to each\n message in a sequence and the result accumulated to produce a single message.
\n\n
\n
Initial value
\n
The initial value of the accumulated value ($A).
\n
Reduce expression
\n
A JSONata expression that is called for each message in the sequence.\n The result is passed to the next call of the expression as the accumulated value.\n In the expression, the following special variables can be used:\n
\n
$A: the accumulated value,
\n
$I: index of the message in the sequence,
\n
$N: number of messages in the sequence.
\n
\n
\n
Fix-up expression
\n
An optional JSONata expression that is applied after the reduce expression\n has been applied to all messages in the sequence.\n In the expression, following special variables can be used:\n
\n
$A: the accumulated value,
\n
$N: number of messages in the sequence.
\n
\n
\n
By default, the reduce expression is applied in order, from the first\n to the last message of the sequence. It can optionally be applied in\n reverse order.
\n
$N is the number of messages that arrive - even if they are identical.
\n
\n
Example: the following settings, given a sequence of numeric values,\n calculates the average value:\n
\n
Reduce expression: $A+payload
\n
Initial value: 0
\n
Fix-up expression: $A/$N
\n
\n \n
Storing messages
\n
This node will buffer messages internally in order to work across sequences. The\n runtime setting nodeMessageBufferMaxLength can be used to limit how many messages nodes\n will buffer.
",
- "sequence/sort": "
A function that sorts message property or a sequence of messages.
\n
When configured to sort message property, the node sorts array data pointed to by specified message property.
\n
When configured to sort a sequence of messages, it will reorder the messages.
\n
The sorting order can be:
\n
\n
ascending,
\n
descending.
\n
\n
For numbers, numerical ordering can be specified by a checkbox.
\n
Sort key can be element value or JSONata expression for sorting property value, or message property or JSONata expression for sorting a message sequence.
\n
When sorting a message sequence, the sort node relies on the received messages to have msg.parts set. The split node generates this property, but can be manually created. It has the following properties:
\n
\n
\n
id - an identifier for the group of messages
\n
index - the position within the group
\n
count - the total number of messages in the group
\n
\n \n
Note: This node internally keeps messages for its operation. In order to prevent unexpected memory usage, maximum number of messages kept can be specified. Default is no limit on number of messages.\n
\n
nodeMessageBufferMaxLength property set in settings.js.
\n
\n ",
- "sequence/split": "
Splits a message into a sequence of messages.
\n\n
Inputs
\n
\n
payloadobject | string | array | buffer
\n
The behaviour of the node is determined by the type of msg.payload:\n
\n
string/buffer - the message is split using the specified character (default: \\n), buffer sequence or into fixed lengths.
\n
array - the message is split into either individual array elements, or arrays of a fixed-length.
\n
object - a message is sent for each key/value pair of the object.
\n
\n
\n
\n
Outputs
\n
\n
partsobject
\n
This property contains information about how the message was split from\n the original message. If passed to the join node, the sequence can be\n reassembled into a single message. The property has the following properties:\n
\n
id - an identifier for the group of messages
\n
index - the position within the group
\n
count - if known, the total number of messages in the group. See 'streaming mode' below.
\n
type - the type of message - string/array/object/buffer
\n
ch - for a string or buffer, the data used to the split the message as either the string or an array of bytes
\n
key - for an object, the key of the property this message was created from. The node can be configured to also copy this value to another message properties, such as msg.topic.
\n
len - the length of each message when split using a fixed length value
\n
\n
\n
\n
Details
\n
This node makes it easy to create a flow that performs common actions across\n a sequence of messages before, using the join node, recombining the\n sequence into a single message.
\n
It uses the msg.parts property to track the individual parts\n of a sequence.
\n
Streaming mode
\n
The node can also be used to reflow a stream of messages. For example, a\n serial device that sends newline-terminated commands may deliver a single message\n with a partial command at its end. In 'streaming mode', this node will split\n a message and send each complete segment. If there is a partial segment at the end,\n the node will hold on to it and prepend it to the next message that is received.\n
\n
When operating in this mode, the node will not set the msg.parts.count\n property as it does not know how many messages to expect in the stream. This\n means it cannot be used with the join node in its automatic mode.
",
- "storage/read-file": "
Watches a directory or file for changes.
\n
You can enter a list of comma separated directories and/or files. You will\n need to put quotes \"...\" around any that have spaces in.
\n
On Windows you must use double back-slashes \\\\ in any directory names.
\n
The full filename of the file that actually changed is put into msg.payload and msg.filename,\n while a stringified version of the watch list is returned in msg.topic.
\n
msg.file contains just the short filename of the file that changed.\n msg.type has the type of thing changed, usually file or directory,\n while msg.size holds the file size in bytes.
\n
Of course in Linux, everything is a file and thus can be watched...
\n
Note: The directory or file must exist in order to be watched. If the file\n or directory gets deleted it may no longer be monitored even if it gets re-created.
",
- "storage/write-file": "
Writes msg.payload to a file, either adding to the end or replacing the existing content.\n Alternatively, it can delete the file.
\n
Inputs
\n
\n
filename string
\n
The name of the file to be updated can be provided in the node configuration, or as a message property. \n By default it will use msg.filename but this can be customised in the node.\n
\n
encoding string
\n
If encoding is configured to be set by msg, then this optional property can set the encoding.\n
\n
Output
\n
On completion of write, input message is sent to output port.
\n
Details
\n
Each message payload will be added to the end of the file, optionally appending\n a newline (\\n) character between each one.
\n
If msg.filename is used the file will be closed after every write.\n For best performance use a fixed filename.
\n
It can be configured to overwrite the entire file rather than append. For example,\n when writing binary data to a file, such as an image, this option should be used\n and the option to append a newline should be disabled.
\n
Encoding of data written to a file can be specified from list of encodings.
\n
Alternatively, this node can be configured to delete the file.
"
-}
diff --git a/nuxt/lib/core-nodes-sync.mjs b/nuxt/lib/core-nodes-sync.mjs
index 6d3edbd2e9..51da101c93 100644
--- a/nuxt/lib/core-nodes-sync.mjs
+++ b/nuxt/lib/core-nodes-sync.mjs
@@ -6,11 +6,17 @@
// repo at build time (`lib/core-node-docs.js`). @nuxt/content indexes files, so the pages
// have to exist as markdown before it runs.
//
-// The help text is upstream's, so it is mirrored rather than owned: `data/core-node-help.json`
-// holds a snapshot, committed, and `refreshCoreNodeHelp` re-fetches it on demand. That
-// keeps the production build hermetic. Fetching per build would put a third-party raw
-// githubusercontent URL on the critical path of every deploy and fail the site whenever
-// upstream renamed a locale file.
+// The help text is upstream's, so it is fetched from the Node-RED repo on every build
+// rather than copied into this one. That is a deliberate trade: a copy cannot be 1:1 with
+// upstream, and nobody should have to remember to refresh it. The cost is that a deploy
+// depends on raw.githubusercontent.com being reachable and on the catalogue still matching
+// upstream's help names.
+//
+// What is NOT acceptable is the old failure mode. `lib/core-node-docs.js` selected the
+// help with an xpath, got an empty node-set when upstream renamed a block, joined it to
+// an empty string and rendered the page anyway. Four pages shipped an empty "Node
+// Documentation" section that way. So every miss here throws, and a rename stops the
+// build instead of quietly emptying a page.
//
// The FlowFuse-authored half is `src/_includes/core-nodes/-use-case.md`: a short
// "why you would reach for this node" intro that exists for every node. That is the part
@@ -75,22 +81,59 @@ export function listNodes (coreNodes) {
}
/**
- * Re-fetch every node's help from upstream and rewrite the snapshot.
+ * Fetch every node's help from the Node-RED repo.
+ *
+ * Uses the global fetch and nothing else: `scripts/sync_docs.mjs` runs this before
+ * `npm install` in CI, so it cannot reach for a dependency.
*
- * Run deliberately (`node scripts/refresh_core_node_help.mjs`), never from a build. A node
- * whose help cannot be fetched throws rather than being written empty: a silently
- * help-less page looks fine in CI and is useless to a reader.
+ * Two failures are told apart on purpose. A transport failure or a 5xx is transient and
+ * retried, because one bad minute at GitHub should not fail a deploy. A 404, or a file
+ * that does not contain the help name the catalogue asked for, is a real mismatch that no
+ * retry will fix, so it throws immediately and names both the node and the URL.
+ *
+ * One locale file often serves several nodes, so responses are fetched once per file and
+ * reused. That is 21 requests for 38 nodes.
*/
-export async function refreshCoreNodeHelp ({ coreNodes, fetchImpl = fetch } = {}) {
+export async function fetchCoreNodeHelp ({ coreNodes, fetchImpl = fetch, retries = 3, delay = 500 } = {}) {
+ const nodes = listNodes(coreNodes)
+ const files = new Map()
+
+ for (const url of new Set(nodes.map(n => `${UPSTREAM}/${n.category}/${n.file}.html`))) {
+ let lastError
+ for (let attempt = 1; attempt <= retries; attempt++) {
+ try {
+ const res = await fetchImpl(url)
+ if (res.status === 404) throw new Error(`${url} returned 404; the catalogue points at a file upstream no longer has`)
+ if (!res.ok) { lastError = new Error(`${url} returned ${res.status}`); }
+ else { files.set(url, await res.text()); lastError = null; break }
+ } catch (err) {
+ if (/404/.test(err.message)) throw err
+ lastError = err
+ }
+ if (attempt < retries) await new Promise(r => setTimeout(r, delay * attempt))
+ }
+ if (lastError) throw new Error(`Could not fetch core node help: ${lastError.message}`)
+ }
+
const help = {}
- for (const node of listNodes(coreNodes)) {
+ const missing = []
+ for (const node of nodes) {
const url = `${UPSTREAM}/${node.category}/${node.file}.html`
- const res = await fetchImpl(url)
- if (!res.ok) throw new Error(`${node.name}: ${url} returned ${res.status}`)
- const extracted = extractHelp(await res.text(), node.xpath)
- if (!extracted) throw new Error(`${node.name}: no data-help-name="${node.xpath}" in ${url}`)
+ const extracted = extractHelp(files.get(url), node.xpath)
+ if (!extracted) missing.push(`${node.name} (wanted data-help-name="${node.xpath}" in ${node.category}/${node.file}.html)`)
help[`${node.category}/${node.slug}`] = extracted
}
+
+ // Reported together rather than one at a time: when upstream reorganises a locale
+ // file, several nodes move at once and one error per run makes that take several runs
+ // to discover.
+ if (missing.length) {
+ throw new Error(
+ 'Upstream help not found for ' + missing.length + ' core node(s):\n ' + missing.join('\n ') +
+ '\nUpstream renamed or moved these help blocks. Fix src/_data/coreNodes.json.'
+ )
+ }
+
return help
}
@@ -149,6 +192,9 @@ ${list}
* gitignored and wiped on every sync - the same place guides-sync writes to, and the
* reason these generated pages are not committed. Writing them into content-guides/
* instead would dirty the working tree on every build.
+ *
+ * Takes `help` rather than fetching it, so the whole tree-writing half stays synchronous
+ * and testable without a network stub.
*/
export function syncCoreNodes ({ repoRoot, nuxtRoot, coreNodes, help, logger = console }) {
const outDir = join(nuxtRoot, 'content', 'docs', 'node-red', 'core-nodes')
@@ -157,15 +203,19 @@ export function syncCoreNodes ({ repoRoot, nuxtRoot, coreNodes, help, logger = c
if (missing.length) {
throw new Error(
- 'No mirrored help for: ' + missing.map(n => n.name).join(', ') +
- '. Run scripts/refresh_core_node_help.mjs.'
+ 'No help fetched for: ' + missing.map(n => n.name).join(', ') +
+ '. fetchCoreNodeHelp should have thrown before this point.'
)
}
mkdirSync(outDir, { recursive: true })
- // Section index.
- writeFileSync(join(outDir, 'README.md'), `---
+ // `index.md`, not `README.md`. The README convention belongs to the sync layer:
+ // guides-sync's destinationFor renames it on the way into the content tree. This
+ // writes straight into that tree, so it has to use the name @nuxt/content and the
+ // prerender collector expect. Getting this wrong produced routes like
+ // /docs/node-red/core-nodes/README/ that prerendered as 404s and failed the build.
+ writeFileSync(join(outDir, 'index.md'), `---
title: "Node-RED core nodes"
navTitle: "Core nodes"
navOrder: 3
@@ -191,7 +241,7 @@ ${Object.entries(CATEGORIES)
if (!inCategory.length) continue
mkdirSync(join(outDir, category), { recursive: true })
- writeFileSync(join(outDir, category, 'README.md'),
+ writeFileSync(join(outDir, category, 'index.md'),
renderCategoryPage(category, inCategory), 'utf8')
for (const node of inCategory) {
diff --git a/nuxt/lib/core-nodes-sync.test.mjs b/nuxt/lib/core-nodes-sync.test.mjs
index 831d0afe59..d4d4c7804f 100644
--- a/nuxt/lib/core-nodes-sync.test.mjs
+++ b/nuxt/lib/core-nodes-sync.test.mjs
@@ -6,8 +6,8 @@ import test from 'node:test'
import {
extractHelp,
+ fetchCoreNodeHelp,
listNodes,
- refreshCoreNodeHelp,
renderCoreNodePage,
slugFor,
syncCoreNodes,
@@ -55,19 +55,69 @@ test('the catalogue flattens to nodes carrying their category and slug', () => {
assert.deepEqual(nodes.map(n => `${n.category}/${n.slug}`), ['common/inject', 'network/mqtt-in'])
})
-test('a refresh that cannot find a node throws instead of storing nothing', async () => {
- const fetchImpl = async () => ({ ok: true, text: async () => '' })
+const ok = (body) => async () => ({ ok: true, status: 200, text: async () => body })
+
+test('a fetch that cannot find the help name fails the build and names every miss', async () => {
+ // The regression this guards: the Eleventy version got an empty node-set here, joined
+ // it to '' and published the page anyway. Four pages shipped with no help that way.
+ const fetchImpl = ok('')
await assert.rejects(
- () => refreshCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl }),
- /no data-help-name/
+ () => fetchCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl }),
+ (err) => /Upstream help not found for 2 core node\(s\)/.test(err.message)
+ && /Inject/.test(err.message) && /MQTT In/.test(err.message)
)
})
-test('a refresh propagates an upstream HTTP failure', async () => {
- const fetchImpl = async () => ({ ok: false, status: 404 })
+test('a 404 is a real mismatch and is not retried', async () => {
+ let calls = 0
+ const fetchImpl = async () => { calls++; return { ok: false, status: 404 } }
+
+ await assert.rejects(() => fetchCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl, delay: 0 }), /404/)
+ assert.equal(calls, 1, 'a 404 never becomes a success, so retrying only slows the build')
+})
+
+test('a transient 5xx is retried and then succeeds', async () => {
+ let calls = 0
+ const fetchImpl = async () => {
+ calls++
+ return calls === 1
+ ? { ok: false, status: 503 }
+ : { ok: true, status: 200, text: async () => '' }
+ }
+
+ const help = await fetchCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl, delay: 0 })
+
+ assert.equal(help['common/inject'], 'i')
+ assert.ok(calls > 1, 'one bad minute at GitHub must not fail a deploy')
+})
+
+test('a persistent 5xx gives up and fails the build', async () => {
+ const fetchImpl = async () => ({ ok: false, status: 502 })
+
+ await assert.rejects(
+ () => fetchCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl, retries: 2, delay: 0 }),
+ /Could not fetch core node help.*502/s
+ )
+})
- await assert.rejects(() => refreshCoreNodeHelp({ coreNodes: CATALOGUE, fetchImpl }), /404/)
+test('one locale file serving several nodes is fetched once', async () => {
+ const shared = {
+ network: [
+ { xpath: 'mqtt in', name: 'MQTT In', file: '10-mqtt' },
+ { xpath: 'mqtt out', name: 'MQTT Out', file: '10-mqtt' },
+ ],
+ }
+ let calls = 0
+ const fetchImpl = async () => {
+ calls++
+ return { ok: true, status: 200, text: async () => '' }
+ }
+
+ const help = await fetchCoreNodeHelp({ coreNodes: shared, fetchImpl, delay: 0 })
+
+ assert.equal(calls, 1)
+ assert.deepEqual(help, { 'network/mqtt-in': 'i', 'network/mqtt-out': 'o' })
})
test('a page leads with the FlowFuse use case and puts the mirrored help under its own heading', () => {
@@ -94,7 +144,7 @@ test('a sync missing any node help fails loudly and names the node', () => {
help: { 'common/inject': 'x' },
logger: { info () {} },
}),
- /MQTT In.*refresh_core_node_help/s
+ /No help fetched for.*MQTT In/s
)
})
@@ -114,7 +164,10 @@ test('a full sync writes a page per node plus the section and category indexes',
const entries = readdirSync(dir).sort()
assert.equal(count, 2)
- assert.deepEqual(entries, ['README.md', 'common', 'inject.md', 'mqtt-in.md', 'network'])
- assert.match(readFileSync(join(dir, 'README.md'), 'utf8'), /navGroup|navTitle: "Core nodes"/)
- assert.match(readFileSync(join(dir, 'common/README.md'), 'utf8'), /\/docs\/node-red\/core-nodes\/inject\//)
+ // index.md, not README.md: this writes straight into the content tree rather than
+ // through guides-sync, which is what renames README on the way in. README.md here
+ // prerenders as /docs/node-red/core-nodes/README/ and 404s the build.
+ assert.deepEqual(entries, ['common', 'index.md', 'inject.md', 'mqtt-in.md', 'network'])
+ assert.match(readFileSync(join(dir, 'index.md'), 'utf8'), /navTitle: "Core nodes"/)
+ assert.match(readFileSync(join(dir, 'common/index.md'), 'utf8'), /\/docs\/node-red\/core-nodes\/inject\//)
})
diff --git a/nuxt/modules/docs-source.ts b/nuxt/modules/docs-source.ts
index b91da456e2..d2346fd82c 100644
--- a/nuxt/modules/docs-source.ts
+++ b/nuxt/modules/docs-source.ts
@@ -9,7 +9,7 @@ import { syncDocs } from '../lib/docs-sync.mjs'
// @ts-ignore same
import { syncGuides } from '../lib/guides-sync.mjs'
// @ts-ignore same
-import { syncCoreNodes } from '../lib/core-nodes-sync.mjs'
+import { fetchCoreNodeHelp, syncCoreNodes } from '../lib/core-nodes-sync.mjs'
const logger = useLogger('docs-source')
@@ -40,15 +40,11 @@ export default defineNuxtModule({
await syncDocs({ repoRoot, nuxtRoot, logger })
syncGuides({ repoRoot, nuxtRoot, logger })
// The core-node pages were never files: under Eleventy they were paginated out of
- // coreNodes.json. They are generated from that catalogue plus the committed help
- // snapshot, so a deploy never reaches out to raw.githubusercontent.com.
- syncCoreNodes({
- repoRoot,
- nuxtRoot,
- coreNodes: JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8')),
- help: JSON.parse(readFileSync(join(nuxtRoot, 'lib', 'core-node-help.json'), 'utf8')),
- logger,
- })
+ // coreNodes.json, each fetching its help from the Node-RED repo. They still fetch
+ // per build, so the help is never a stale copy, but a node whose help cannot be
+ // found now fails the build instead of rendering an empty section.
+ const coreNodes = JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8'))
+ syncCoreNodes({ repoRoot, nuxtRoot, coreNodes, help: await fetchCoreNodeHelp({ coreNodes }), logger })
if (!existsSync(contentDocsDir)) return
diff --git a/scripts/refresh_core_node_help.mjs b/scripts/refresh_core_node_help.mjs
deleted file mode 100644
index 400234cf99..0000000000
--- a/scripts/refresh_core_node_help.mjs
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env node
-// Re-fetches every core node's built-in help from the Node-RED project and rewrites the
-// committed snapshot at nuxt/lib/core-node-help.json.
-//
-// Run this deliberately, when upstream's help has changed. It is NOT part of the build:
-// the /docs/node-red/core-nodes/** pages are generated from the snapshot so a deploy
-// never depends on raw.githubusercontent.com being reachable.
-//
-// node scripts/refresh_core_node_help.mjs
-//
-// Then commit the diff to nuxt/lib/core-node-help.json alongside whatever prompted it.
-
-import { readFileSync, writeFileSync } from 'node:fs'
-import { dirname, join } from 'node:path'
-import { fileURLToPath } from 'node:url'
-
-import { refreshCoreNodeHelp } from '../nuxt/lib/core-nodes-sync.mjs'
-
-const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
-const coreNodes = JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8'))
-const dest = join(repoRoot, 'nuxt/lib/core-node-help.json')
-
-const help = await refreshCoreNodeHelp({ coreNodes })
-const keys = Object.keys(help).sort()
-const sorted = Object.fromEntries(keys.map(k => [k, help[k]]))
-
-writeFileSync(dest, `${JSON.stringify(sorted, null, 2)}\n`, 'utf8')
-console.log(`Wrote help for ${keys.length} nodes to nuxt/lib/core-node-help.json`)
diff --git a/scripts/sync_docs.mjs b/scripts/sync_docs.mjs
index 9223a0fbd6..77fd48aabf 100644
--- a/scripts/sync_docs.mjs
+++ b/scripts/sync_docs.mjs
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url'
import { syncDocs } from '../nuxt/lib/docs-sync.mjs'
import { syncGuides } from '../nuxt/lib/guides-sync.mjs'
-import { syncCoreNodes } from '../nuxt/lib/core-nodes-sync.mjs'
+import { fetchCoreNodeHelp, syncCoreNodes } from '../nuxt/lib/core-nodes-sync.mjs'
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
@@ -17,9 +17,5 @@ const nuxtRoot = join(repoRoot, 'nuxt')
await syncDocs({ repoRoot, nuxtRoot })
// After syncDocs, which wipes the tree it writes into.
syncGuides({ repoRoot, nuxtRoot })
-syncCoreNodes({
- repoRoot,
- nuxtRoot,
- coreNodes: JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8')),
- help: JSON.parse(readFileSync(join(nuxtRoot, 'lib', 'core-node-help.json'), 'utf8')),
-})
+const coreNodes = JSON.parse(readFileSync(join(repoRoot, 'src/_data/coreNodes.json'), 'utf8'))
+syncCoreNodes({ repoRoot, nuxtRoot, coreNodes, help: await fetchCoreNodeHelp({ coreNodes }) })
From d989be258abffd63e04ca625d368eae7e7a45a73 Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 18:10:09 +0200
Subject: [PATCH 05/17] docs: point the rest of the site at the library's new
/docs URLs
The link checker caught what the migration missed. rewriteLibraryPaths only ran
over the pages being moved, so every link INTO the library from elsewhere still
pointed at /node-red/**: blog posts, customer stories, the integrations pages,
the AI page, the handbook. Those paths 301 now, but the redirects are Nitro route
rules rather than files, so hyperlink checks the built output, finds nothing at
the old path, and fails the build. Anchored links failed twice over.
The substitution has to be anchored on a delimiter that cannot appear inside a
URL. Two earlier attempts got this wrong in different ways:
- Without a (?
-const installVideoUrl = '/node-red/getting-started/library/images/installing-node-red-node.webm'
+const installVideoUrl = '/docs/node-red/getting-started/library/images/installing-node-red-node.webm'
const installPngUrl = '/images/integrations/palette-manager-install.png'
@@ -21,7 +21,7 @@ const installPngUrl = '/images/integrations/palette-manager-install.png'
Install in Node-RED via the
ONNX vision models inside flows next to the machine, with camera ingest over RTSP, for inference that works offline and keeps data on your network. Detection results flow into the same logic as any other signal, so you can trigger alerts or actions from what the model sees. Running at the edge means no round trip to the cloud and no image data leaving the plant.` },
- { name: 'Use any model within flows', diagram: 'any-model', description: `Certified LLM nodes bring OpenAI, Anthropic, Gemini or local models via Ollama into any flow with your own keys. Choose the provider that fits each task, or keep everything on local models when data cannot leave your network. Because you supply the keys, model access and spend stay under your control.` },
+ { name: 'Automated visual inspection', diagram: 'visual-inspection', description: `Run ONNX vision models inside flows next to the machine, with camera ingest over RTSP, for inference that works offline and keeps data on your network. Detection results flow into the same logic as any other signal, so you can trigger alerts or actions from what the model sees. Running at the edge means no round trip to the cloud and no image data leaving the plant.` },
+ { name: 'Use any model within flows', diagram: 'any-model', description: `Certified LLM nodes bring OpenAI, Anthropic, Gemini or local models via Ollama into any flow with your own keys. Choose the provider that fits each task, or keep everything on local models when data cannot leave your network. Because you supply the keys, model access and spend stay under your control.` },
],
},
{
@@ -118,7 +118,7 @@ const CAPABILITY_GROUPS = [
eyebrow: 'Expose',
subtitle: 'The other direction. Your flows become tools that an agent can call as part of its work.',
items: [
- { name: 'AI that uses your tools', diagram: 'your-tools', externalNote: 'Anyone can build them. For external agents, whether they can call them depends on the agent.', description: `Build your own MCP servers and let Insights-mode agents call your tools and services as part of a workflow. Wrap an internal API or system as a tool once, then let agents use it wherever it fits. The agent stays inside the workflow you designed, calling only the tools you register.` },
+ { name: 'AI that uses your tools', diagram: 'your-tools', externalNote: 'Anyone can build them. For external agents, whether they can call them depends on the agent.', description: `Build your own MCP servers and let Insights-mode agents call your tools and services as part of a workflow. Wrap an internal API or system as a tool once, then let agents use it wherever it fits. The agent stays inside the workflow you designed, calling only the tools you register.` },
],
},
]
diff --git a/nuxt/pages/integrations/opcua.vue b/nuxt/pages/integrations/opcua.vue
index 7ca434d32c..402f0b779a 100644
--- a/nuxt/pages/integrations/opcua.vue
+++ b/nuxt/pages/integrations/opcua.vue
@@ -48,42 +48,42 @@ const capabilities = [
title: 'Structured Information Model',
description: 'Browse a real address space of objects, types, and relationships instead of a flat register list. Data arrives with a name and structure attached, not a bare tag number.',
linkText: 'Browse & explore the address space',
- url: '/node-red/flowfuse/edge/opcua/#12.-browse',
+ url: '/docs/flowfuse-nodes/edge/opcua/#12.-browse',
},
{
icon: 'i-heroicons-bolt',
title: 'Real-Time Subscriptions',
description: 'Subscribe to nodes and get notified only when values change. No polling every tag on a fixed interval, no wasted controller scan budget.',
linkText: 'Monitor node reference',
- url: '/node-red/flowfuse/edge/opcua/#10.-monitor',
+ url: '/docs/flowfuse-nodes/edge/opcua/#10.-monitor',
},
{
icon: 'i-heroicons-clock',
title: 'Historical Access & Logging',
description: 'Read a server\'s native History (HA) service, or log live values into InfluxDB or TimescaleDB. Trends stay queryable long after the PLC buffer rolls over.',
linkText: 'History Read node reference',
- url: '/node-red/flowfuse/edge/opcua/#14.-history-read',
+ url: '/docs/flowfuse-nodes/edge/opcua/#14.-history-read',
},
{
icon: 'i-heroicons-bell-alert',
title: 'Alarms & Events',
description: 'Subscribe to condition-based alarms natively, filtered by type and severity on the server side. Stop polling fault bits and reconstructing state changes after the fact.',
linkText: 'Monitor Event node reference',
- url: '/node-red/flowfuse/edge/opcua/#11.-monitor-event',
+ url: '/docs/flowfuse-nodes/edge/opcua/#11.-monitor-event',
},
{
icon: 'i-heroicons-shield-check',
title: 'Security Built Into the Spec',
description: 'X.509 certificate authentication, user authentication, and SignAndEncrypt come with the standard. Manage the certificate trust list yourself instead of leaving it on vendor defaults.',
linkText: 'Configure a secure connection',
- url: '/node-red/flowfuse/edge/opcua/#5.-configure-a-connection',
+ url: '/docs/flowfuse-nodes/edge/opcua/#5.-configure-a-connection',
},
{
icon: 'i-heroicons-server-stack',
title: 'Client + Server, One Runtime',
description: 'Read and write a third-party server as a client. On self-hosted FlowFuse, expose your own information model as a server, side by side.',
linkText: 'Hosting an OPC UA server',
- url: '/node-red/flowfuse/edge/opcua/#16.-hosting-an-opc-ua-server',
+ url: '/docs/flowfuse-nodes/edge/opcua/#16.-hosting-an-opc-ua-server',
},
]
@@ -93,21 +93,21 @@ const protocols = [
title: 'Modbus',
description: 'TCP, UDP, and Serial (RTU/ASCII), running right next to your OPC UA connections. Read and write coils and registers, or simulate a server for testing.',
linkText: 'Modbus Certified Node',
- url: '/node-red/flowfuse/edge/modbus/',
+ url: '/docs/flowfuse-nodes/edge/modbus/',
},
{
icon: 'i-heroicons-cpu-chip',
title: 'EtherNet/IP (CIP)',
description: 'Native Rockwell and Allen-Bradley connectivity: ControlLogix, CompactLogix, Micro800, SLC500, and other CIP-capable devices, normalized into the same data model as your OPC UA connection.',
linkText: 'CIP Suite Certified Node',
- url: '/node-red/flowfuse/edge/cip-suite/',
+ url: '/docs/flowfuse-nodes/edge/cip-suite/',
},
{
icon: 'i-heroicons-camera',
title: 'RTSP Video',
description: 'Turn factory-floor camera feeds into images your flow can act on. Join a frame with the OPC UA values read at that instant, so a quality event is recorded with both the picture and the process conditions behind it.',
linkText: 'RTSP Certified Node',
- url: '/node-red/flowfuse/edge/rtsp/',
+ url: '/docs/flowfuse-nodes/edge/rtsp/',
},
{
icon: 'i-heroicons-server-stack',
diff --git a/src/_data/certifiedNodes.js b/src/_data/certifiedNodes.js
index f482974870..d9faffe5fa 100644
--- a/src/_data/certifiedNodes.js
+++ b/src/_data/certifiedNodes.js
@@ -29,7 +29,7 @@ const CATALOGUES = [
rather than the FlowFuse documentation page. Entries here win over `url`.
*/
const DOCS_PATH_OVERRIDES = {
- "@flowfuse-certified-nodes/opcua": "/node-red/flowfuse/edge/opcua/",
+ "@flowfuse-certified-nodes/opcua": "/docs/flowfuse-nodes/edge/opcua/",
};
/*
@@ -100,11 +100,11 @@ module.exports = async function () {
version: module.version,
collection: catalogue.collection,
collectionLabel: catalogue.label,
- collectionIndex: `/node-red/flowfuse/${catalogue.collection}/`,
+ collectionIndex: `/docs/flowfuse-nodes/${catalogue.collection}/`,
docsPath:
DOCS_PATH_OVERRIDES[module.id] ||
docsPathFromCatalogueUrl(module.url) ||
- `/node-red/flowfuse/${catalogue.collection}/`,
+ `/docs/flowfuse-nodes/${catalogue.collection}/`,
};
byId[node.id] = node;
all.push(node);
diff --git a/src/_includes/core-nodes/batch-use-case.md b/src/_includes/core-nodes/batch-use-case.md
index 2a5fda3be4..dc7415c5f3 100644
--- a/src/_includes/core-nodes/batch-use-case.md
+++ b/src/_includes/core-nodes/batch-use-case.md
@@ -22,7 +22,7 @@ The overlap option lets you repeat messages between batches. When enabled, messa
Groups all messages that arrive within a specified time period. Set it to 2 seconds and every message received in that window gets batched together. When the interval expires, the batch releases and a new window starts. You can optionally configure the node to send an empty message if nothing arrives within the interval.
-
+
### Concatenate Sequences
diff --git a/src/_includes/core-nodes/debug-use-case.md b/src/_includes/core-nodes/debug-use-case.md
index 1aae775b9e..0520e525a1 100644
--- a/src/_includes/core-nodes/debug-use-case.md
+++ b/src/_includes/core-nodes/debug-use-case.md
@@ -50,19 +50,19 @@ The node can be enabled or disabled without modifying the flow. A disabled Debug
The Copy Path feature lets you quickly copy the property path of any value in the debug output. This is a real time saver when building Change or Function nodes, helping you avoid typos and errors. Simply click the small icon next to any property in the debug output to copy its full path.
-
+
### Copy Value
Copy Value gives you an exact copy of any property value to use in Inject, Change, or Function nodes. This is extremely useful when you need to simulate real data or when sharing example flows with others for troubleshooting. Click the value in the debug output and select Copy Value from the menu.
-
+
### Pin Open
When debug output contains many nested properties, Pin Open helps you keep specific items expanded while collapsing others. This makes it easy to focus on the data you care about without losing your place as new messages arrive.
-
+
## Examples
diff --git a/src/_includes/core-nodes/delay-use-case.md b/src/_includes/core-nodes/delay-use-case.md
index ccf5ca8630..24f0260f25 100644
--- a/src/_includes/core-nodes/delay-use-case.md
+++ b/src/_includes/core-nodes/delay-use-case.md
@@ -32,7 +32,7 @@ There's situations however where the queue needs to be cleared. This is done by
When a `reset` property is set, the queue will be empty.
This is useful for when the lack of an event might need to send a notification. Schedule the notification
-to be sent but allow a positive event to cancel the notification. In the example below; say you want to turn off the lights if no movement was detected for a period of time, you set the delay node to that time. When there's motion detected, you than send a `msg.reset` message to the delay node to cancel them turning of the light. This depends on an inject node set to [send a message on an interval](/node-red/core-nodes/inject/#run-a-flow-daily-at-midnight) to turn the lights off.
+to be sent but allow a positive event to cancel the notification. In the example below; say you want to turn off the lights if no movement was detected for a period of time, you set the delay node to that time. When there's motion detected, you than send a `msg.reset` message to the delay node to cancel them turning of the light. This depends on an inject node set to [send a message on an interval](/docs/node-red/core-nodes/inject/#run-a-flow-daily-at-midnight) to turn the lights off.
{% renderFlow %}
[{"id":"06b93157ff07c9b1","type":"delay","z":"e512003df3c971c7","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":460,"y":100,"wires":[["f345c902d09aaf76"]]},{"id":"1965e58562943d69","type":"inject","z":"e512003df3c971c7","name":"Motion detected","props":[{"p":"reset","v":"true","vt":"bool"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":250,"y":160,"wires":[["06b93157ff07c9b1"]]},{"id":"f345c902d09aaf76","type":"debug","z":"e512003df3c971c7","name":"Never receive a message","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":670,"y":100,"wires":[]},{"id":"834f0812a01be7a4","type":"inject","z":"e512003df3c971c7","name":"Turn off lights","props":[{"p":"payload"}],"repeat":"10","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":100,"wires":[["06b93157ff07c9b1"]]}]
diff --git a/src/_includes/core-nodes/html-use-case.md b/src/_includes/core-nodes/html-use-case.md
index 773ed3f98e..423bce325b 100644
--- a/src/_includes/core-nodes/html-use-case.md
+++ b/src/_includes/core-nodes/html-use-case.md
@@ -2,7 +2,7 @@ Extracts elements from an HTML document.
## Where and why do we use the HTML node?
-The HTML node parses HTML documents and extracts specific elements using CSS selectors. This is essential when you need to scrape data from web pages, extract specific content from HTML responses, or process HTML documents to retrieve structured information. Unlike the [template node](/node-red/core-nodes/template/) which generates HTML, this node is purely for parsing and extraction.
+The HTML node parses HTML documents and extracts specific elements using CSS selectors. This is essential when you need to scrape data from web pages, extract specific content from HTML responses, or process HTML documents to retrieve structured information. Unlike the [template node](/docs/node-red/core-nodes/template/) which generates HTML, this node is purely for parsing and extraction.
## How it works
diff --git a/src/_includes/core-nodes/http-request-use-case.md b/src/_includes/core-nodes/http-request-use-case.md
index a9eac59df8..23dc272303 100644
--- a/src/_includes/core-nodes/http-request-use-case.md
+++ b/src/_includes/core-nodes/http-request-use-case.md
@@ -14,7 +14,7 @@ Below, you'll find a range of settings to tailor HTTP requests to fit the needs
- **Ignore:** If enabled Payload will be ignored.
- **Append to query-string parameter:** Enabling this option will Allow sending URL query string parameters using `msg.payload`.
- **Send as request:** Send payload data as part of the request body.
-- **Enable Secure Connection:** Allows to activate SSL/TLS for secure communication. TLS configuration options are available, For more information refer to [TLS config node](/node-red/core-nodes/tls).
+- **Enable Secure Connection:** Allows to activate SSL/TLS for secure communication. TLS configuration options are available, For more information refer to [TLS config node](/docs/node-red/core-nodes/tls).
- **Use Authentication:** If required, allow to provide credentials for authentication.
- **Type:** Select the authentication type.
- **basic:** Uses Basic authentication where the username and password are sent in the request headers in Base64-encoded form.
@@ -24,7 +24,7 @@ Below, you'll find a range of settings to tailor HTTP requests to fit the needs
- **bearer:** Uses Bearer token authentication where a bearer token, typically a JSON Web Token (JWT), is sent in the Authorization header.
- **Token:** Provide the bearer token if bearer authentication is selected.
- **Enable Connection Keep-Alive:** Enabling this option will allow Maintain persistent connections for efficiency.
-- **Use Proxy:** Allows to Route requests through a proxy server if necessary, for more information on the configuration of [HTTP Proxy](/node-red/core-nodes/http-proxy/) config node
+- **Use Proxy:** Allows to Route requests through a proxy server if necessary, for more information on the configuration of [HTTP Proxy](/docs/node-red/core-nodes/http-proxy/) config node
- **Only send non-2xx responses to Catch node:** Enabling this option will send only non-success responses to the Catch node.
- **Disable Strict HTTP Parsing:** Enabling this option relaxes how Node-RED interprets HTTP responses. It's handy when dealing with responses that don't perfectly match the standard HTTP format.
- **Return:** Allows to Choose the format for response data conversion
@@ -37,7 +37,7 @@ Below, you'll find a range of settings to tailor HTTP requests to fit the needs
1. **API Integration:** The HTTP request node allows seamless integration with external APIs. Developers can utilize it to fetch data from APIs using GET requests or send data to APIs using POST/PUT requests. For instance, fetching weather data from a weather API or posting data to a messaging service like Slack are common scenarios.
-2. **Webhooks:** With the HTTP request node, users can set up webhooks to trigger actions in response to specific events. This enables real-time communication between different applications. For example, triggering a webhook to notify a third-party service when certain conditions are met or when data is received from a sensor. For more information, refer to [Using webhook with Node-RED](/node-red/integration-technologies/webhook/)
+2. **Webhooks:** With the HTTP request node, users can set up webhooks to trigger actions in response to specific events. This enables real-time communication between different applications. For example, triggering a webhook to notify a third-party service when certain conditions are met or when data is received from a sensor. For more information, refer to [Using webhook with Node-RED](/docs/node-red/integration-technologies/webhook/)
3. **Remote Control and Device Management:** In smart home systems, the HTTP request node can be used to facilitates remote device management. It allows users to control various devices such as lights, thermostats, and security cameras via web or mobile interfaces by interacting with device APIs. Actions like toggling devices, adjusting settings, and receiving real-time updates can be achieved through the HTTP request node.
diff --git a/src/_includes/core-nodes/mqtt-broker-config-use-case.md b/src/_includes/core-nodes/mqtt-broker-config-use-case.md
index d77662908c..c32b49b9a4 100644
--- a/src/_includes/core-nodes/mqtt-broker-config-use-case.md
+++ b/src/_includes/core-nodes/mqtt-broker-config-use-case.md
@@ -13,7 +13,7 @@ The MQTT-Broker config node in Node-RED is used to define the connection setting
- **MQTT V3.1.1 (legacy)**
- **MQTT V3.1.1**
- **MQTT V5**
-- **Use TLS**: Enabling this option node will use TLS, for more information TLS refer to [TLS node docs](/node-red/core-nodes/tls/)
+- **Use TLS**: Enabling this option node will use TLS, for more information TLS refer to [TLS node docs](/docs/node-red/core-nodes/tls/)
- **Client ID**: The client identifier to use when connecting to the broker. If left blank, a random ID will be generated.
- **Keep Alive**: This setting defines the interval (in seconds) at which the MQTT client sends a "keep alive" message to the broker to maintain the connection. It ensures that the connection remains active even if there is no other data being transmitted. If you set this to 0, the client will not send keep-alive messages.
- **Session**:
diff --git a/src/_includes/core-nodes/tcp-in-use-case.md b/src/_includes/core-nodes/tcp-in-use-case.md
index 2c0f0521c8..c1bb66ff62 100644
--- a/src/_includes/core-nodes/tcp-in-use-case.md
+++ b/src/_includes/core-nodes/tcp-in-use-case.md
@@ -13,7 +13,7 @@ TCP (Transmission Control Protocol) is one of the core protocols of the Internet
- **Connect to:** Enables the TCP node to act as a client, establishing connections to remote servers.
- **Hostname:** Specifies the hostname or IP address of the remote server when using the "Connect to" type.
- **Port:** Specifies the TCP port number to listen on (when using "Listen on" type) or to connect to (when using "Connect to" type).
-- **Enable secure (SSL/TLS) connection:** Enabling this option activates SSL/TLS for secure communication. In Node-RED we have TLS config node which allows to activate TLS secure communication, refer to [TLS config node](/node-red/core-nodes/tls/) for details on configurations.
+- **Enable secure (SSL/TLS) connection:** Enabling this option activates SSL/TLS for secure communication. In Node-RED we have TLS config node which allows to activate TLS secure communication, refer to [TLS config node](/docs/node-red/core-nodes/tls/) for details on configurations.
- **Output:**
- **Streams of:** Selecting this option will outputs the data stream received from the TCP connection as a continuous stream of messages.
- **Single:** Selecting this option will outputs a single message containing the data received from the TCP connection.
diff --git a/src/_includes/core-nodes/tpc-out-use-case.md b/src/_includes/core-nodes/tpc-out-use-case.md
index 9d34eefee5..43c7498e62 100644
--- a/src/_includes/core-nodes/tpc-out-use-case.md
+++ b/src/_includes/core-nodes/tpc-out-use-case.md
@@ -2,7 +2,7 @@
TCP-Out nodes in Node-RED facilitate communication over TCP/IP networks by providing options to connect to remote TCP ports, accept incoming connections, or reply to messages received from a TCP In node.
-If you dont know what is TCP, refer to [What is TCP](/node-red/core-nodes/tcp-in/#what-is-udp/).
+If you dont know what is TCP, refer to [What is TCP](/docs/node-red/core-nodes/tcp-in/#what-is-udp/).
## Configuring TCP-Out Node
@@ -16,7 +16,7 @@ To configure a TCP-Out node in Node-RED, follow these steps:
2. **Port**: Specify the port number for the TCP connection.
-3. **Enable Secure (SSL/TLS) Connection**: Activate SSL/TLS for secure communication. If enabled, Node-RED provides a TLS config node for configuring TLS settings. Refer to the [TLS config node](/node-red/core-nodes/tls/) documentation for details on configurations.
+3. **Enable Secure (SSL/TLS) Connection**: Activate SSL/TLS for secure communication. If enabled, Node-RED provides a TLS config node for configuring TLS settings. Refer to the [TLS config node](/docs/node-red/core-nodes/tls/) documentation for details on configurations.
4. **Close Connection After Each Message is Sent**: Choose whether to close the connection after sending each message.
diff --git a/src/_includes/core-nodes/udp-out-use-case.md b/src/_includes/core-nodes/udp-out-use-case.md
index 17e6729ace..6e6791f932 100644
--- a/src/_includes/core-nodes/udp-out-use-case.md
+++ b/src/_includes/core-nodes/udp-out-use-case.md
@@ -2,7 +2,7 @@
The udp-out node in Node-RED is a node used for sending UDP messages to a specified network destination. When you add a udp-out node to your Node-RED flow, you configure it with the IP address and port of the destination device or service. Then, any messages received by the udp-out node are sent as UDP packets to that destination. This node is particularly useful for applications where real-time communication or lightweight message transmission is required.
-For more information on UDP refer to [What is UDP](/node-red/core-nodes/udp-in/#what-is-udp%3F)
+For more information on UDP refer to [What is UDP](/docs/node-red/core-nodes/udp-in/#what-is-udp%3F)
## Configuring UDP-Out Node
diff --git a/src/_includes/core-nodes/unknown-use-case.md b/src/_includes/core-nodes/unknown-use-case.md
index c5048c5abe..c1c15a200f 100644
--- a/src/_includes/core-nodes/unknown-use-case.md
+++ b/src/_includes/core-nodes/unknown-use-case.md
@@ -24,7 +24,7 @@ Access the Palette Manager through the Node-RED menu to see all installed packag
The System Info dialog provides a comprehensive list of installed node packages that you can view and copy for documentation.
-
+
## Migrating flows to FlowFuse
diff --git a/src/blog/2023/06/community-news-06.md b/src/blog/2023/06/community-news-06.md
index 0a3a1c47ed..a5bdaee0c3 100644
--- a/src/blog/2023/06/community-news-06.md
+++ b/src/blog/2023/06/community-news-06.md
@@ -37,7 +37,7 @@ On June 27, FlowFuse is doing a webinar with our friends at InfluxDB. A great op
- [Bringing High Availability to Node-RED](/blog/2023/05/bringing-high-availability-to-node-red/) - FlowFuse CTO discusses the strategy for delivering high availability in the FlowFuse platform.
- Two articles featuring how to connect Modbus data with Node-RED:
- - [Using Node-RED to Visualize Industrial Production Data via Modbus](/node-red/protocol/modbus/)
+ - [Using Node-RED to Visualize Industrial Production Data via Modbus](/docs/node-red/protocol/modbus/)
- [Best Practices Integrating a Modbus Device With Node-RED](/blog/2023/05/integrating modbus with node-red/)
- [Node-RED Tips - Dashboard Edition](/blog/2023/06/3-quick-node-red-tips-7/) - A new set of Node-RED quick tips that are focused on using Node-RED Dashboard.
diff --git a/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md b/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md
index 9749dbe1f3..0988dbcf31 100644
--- a/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md
+++ b/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md
@@ -197,7 +197,7 @@ Depending on how noisy the REAL data is, which is common with unfiltered 4-20mA

-In the example above, we arbitrarily applied a 3% [deadband](/node-red/core-nodes/filter/)
+In the example above, we arbitrarily applied a 3% [deadband](/docs/node-red/core-nodes/filter/)
to the `Robot_Position` value, which means that the value must change by greater than or equal to 3% compared to the last input value, or else the data will be discarded before being sent to the stacklight PLC.
You can adjust the deadband to find the right balance for your particular application.
diff --git a/src/blog/2023/07/community-news-07.md b/src/blog/2023/07/community-news-07.md
index d50bff5d4a..9778bdbab5 100644
--- a/src/blog/2023/07/community-news-07.md
+++ b/src/blog/2023/07/community-news-07.md
@@ -35,11 +35,11 @@ Our next webinar will be focused on the device management capabilities in the Fl
- [Node-RED as a No-Code Ethernet/IP to S7 Protocol Converter](/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/) - A guide to using Node-RED for converting ethernet IP data to Siemens S7. Also a [video version](https://youtu.be/dteXgcBXUnk) of the same content.
-- [MQTT and its Role in IoT and Industrial IoT](/node-red/protocol/mqtt/) - A practical explainer on the role of MQTT in IoT use cases and how to connect with an MQTT broker in Node-RED.
+- [MQTT and its Role in IoT and Industrial IoT](/docs/node-red/protocol/mqtt/) - A practical explainer on the role of MQTT in IoT use cases and how to connect with an MQTT broker in Node-RED.
- Two new Node-RED Nodes Explained articles
- - [Nodes explained: Split](/node-red/core-nodes/split/)
- - [Nodes explained: Filter](/node-red/core-nodes/filter/)
+ - [Nodes explained: Split](/docs/node-red/core-nodes/split/)
+ - [Nodes explained: Filter](/docs/node-red/core-nodes/filter/)
## From the Community
diff --git a/src/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red.md b/src/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red.md
index f48fca5552..ed8f90cf8a 100644
--- a/src/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red.md
+++ b/src/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red.md
@@ -67,7 +67,7 @@ meta:
answer: "The complete flow source code for all four objectives, browsing, reading, writing, and events, is included inline within the article, ready to import directly into Node-RED."
tldr: "This part of the OPC UA series shows how to build an interactive Node-RED dashboard that connects to a third-party OPC UA server to browse the address space, read and write values, and display alarms and events. Complete flow source code is provided, making it a practical starting point for production OPC UA client applications."
---
-This article is the third and final part of our OPC UA content series. In the [first article](/blog/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/), we cover some OPC UA fundamentals and walk through an example OPC UA Server flow. In the [second article](/node-red/protocol/opc-ua/), we built a SSL-secured OPC UA server using data from an Allen Bradley PLC as a source.
+This article is the third and final part of our OPC UA content series. In the [first article](/blog/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/), we cover some OPC UA fundamentals and walk through an example OPC UA Server flow. In the [second article](/docs/node-red/protocol/opc-ua/), we built a SSL-secured OPC UA server using data from an Allen Bradley PLC as a source.
In this article, we show how to build an OPC Client in Node-RED that communicates with a 3rd party OPC UA Server and utilizes an interactive dashboard.
@@ -288,7 +288,7 @@ So, you've successfully built your Node-RED application, congratulations! But no
Enter [FlowFuse](/), a cloud-based platform designed to add production-grade features to your Node-RED applications. With FlowFuse, you can seamlessly integrate advanced security measures, scale your application as needed, and collaborate effortlessly with your team. It simplifies management and deployment, turning your Node-RED project into a robust, scalable solution.
-If you're interested in learning how to use Node-RED for professional use cases, check out our eBook: [Ultimate Beginner's Guide to Professionals](/ebooks/beginner-guide-to-a-professional-nodered/). For additional resources, visit our [Node-RED Learning Resources section](/node-red/core-nodes/), where you can explore integrations with different protocols, messaging services, databases, hardware, and much more.
+If you're interested in learning how to use Node-RED for professional use cases, check out our eBook: [Ultimate Beginner's Guide to Professionals](/ebooks/beginner-guide-to-a-professional-nodered/). For additional resources, visit our [Node-RED Learning Resources section](/docs/node-red/core-nodes/), where you can explore integrations with different protocols, messaging services, databases, hardware, and much more.
## Conclusion
diff --git a/src/blog/2023/08/community-news-08.md b/src/blog/2023/08/community-news-08.md
index 726de76302..72866c6051 100644
--- a/src/blog/2023/08/community-news-08.md
+++ b/src/blog/2023/08/community-news-08.md
@@ -31,7 +31,7 @@ OPC-UA is a popular communication protocol used to communicate industrial data b
- Our developer advocate, Richard Meyer, published a series of articles on OPC-UA:
- Part 1: [How to Deploy a Basic OPC-UA Server in Node-RED](/blog/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/)
- - Part 2: [How to Build a Secure OPC-UA Server for PLCs in Node-RED](/node-red/protocol/opc-ua/)
+ - Part 2: [How to Build a Secure OPC-UA Server for PLCs in Node-RED](/docs/node-red/protocol/opc-ua/)
- Part 3: [How to Build an OPC UA Client Dashboard in Node-RED](/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/)
diff --git a/src/blog/2023/10/community-news-10.md b/src/blog/2023/10/community-news-10.md
index d1fad8e9eb..7485b3aeb7 100644
--- a/src/blog/2023/10/community-news-10.md
+++ b/src/blog/2023/10/community-news-10.md
@@ -49,7 +49,7 @@ The next generation of the Node-RED dashboard is starting to mature. Development
* [Share & Preview Flows on flows.nodered.org](/blog/2023/09/flow-viewer/) - A new way to visualize flows in web pages.
-* [Charting REST API Data in a Dashboard](/node-red/integration-technologies/rest/) - A short tutorial on how to gather data from a REST API for a dashboard.
+* [Charting REST API Data in a Dashboard](/docs/node-red/integration-technologies/rest/) - A short tutorial on how to gather data from a REST API for a dashboard.
* Modernize your legacy industrial data - Two part series on making sense of your industrial data.
- [Part 1](/blog/2023/09/modernize-your-legacy-industrial-data/)
diff --git a/src/blog/2023/10/custom-vuetify-components-dashboard.md b/src/blog/2023/10/custom-vuetify-components-dashboard.md
index ba1039f0b0..ae9a20123d 100644
--- a/src/blog/2023/10/custom-vuetify-components-dashboard.md
+++ b/src/blog/2023/10/custom-vuetify-components-dashboard.md
@@ -40,7 +40,7 @@ generated for you, in my case it's:
### Using the template node
-Like the [template core node](/node-red/core-nodes/template), the dashboard package
+Like the [template core node](/docs/node-red/core-nodes/template), the dashboard package
comes with [a template node of its own](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html).
If we take the HTML from the Vuetify docs pages and copy it in a template node
the spinner will show up on the dashboard.
@@ -52,7 +52,7 @@ the spinner will show up on the dashboard.
While a custom element on a page is cool, and shows you can inject arbitrary HTML
on a Dashboard, it's even better if we could make the element dynamic. So let's
start with a first dynamic element. The quickest way to get that done is have
-an [`Inject`](/node-red/core-nodes/inject) node output a random number every second.
+an [`Inject`](/docs/node-red/core-nodes/inject) node output a random number every second.
So let's hook up an Inject, with `msg.payload`'s output being a JSONata expression
`$round($random() * 100)` to generate a random number. And let's make sure it
@@ -82,7 +82,7 @@ This is mostly a programmers job, but it's not hard, so let's get to it. A butto
would be great to reset the timer, and for the sake of this post we can hardcode
the deadline to 1m from the button press.
-When dragging in a button node, connect it to a [change](/node-red/core-nodes/change)
+When dragging in a button node, connect it to a [change](/docs/node-red/core-nodes/change)
node. In the change node set the flow variable `flow.deadline` to the timestamp. The
Inject node from earlier needs updating to inject the `flow.deadline`. All that's
left is calculating how many seconds passed, and normalizing 60 seconds to the
diff --git a/src/blog/2023/11/community-news-11.md b/src/blog/2023/11/community-news-11.md
index e06ced8fe3..830502cc54 100644
--- a/src/blog/2023/11/community-news-11.md
+++ b/src/blog/2023/11/community-news-11.md
@@ -59,7 +59,7 @@ Explore building manufacturing applications with FlowFuse Blueprints in our upco
* [Meet FlowFuse at SPS Nuremberg](/blog/2023/11/meet-us-at-sps-nuremberg/) - Talk about Node-RED and how FlowFuse can help you operationalize your flows!
-* [Install the FlowFuse Edge Agent on the Raspberry Pi 5](/node-red/hardware/raspberry-pi-5) - Managing your Raspberry Pi 5 with Node-RED through FlowFuse is easy to set up
+* [Install the FlowFuse Edge Agent on the Raspberry Pi 5](/docs/node-red/hardware/raspberry-pi-5) - Managing your Raspberry Pi 5 with Node-RED through FlowFuse is easy to set up
* [Innovate from within - Why manufacturing must embrace Citizen Developers](/blog/2023/10/citizen-development/) - Empower your Operational Technology teams as Citizen Developers
diff --git a/src/blog/2024/01/unified-namespace-what-broker.md b/src/blog/2024/01/unified-namespace-what-broker.md
index 0970214521..141e3608ec 100644
--- a/src/blog/2024/01/unified-namespace-what-broker.md
+++ b/src/blog/2024/01/unified-namespace-what-broker.md
@@ -39,13 +39,13 @@ In addition to vendor lock-in, cloud message queue brokers also introduce relian
### Exotic options
-RabbitMQ is a widely used open-source message broker that’s mostly used as an event message bus for web applications. It can also function as a hub in a unified namespace. The broker primarily supports the [AMQP](/node-red/protocol/amqp/) (Advanced Message Queuing Protocol), considered the industry standard for high-performance messaging systems. It also supports STOMP (Streaming Text Oriented Messaging Protocol) and MQTT (MQ Telemetry Transport), catering to various messaging needs.
+RabbitMQ is a widely used open-source message broker that’s mostly used as an event message bus for web applications. It can also function as a hub in a unified namespace. The broker primarily supports the [AMQP](/docs/node-red/protocol/amqp/) (Advanced Message Queuing Protocol), considered the industry standard for high-performance messaging systems. It also supports STOMP (Streaming Text Oriented Messaging Protocol) and MQTT (MQ Telemetry Transport), catering to various messaging needs.
NATS, short for Network Agnostic Messaging System, is another open-source message broker that is designed for simplicity and reliability. NATS implements its own protocols, making it harder to be interoperable with hardware and software previously purchased. NATS has requirements on message structure too, which creates another barrier to adoption for IoT use cases.
## How Node-RED Helps
-Node-RED provides a powerful and flexible way to integrate with various brokers, supporting protocols such as [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), [Kafka](/blog/2024/03/using-kafka-with-node-red/), and [AMQP](/node-red/protocol/amqp/). It allows you to build and manage workflows that interact with your chosen broker, seamlessly connecting different data sources and systems.
+Node-RED provides a powerful and flexible way to integrate with various brokers, supporting protocols such as [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), [Kafka](/blog/2024/03/using-kafka-with-node-red/), and [AMQP](/docs/node-red/protocol/amqp/). It allows you to build and manage workflows that interact with your chosen broker, seamlessly connecting different data sources and systems.
However, using Node-RED alone in production environments requires additional considerations, such as server deployment, instance management, security implementation, and scalability. This is where FlowFuse enhances Node-RED's capabilities by adding production-ready features. FlowFuse simplifies managing and deploying Node-RED applications, providing essential functionalities like scalability, robust security, and efficient collaboration tools.
diff --git a/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md b/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md
index 47c859b511..20982f38a7 100644
--- a/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md
+++ b/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md
@@ -148,7 +148,7 @@ _Screenshot displaying the change node retrieving expense data from global conte
{data-zoomable}
_Screenshot displaying the change node converting amount to number_
-5. Drag a **Join** node onto the canvas, select mode as **reduced expression**, and set the **Reduce exp** to `$A + payload.amount`. Set Initial value to `0`, and **Fix-up exp** to `$A`. Give this **join** node the name "Calculate total". This function operates similarly to using the javascript reduce method on an array to calculate the sum of its values. `$A` stores the accumulated value, and with every incoming message payload, it adds the `payload.amount` value to it, for more details on this refer to the [core node docs on join node](/node-red/core-nodes/join/).
+5. Drag a **Join** node onto the canvas, select mode as **reduced expression**, and set the **Reduce exp** to `$A + payload.amount`. Set Initial value to `0`, and **Fix-up exp** to `$A`. Give this **join** node the name "Calculate total". This function operates similarly to using the javascript reduce method on an array to calculate the sum of its values. `$A` stores the accumulated value, and with every incoming message payload, it adds the `payload.amount` value to it, for more details on this refer to the [core node docs on join node](/docs/node-red/core-nodes/join/).
{data-zoomable}
_Screenshot displaying the join node calculating the total income and expense data_
diff --git a/src/blog/2024/05/understanding-node-flow-global-environment-variables-in-node-red.md b/src/blog/2024/05/understanding-node-flow-global-environment-variables-in-node-red.md
index 381c12ac4c..39e726a23d 100644
--- a/src/blog/2024/05/understanding-node-flow-global-environment-variables-in-node-red.md
+++ b/src/blog/2024/05/understanding-node-flow-global-environment-variables-in-node-red.md
@@ -49,7 +49,7 @@ Variables in Node-RED serve as containers for storing and managing data througho
Node-RED offers three primary variable categories:
-**Message variables** travel with the message object as it flows through your nodes. The most common example is `msg.payload`, which carries the primary data between nodes. For a deeper dive into message handling, see the [Understanding Node-RED Messages](/node-red/getting-started/node-red-messages/) guide.
+**Message variables** travel with the message object as it flows through your nodes. The most common example is `msg.payload`, which carries the primary data between nodes. For a deeper dive into message handling, see the [Understanding Node-RED Messages](/docs/node-red/getting-started/node-red-messages/) guide.
**Context variables** store application state at different levels, node, flow, or global scope. They persist data that needs to be accessed across multiple message events, making them ideal for tracking counters, storing configuration, or maintaining state.
diff --git a/src/blog/2024/06/how-to-use-mqtt-in-node-red.md b/src/blog/2024/06/how-to-use-mqtt-in-node-red.md
index af62446b49..721b9f2784 100644
--- a/src/blog/2024/06/how-to-use-mqtt-in-node-red.md
+++ b/src/blog/2024/06/how-to-use-mqtt-in-node-red.md
@@ -77,7 +77,7 @@ Before you start, make sure you have:
Node-RED ships with MQTT nodes already installed. Open your Node-RED editor and look in the palette, you'll find **mqtt-in** and **mqtt-out** under the network section.
-> If you're using FlowFuse's managed MQTT broker, this is straightforward. Use the [ff-mqtt-in](/node-red/flowfuse/mqtt/mqtt-in/) and [ff-mqtt-out](/node-red/flowfuse/mqtt/mqtt-out/) nodes instead of the standard MQTT nodes. Simply drag one onto the canvas, and the connection to FlowFuse's broker will be configured automatically.
+> If you're using FlowFuse's managed MQTT broker, this is straightforward. Use the [ff-mqtt-in](/docs/flowfuse-nodes/mqtt/mqtt-in/) and [ff-mqtt-out](/docs/flowfuse-nodes/mqtt/mqtt-out/) nodes instead of the standard MQTT nodes. Simply drag one onto the canvas, and the connection to FlowFuse's broker will be configured automatically.
For any other broker, drag an **mqtt-in** or **mqtt-out** node onto your workspace. Double-click it to open the configuration panel.
diff --git a/src/blog/2024/07/calling-python-script-from-node-red.md b/src/blog/2024/07/calling-python-script-from-node-red.md
index c9759b6843..a1e5e97c05 100644
--- a/src/blog/2024/07/calling-python-script-from-node-red.md
+++ b/src/blog/2024/07/calling-python-script-from-node-red.md
@@ -103,7 +103,7 @@ Let's now see how to call a Python script from Node-RED. First, we'll create a b
```
::
-Now, let's execute this Python script from Node-RED. To do that, we will use Node-RED's [Exec](/node-red/core-nodes/exec/) node, which allows running commands on your system.
+Now, let's execute this Python script from Node-RED. To do that, we will use Node-RED's [Exec](/docs/node-red/core-nodes/exec/) node, which allows running commands on your system.
1. Drag an Inject node onto the canvas.
2. Drag an Exec node onto the canvas and Configure the command to `python ./example.py -u`. The -u flag prevents potential output buffering issues when executing Python scripts via exec.
@@ -125,7 +125,7 @@ Now, when you deploy this flow and click on the inject node to execute the file,
Having explored how to run a Python script within Node-RED with the basic practical example, let's move to a real-world scenario. We'll demonstrate how to read sensor data using Python, despite Node-RED providing numerous community-built nodes for this purpose. This approach provides deeper insights into integrating external scripts, showcasing the flexibility of Node-RED for custom solutions.
-Before proceeding, ensure that Node-RED is running on a device connected to a temperature sensor. For detailed instructions, refer to [Setting Up Node-RED on Different Hardware](/node-red/hardware/), In this case, we are running Node-RED on a Raspberry Pi 5 with a DHT11 sensor connected to it.
+Before proceeding, ensure that Node-RED is running on a device connected to a temperature sensor. For detailed instructions, refer to [Setting Up Node-RED on Different Hardware](/docs/node-red/hardware/), In this case, we are running Node-RED on a Raspberry Pi 5 with a DHT11 sensor connected to it.
1. Drag an Inject node onto the canvas, and set repeat to 1 seconds of interval.
2. Drag an Exec node and set the path to `python .py`, replace the filename with the name of the file which reads the sensor data, and make sure the python file doesn't contain the loop.
diff --git a/src/blog/2024/08/opc-ua-to-mqtt-with-node-red.md b/src/blog/2024/08/opc-ua-to-mqtt-with-node-red.md
index a4e166badd..b8509e2063 100644
--- a/src/blog/2024/08/opc-ua-to-mqtt-with-node-red.md
+++ b/src/blog/2024/08/opc-ua-to-mqtt-with-node-red.md
@@ -190,13 +190,13 @@ _Video showing the successful bridging of OPC UA data to MQTT_
- [OPC UA Client and Server on FlowFuse](/integrations/opcua/)
See FlowFuse's full OPC UA client and server capabilities, certified node, and security model in one place.
-- [Using MQTT with Node-RED](/node-red/protocol/mqtt/)
+- [Using MQTT with Node-RED](/docs/node-red/protocol/mqtt/)
Learn how to integrate MQTT with Node-RED to enhance your IoT solutions with real-time data messaging.
- [How to Build an OPC UA Client Dashboard in Node-RED](/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/)
Follow a step-by-step guide to create a comprehensive OPC UA client dashboard in Node-RED for effective monitoring and control.
-- [Building a Secure OPC UA Server in Node-RED](/node-red/protocol/opc-ua/)
+- [Building a Secure OPC UA Server in Node-RED](/docs/node-red/protocol/opc-ua/)
Explore best practices for configuring a secure OPC UA server in Node-RED to ensure safe and reliable data exchange.
- [How to Deploy a Basic OPC UA Server in Node-RED](/blog/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/)
diff --git a/src/blog/2024/10/exploring-flowfuse-project-nodes.md b/src/blog/2024/10/exploring-flowfuse-project-nodes.md
index a8df1675f5..491151b560 100644
--- a/src/blog/2024/10/exploring-flowfuse-project-nodes.md
+++ b/src/blog/2024/10/exploring-flowfuse-project-nodes.md
@@ -137,7 +137,7 @@ This example showcases one of many powerful use cases for FlowFuse project nodes
## Using Project Call Nodes
-**Project Call** nodes are ideal for triggering flows deployed on another Node-RED instance and retrieving the final result as a response. While they function similarly to [webhooks](/node-red/integration-technologies/webhook/), they utilize [MQTT](/node-red/protocol/mqtt/) as their underlying mechanism instead of HTTP. In this section, we will demonstrate the use of a **Project Call** node through an example of making an on-demand temperature request.
+**Project Call** nodes are ideal for triggering flows deployed on another Node-RED instance and retrieving the final result as a response. While they function similarly to [webhooks](/docs/node-red/integration-technologies/webhook/), they utilize [MQTT](/docs/node-red/protocol/mqtt/) as their underlying mechanism instead of HTTP. In this section, we will demonstrate the use of a **Project Call** node through an example of making an on-demand temperature request.
### How Project Call Nodes Work
@@ -175,7 +175,7 @@ Throughout this section, we will explore how to utilize the **Project Call** nod
```
::
-1. Copy/download the flow above and import/upload it into your Raspberry Pi Node-RED instance. Ensure you have correctly interfaced the DHT11 sensor with your Raspberry Pi. For more information, refer to our guide on [Setting Up Node-RED on Raspberry Pi 4](/node-red/hardware/raspberry-pi-4/), which explains how to install the device agent on the Raspberry Pi and read temperature data from the DHT11 sensor.
+1. Copy/download the flow above and import/upload it into your Raspberry Pi Node-RED instance. Ensure you have correctly interfaced the DHT11 sensor with your Raspberry Pi. For more information, refer to our guide on [Setting Up Node-RED on Raspberry Pi 4](/docs/node-red/hardware/raspberry-pi-4/), which explains how to install the device agent on the Raspberry Pi and read temperature data from the DHT11 sensor.
2. Deploy the flow.
Once you deploy the flow, you will see the temperature data displayed on the Debug panel if everything was done correctly. Now, let’s add the project nodes to trigger the temperature reading flow on demand and retrieve the data accordingly.
diff --git a/src/blog/2024/10/quick-ways-to-write-functions-in-node-red.md b/src/blog/2024/10/quick-ways-to-write-functions-in-node-red.md
index ac0b722c85..6fa7ad4e99 100644
--- a/src/blog/2024/10/quick-ways-to-write-functions-in-node-red.md
+++ b/src/blog/2024/10/quick-ways-to-write-functions-in-node-red.md
@@ -28,7 +28,7 @@ _Image showing the function node_
## What Are Function Nodes and Common Challenges?
-[Function Nodes](/node-red/core-nodes/function/) in Node-RED allow you to write custom JavaScript for processing messages. While they provide flexibility, many users find it challenging to turn complex business rules into code and manage variables. For more details on the benefits and drawbacks of using Function Nodes, refer to this [Article](/blog/2023/03/why-should-you-use-node-red-function-nodes/).
+[Function Nodes](/docs/node-red/core-nodes/function/) in Node-RED allow you to write custom JavaScript for processing messages. While they provide flexibility, many users find it challenging to turn complex business rules into code and manage variables. For more details on the benefits and drawbacks of using Function Nodes, refer to this [Article](/blog/2023/03/why-should-you-use-node-red-function-nodes/).
Before using Function Nodes, consider if existing low-code nodes can fulfill your needs. Using standard low-code nodes can simplify your approach and enhance collaboration and clarity. If you still feel the need to use Function Nodes, don’t worry, In the following section, we’ll explore straightforward strategies to make working with Function Nodes easier.
diff --git a/src/blog/2024/11/building-uns-with-flowfuse.md b/src/blog/2024/11/building-uns-with-flowfuse.md
index 1ca9810104..6fb4c51e4c 100644
--- a/src/blog/2024/11/building-uns-with-flowfuse.md
+++ b/src/blog/2024/11/building-uns-with-flowfuse.md
@@ -40,7 +40,7 @@ Fortunately, Node-RED provides support for a wide range of industrial protocols,
By leveraging Node-RED, you can collect data from these legacy systems, process and transform the data using low-code workflows, and then seamlessly send it to the cloud via modern protocols such as MQTT, Kafka, AMQP, and more.
-In our example, Node-RED can directly collect metrics from sensor using [I2C](https://flows.nodered.org/node/node-red-contrib-i2c) on the Raspberry Pi. This approach simplifies the process by eliminating the need for additional communication layers. To run Node-RED on the Raspberry Pi, we use [FlowFuse Device Agent](/platform/device-agent/), This agent enables you to remotely monitor, manage, and build Node-RED flows securely through the FlowFuse platform remotely. [See here](/node-red/hardware/) for more details on how to set up and run FlowFuse Device Agent on different devices.
+In our example, Node-RED can directly collect metrics from sensor using [I2C](https://flows.nodered.org/node/node-red-contrib-i2c) on the Raspberry Pi. This approach simplifies the process by eliminating the need for additional communication layers. To run Node-RED on the Raspberry Pi, we use [FlowFuse Device Agent](/platform/device-agent/), This agent enables you to remotely monitor, manage, and build Node-RED flows securely through the FlowFuse platform remotely. [See here](/docs/node-red/hardware/) for more details on how to set up and run FlowFuse Device Agent on different devices.
### Step 2: Transform and Process the Collected metrics
@@ -60,7 +60,7 @@ Raw data from the ADXL345 sensor might look like this:
[26,0,244,255,37,255]
```
-Using a [Function node](/node-red/core-nodes/function/) in Node-RED, we can convert this into a human-readable format.
+Using a [Function node](/docs/node-red/core-nodes/function/) in Node-RED, we can convert this into a human-readable format.
{data-zoomable}
_Function node: Transforming Raw Data into Readable Format_
@@ -80,7 +80,7 @@ While this is more readable, it can still be challenging to monitor changes in v
{data-zoomable}
_Change node: Calculating Magnitude_
-After calculating the Magnitude using a [Change node](/node-red/core-nodes/change/), the data might look like this:
+After calculating the Magnitude using a [Change node](/docs/node-red/core-nodes/change/), the data might look like this:
```json
{
@@ -160,7 +160,7 @@ While Sparkplug B provides a standardized topic model, its hierarchy may not alw
### Step 5: Sending Collected metrics to UNS
-With your topic naming convention chosen, it’s time to send the data to the UNS. In Node-RED, we will use the [MQTT Out](/node-red/core-nodes/mqtt-in/) node to send the transformed data to the broker.
+With your topic naming convention chosen, it’s time to send the data to the UNS. In Node-RED, we will use the [MQTT Out](/docs/node-red/core-nodes/mqtt-in/) node to send the transformed data to the broker.
1. Drag an MQTT out node into your flow.
2. Configure the node to connect to the FlowFuse MQTT Broker using the client credentials generated earlier.
diff --git a/src/blog/2024/11/why-pub-sub-in-uns.md b/src/blog/2024/11/why-pub-sub-in-uns.md
index 817e63e04d..c189f8f6f6 100644
--- a/src/blog/2024/11/why-pub-sub-in-uns.md
+++ b/src/blog/2024/11/why-pub-sub-in-uns.md
@@ -32,7 +32,7 @@ Before discussing why Publish/Subscribe (Pub/Sub) is essential for a UNS, let's
{data-zoomable}
_Publish Subscribe Model_
-The Pub/Sub model is a way for systems to communicate where one component, called the publisher, sends messages to a central system ( Broker such as [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), [RabitMQ](/node-red/protocol/amqp/), and [Kafka](/blog/2024/03/using-kafka-with-node-red/) ), and other components, called subscribers, receive those messages. The publisher doesn’t need to know who the subscribers are, and the subscribers don’t know who the publishers are. The central system, or broker, ensures the right messages go to the right subscribers based on their interests.
+The Pub/Sub model is a way for systems to communicate where one component, called the publisher, sends messages to a central system ( Broker such as [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), [RabitMQ](/docs/node-red/protocol/amqp/), and [Kafka](/blog/2024/03/using-kafka-with-node-red/) ), and other components, called subscribers, receive those messages. The publisher doesn’t need to know who the subscribers are, and the subscribers don’t know who the publishers are. The central system, or broker, ensures the right messages go to the right subscribers based on their interests.
Additionally, it’s important to note that the roles of publisher and subscriber are not mutually exclusive. A component can act as a publisher in one context, sending messages to the broker, and as a subscriber in another, receiving messages from the broker.
diff --git a/src/blog/2024/12/publishing-modbus-data-to-uns.md b/src/blog/2024/12/publishing-modbus-data-to-uns.md
index a471bba9d5..a8bf6aefc8 100644
--- a/src/blog/2024/12/publishing-modbus-data-to-uns.md
+++ b/src/blog/2024/12/publishing-modbus-data-to-uns.md
@@ -130,7 +130,7 @@ After configuring the connection, it's time to test the data collection.
If no data appears, check the connection settings (IP address, Unit ID, port, etc.) and ensure your Modbus device is correctly configured and accessible. If you use a simulator like ModSim, ensure it’s running and properly configured to send data.
-For more information on using Modbus with Node-RED, please read our tutorial on [Using Modbus with Node-RED](/node-red/protocol/modbus/).
+For more information on using Modbus with Node-RED, please read our tutorial on [Using Modbus with Node-RED](/docs/node-red/protocol/modbus/).
If you prefer a video explanation, [Kurt Braun](https://www.linkedin.com/in/wago-kurt-braun/) from WAGO demonstrates how to collect Modbus data using Node-RED in FlowFuse:
@@ -161,7 +161,7 @@ We must convert these raw register values into human-readable formats for cloud
To determine how to process raw Modbus data, such as dividing by a specific value, concatenating, or applying other transformation formulas, refer to the manual of the sensor you use for specific instructions.
-In Node-RED, you can use various nodes for transformation. You can choose the [Function node](/node-red/core-nodes/function/) for advanced processing, the [Change node](/node-red/core-nodes/change/) for simpler operations, or the [Template node](/node-red/core-nodes/template/) for defining schemas. For more complex data parsing scenarios, such as handling multiple data types (floats, 32-bit integers, strings), dealing with big-endian/little-endian conversions, or performing byte swapping, consider using the `node-red-contrib-buffer-parser` node. This specialized node simplifies parsing Modbus buffers into various data formats without writing custom code. Learn more in our guide on [using Buffer Parser for industrial data](/blog/2025/12/node-red-buffer-parser-industrial-data/). In this article, I will demonstrate a low-code approach using the Change node to process the data cleanly.
+In Node-RED, you can use various nodes for transformation. You can choose the [Function node](/docs/node-red/core-nodes/function/) for advanced processing, the [Change node](/docs/node-red/core-nodes/change/) for simpler operations, or the [Template node](/docs/node-red/core-nodes/template/) for defining schemas. For more complex data parsing scenarios, such as handling multiple data types (floats, 32-bit integers, strings), dealing with big-endian/little-endian conversions, or performing byte swapping, consider using the `node-red-contrib-buffer-parser` node. This specialized node simplifies parsing Modbus buffers into various data formats without writing custom code. Learn more in our guide on [using Buffer Parser for industrial data](/blog/2025/12/node-red-buffer-parser-industrial-data/). In this article, I will demonstrate a low-code approach using the Change node to process the data cleanly.
Additionally, for better organization and accessibility, I will send each metric separately and include additional metadata such as the `timestamp` and `unit`.
@@ -288,7 +288,7 @@ _MQTT Out node configuration for temperature data_
**Using FlowFuse MQTT Nodes**
-If you're using the FlowFuse MQTT broker, FlowFuse provides specialized MQTT nodes that simplify configuration. These nodes automatically configure the broker connection details when you drop them onto the canvas, no manual setup of server address, Client ID, username, or password required. You also won't need to manually create clients in the broker; they're automatically created when you use these nodes. This streamlines the development process and reduces configuration errors. Learn more about [FlowFuse MQTT nodes](/node-red/flowfuse/mqtt/).
+If you're using the FlowFuse MQTT broker, FlowFuse provides specialized MQTT nodes that simplify configuration. These nodes automatically configure the broker connection details when you drop them onto the canvas, no manual setup of server address, Client ID, username, or password required. You also won't need to manually create clients in the broker; they're automatically created when you use these nodes. This streamlines the development process and reduces configuration errors. Learn more about [FlowFuse MQTT nodes](/docs/flowfuse-nodes/mqtt/).
**Step 3.3: Configure MQTT Nodes for Remaining Metrics**
diff --git a/src/blog/2025/01/mqtt-frontrunner-for-uns.md b/src/blog/2025/01/mqtt-frontrunner-for-uns.md
index 538c790ca0..32755595e4 100644
--- a/src/blog/2025/01/mqtt-frontrunner-for-uns.md
+++ b/src/blog/2025/01/mqtt-frontrunner-for-uns.md
@@ -21,7 +21,7 @@ In this first part of our series, we’ll explain why MQTT is the standout choic
The [Unified Namespace (UNS)](/use-cases/uns/) is a data architecture (not just a tool or new technology) that centralizes and organizes data from various sources into a single, unified structure. It eliminates data silos by providing a standardized way to represent, access, and share information across different devices, systems, and services. For more information on what is UNS, read our article: [Introduction to the Unified Namespace](/blog/2023/12/introduction-to-unified-namespace/).
-When choosing a broker for your UNS, it's crucial to consider how well the selected broker fits the specific requirements of your IIoT environment, including the types of devices and systems involved, as well as factors like scalability, reliability, and ease of integration. Several options are available alongside MQTT, including [AMQP](/node-red/protocol/amqp/), [Kafka](/blog/2024/03/using-kafka-with-node-red/), and cloud message brokers like AWS Kinesis and GCP Pub/Sub. While these alternatives offer unique features, MQTT stands out, and we’ll explain why later in this article. But If you’re interested in a brief overview of why these alternatives are not the best fit for UNS, check out our article: [Unified Namespace: What Broker to Use?](/blog/2024/01/unified-namespace-what-broker/)
+When choosing a broker for your UNS, it's crucial to consider how well the selected broker fits the specific requirements of your IIoT environment, including the types of devices and systems involved, as well as factors like scalability, reliability, and ease of integration. Several options are available alongside MQTT, including [AMQP](/docs/node-red/protocol/amqp/), [Kafka](/blog/2024/03/using-kafka-with-node-red/), and cloud message brokers like AWS Kinesis and GCP Pub/Sub. While these alternatives offer unique features, MQTT stands out, and we’ll explain why later in this article. But If you’re interested in a brief overview of why these alternatives are not the best fit for UNS, check out our article: [Unified Namespace: What Broker to Use?](/blog/2024/01/unified-namespace-what-broker/)
There is also an ongoing debate regarding the use of OPC-UA and other protocols for implementing UNS. While I won’t dive into this in detail here as i havent explored it much yet, I encourage you to start with the approach we call reverse engineering. First, understand why MQTT is the preferred choice for implementing UNS, and then explore how its features align with UNS needs. From there, you can evaluate whether other protocols offer similar capabilities. By following this process, you will find the right answer of your questions.
@@ -32,7 +32,7 @@ There is also an ongoing debate regarding the use of OPC-UA and other protocols
Before diving into its specific advantages for UNS, let’s take a brief look at the history of **MQTT** and how it became the backbone of modern IoT communication.
-[MQTT](/node-red/protocol/mqtt/) was developed in the late 1990s by Andy Stanford-Clark at IBM and Arlen Nipper at Eurotech to address communication challenges in low-bandwidth, unreliable networks. This early focus on lightweight messaging paved the way for MQTT to become a pioneering solution for the rapidly expanding IoT space.
+[MQTT](/docs/node-red/protocol/mqtt/) was developed in the late 1990s by Andy Stanford-Clark at IBM and Arlen Nipper at Eurotech to address communication challenges in low-bandwidth, unreliable networks. This early focus on lightweight messaging paved the way for MQTT to become a pioneering solution for the rapidly expanding IoT space.
Since then, MQTT has evolved significantly. The protocol has gone through several iterations, from MQTT 3.1.1 to the more feature-rich MQTT 5.0, each version enhancing the protocol's capabilities to meet the demands of an increasingly connected world. Today, more than 25 years later, MQTT remains the **de facto protocol** for IoT applications, and its simplicity, scalability, and reliability continue to make it an ideal choice for industrial systems.
diff --git a/src/blog/2025/04/build-manufacturing-oee-dashboard.md b/src/blog/2025/04/build-manufacturing-oee-dashboard.md
index 853b068407..f071104869 100644
--- a/src/blog/2025/04/build-manufacturing-oee-dashboard.md
+++ b/src/blog/2025/04/build-manufacturing-oee-dashboard.md
@@ -79,7 +79,7 @@ Before you begin building the OEE Dashboard with FlowFuse, make sure you have th
- **Running FlowFuse Instance:** Make sure you have a FlowFuse instance set up and running. If you don't have an account, check out our [free trial](https://app.flowfuse.com/account/create) and learn how to create an instance in FlowFuse.
- **FlowFuse Dashboard:** Ensure you have [FlowFuse Dashboard](https://flows.nodered.org/node/@flowfuse/node-red-dashboard) (also known as Node-RED Dashboard 2.0 in the community) installed and properly configured on your instance.
-- **SQLite Contrib Node:** Ensure you have [node-red-contrib-sqlite](https://flows.nodered.org/node/node-red-node-sqlite) installed. We use SQLite here because it needs no setup, but the flow works against any database - [FlowFuse Tables](/docs/user/ff-tables/) gives you managed PostgreSQL if you want something production-grade from the start, and our [Database](/node-red/database/) guides cover MongoDB, InfluxDB, TimescaleDB, and DynamoDB.
+- **SQLite Contrib Node:** Ensure you have [node-red-contrib-sqlite](https://flows.nodered.org/node/node-red-node-sqlite) installed. We use SQLite here because it needs no setup, but the flow works against any database - [FlowFuse Tables](/docs/user/ff-tables/) gives you managed PostgreSQL if you want something production-grade from the start, and our [Database](/docs/node-red/database/) guides cover MongoDB, InfluxDB, TimescaleDB, and DynamoDB.
### Preparing Simulated Data
diff --git a/src/blog/2025/04/design-and-scale-oee-dashboard.md b/src/blog/2025/04/design-and-scale-oee-dashboard.md
index 25311c0600..c87815ff59 100644
--- a/src/blog/2025/04/design-and-scale-oee-dashboard.md
+++ b/src/blog/2025/04/design-and-scale-oee-dashboard.md
@@ -271,7 +271,7 @@ To make the dashboard truly useful in a live setting, you must understand how to
2. Replace the existing `sqlite` nodes in your flow with the nodes for the database you are using.
3. If using SQL based database, queries may remain unchanged. For NoSQL or time-series DBs, rewrite the queries as needed.
-For help, refer to our [Database](/node-red/database/) section, which includes guides for MongoDB, PostgreSQL, InfluxDB, TimescaleDB, and DynamoDB. If you do not already have a database to point at, [FlowFuse Tables](/docs/user/ff-tables/) gives you a managed PostgreSQL instance inside the platform - we walk through that pattern end to end in [Building a historical data dashboard with FlowFuse Tables](/blog/2025/08/time-series-dashboard-flowfuse-postgresql/).
+For help, refer to our [Database](/docs/node-red/database/) section, which includes guides for MongoDB, PostgreSQL, InfluxDB, TimescaleDB, and DynamoDB. If you do not already have a database to point at, [FlowFuse Tables](/docs/user/ff-tables/) gives you a managed PostgreSQL instance inside the platform - we walk through that pattern end to end in [Building a historical data dashboard with FlowFuse Tables](/blog/2025/08/time-series-dashboard-flowfuse-postgresql/).
When connecting to your real data source, you may notice that the field names used in your database differ from those used in the our oee dashboard sqlite node queries. While this seems like a lot of manual work, the dashboard is designed with flexibility in mind. You only need to make two changes to adapt the queries to your schema.
diff --git a/src/blog/2025/06/data-acquisition-for-mes.md b/src/blog/2025/06/data-acquisition-for-mes.md
index dba90e0af7..d65c2ba4e6 100644
--- a/src/blog/2025/06/data-acquisition-for-mes.md
+++ b/src/blog/2025/06/data-acquisition-for-mes.md
@@ -64,14 +64,14 @@ Following are some of the most commonly used protocol nodes:
- **Modbus:**
- **OPC UA:**
- **OPC DA:**
-- **MQTT:** [/node-red/core-nodes/mqtt-in/](/node-red/core-nodes/mqtt-in/)
+- **MQTT:** [/docs/node-red/core-nodes/mqtt-in/](/docs/node-red/core-nodes/mqtt-in/)
- **Ethernet/IP:**
- **Siemens S7:**
- **MITSUBISHI MC:**
- **OMRON FINS:**
-- **HTTP:** [/node-red/core-nodes/http-in/](/node-red/core-nodes/http-in/)
+- **HTTP:** [/docs/node-red/core-nodes/http-in/](/docs/node-red/core-nodes/http-in/)
- **LwM2M:**
-- **AMQP:** [/node-red/protocol/amqp/](/node-red/protocol/amqp/)
+- **AMQP:** [/docs/node-red/protocol/amqp/](/docs/node-red/protocol/amqp/)
- **Serialport:**
- **GPIO:**
- **Lorawan:**
diff --git a/src/blog/2025/06/structuring-storing-data-mes-integration.md b/src/blog/2025/06/structuring-storing-data-mes-integration.md
index 64a97c6431..32a817a2dc 100644
--- a/src/blog/2025/06/structuring-storing-data-mes-integration.md
+++ b/src/blog/2025/06/structuring-storing-data-mes-integration.md
@@ -162,7 +162,7 @@ It has tons of nodes for almost every database and cloud storage system you'll f
This includes direct **connections** to normal databases like [MySQL](https://flows.nodered.org/node/node-red-node-mysql) and [PostgreSQL](https://flows.nodered.org/node/node-red-contrib-postgresql) for your organized production data.
-It also has special nodes for time-series databases like [InfluxDB](https://flows.nodered.org/node/node-red-contrib-influxdb) and [TimescaleDB](/node-red/database/timescaledb/) to handle fast sensor and machine data.
+It also has special nodes for time-series databases like [InfluxDB](https://flows.nodered.org/node/node-red-contrib-influxdb) and [TimescaleDB](/docs/node-red/database/timescaledb/) to handle fast sensor and machine data.
Plus, FlowFuse **connects** to big Data Lakes and Cloud Storage services like [Amazon S3](https://flows.nodered.org/node/node-red-node-aws), [Google Cloud Storage](https://flows.nodered.org/node/node-red-contrib-google-cloud), and [Microsoft Azure](https://flows.nodered.org/node/node-red-contrib-azure-storage).
diff --git a/src/blog/2025/06/what-is-mes.md b/src/blog/2025/06/what-is-mes.md
index a84c785d80..7230e10d94 100644
--- a/src/blog/2025/06/what-is-mes.md
+++ b/src/blog/2025/06/what-is-mes.md
@@ -50,7 +50,7 @@ A Manufacturing Execution System (MES) connects live shop-floor activity with th
An MES pulls live data from machines, sensors, and operators, then uses it to guide and confirm each step of production. As a work order runs, it can track which machine is handling it, how much material is used, and whether output is meeting specification in real time.
-The hard part is rarely the concept. It is [getting data out of equipment that was never built to share it](/blog/2025/06/data-acquisition-for-mes/). A decade-old PLC may expose raw, uncontextualized tags over a vendor-specific protocol, while the SCADA system reading that data was designed to present it to an operator, not publish it upward. [Modbus](/node-red/protocol/modbus/), EtherNet/IP, Profinet, [S7comm](/blog/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/), and [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/) can coexist on the same floor, and none inherently carry the order context an ERP system needs.
+The hard part is rarely the concept. It is [getting data out of equipment that was never built to share it](/blog/2025/06/data-acquisition-for-mes/). A decade-old PLC may expose raw, uncontextualized tags over a vendor-specific protocol, while the SCADA system reading that data was designed to present it to an operator, not publish it upward. [Modbus](/docs/node-red/protocol/modbus/), EtherNet/IP, Profinet, [S7comm](/blog/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/), and [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/) can coexist on the same floor, and none inherently carry the order context an ERP system needs.
In a brownfield environment spanning several sites, that becomes a standing integration problem rather than a one-time project. [IT/OT connectivity](/use-cases/it-ot-middleware/) moves production events and quality checks between [PLCs](/landing/plc/), [SCADA](/use-cases/scada/), and [enterprise systems](/landing/enterprise-integration/) without relying on brittle point-to-point integrations.
diff --git a/src/blog/2025/07/quality-control-automation-spc-charts.md b/src/blog/2025/07/quality-control-automation-spc-charts.md
index a456af3d9b..10cc4b542b 100644
--- a/src/blog/2025/07/quality-control-automation-spc-charts.md
+++ b/src/blog/2025/07/quality-control-automation-spc-charts.md
@@ -314,7 +314,7 @@ Time to connect your actual machines. The approach depends on what equipment you
For modern PLCs - anything from the last decade like Siemens S7-1200/1500, Allen-Bradley ControlLogix, or Omron NX - you'll use OPC UA. It's already built into these PLCs. Enable it in the configuration, install `node-red-contrib-opcua` from the FlowFuse palette, and point it at your PLC. The endpoint looks like `opc.tcp://192.168.1.100:4840`. Browse for your measurement tags and connect them to your SPC flow. [Full OPC UA guide here](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/).
-Older equipment speaks Modbus TCP. Check your manual's appendix for the register map. Install `node-red-contrib-modbus`, configure it with your device's IP address and the register holding your measurement (like 40001 for holding registers). Almost every industrial device from the last 30 years supports this. [Modbus tutorial here](/node-red/protocol/modbus/).
+Older equipment speaks Modbus TCP. Check your manual's appendix for the register map. Install `node-red-contrib-modbus`, configure it with your device's IP address and the register holding your measurement (like 40001 for holding registers). Almost every industrial device from the last 30 years supports this. [Modbus tutorial here](/docs/node-red/protocol/modbus/).
For everything else, get creative. Old gauges with RS-232 ports work fine with a USB adapter and the serial node - [see our serial port guide](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/). Machines that dump CSV files can be monitored with the watch node. Manual measurements need just a simple dashboard form, one input field, one submit button. Don't overcomplicate it.
diff --git a/src/blog/2025/09/creating-pareto-chart.md b/src/blog/2025/09/creating-pareto-chart.md
index df3aaf8a15..62ec4bd46c 100644
--- a/src/blog/2025/09/creating-pareto-chart.md
+++ b/src/blog/2025/09/creating-pareto-chart.md
@@ -27,7 +27,7 @@ To create a Pareto Chart for manufacturing data, you'll need access to industria
### Step 1: Connect to Your Data Source
-The first step to create a Pareto Chart is accessing the data you want to analyze. In industrial environments, machine or process data is commonly collected via [industrial protocols](/node-red/protocol/) such as [OPC-UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/), [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), or direct [database](/node-red/database/) queries. Modern industrial platforms support nearly all industrial protocols and databases, making it easy to connect to your existing systems.
+The first step to create a Pareto Chart is accessing the data you want to analyze. In industrial environments, machine or process data is commonly collected via [industrial protocols](/docs/node-red/protocol/) such as [OPC-UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/), [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), or direct [database](/docs/node-red/database/) queries. Modern industrial platforms support nearly all industrial protocols and databases, making it easy to connect to your existing systems.
To connect your data:
diff --git a/src/blog/2025/09/it-vs-ot-difference-between-information-technology-and-operational-technology.md b/src/blog/2025/09/it-vs-ot-difference-between-information-technology-and-operational-technology.md
index 313e745081..d7f5f49d64 100644
--- a/src/blog/2025/09/it-vs-ot-difference-between-information-technology-and-operational-technology.md
+++ b/src/blog/2025/09/it-vs-ot-difference-between-information-technology-and-operational-technology.md
@@ -64,11 +64,11 @@ Connecting the two (IT/OT convergence) is central to Industry 4.0, but it only w
OT is the umbrella term for the systems that directly run production: [Programmable Logic Controllers (PLCs)](/blog/2025/10/plc-to-mqtt-using-flowfuse/) that execute real-time control logic, [SCADA](/use-cases/scada/) systems that provide centralized monitoring across distributed sites, Distributed Control Systems (DCS) for complex continuous processes, and [Human-Machine Interfaces (HMI)](/blog/2025/11/building-hmi-for-equipment-control/) that give operators visualization and control. Safety Instrumented Systems, Building Management Systems, and [Manufacturing Execution Systems (MES)](/use-cases/mes/) round out the OT landscape. Collectively, these are known as Industrial Control Systems (ICS).
-OT prioritizes safety, availability, and reliability above everything else. These systems were historically air-gapped, receive updates rarely (often only during planned shutdowns), and communicate over industrial protocols like [Modbus](/node-red/protocol/modbus/), Profibus, [OPC-UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/) rather than standard internet protocols. Equipment stays in service for 15-25+ years, runs on millisecond timing, and has essentially zero tolerance for downtime: every stopped minute costs money.
+OT prioritizes safety, availability, and reliability above everything else. These systems were historically air-gapped, receive updates rarely (often only during planned shutdowns), and communicate over industrial protocols like [Modbus](/docs/node-red/protocol/modbus/), Profibus, [OPC-UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/) rather than standard internet protocols. Equipment stays in service for 15-25+ years, runs on millisecond timing, and has essentially zero tolerance for downtime: every stopped minute costs money.
## What is IT (Information Technology)?
-IT covers the systems, software, and infrastructure that manage business data and enable enterprise operations: [email](/node-red/notification/email/) and [databases](/node-red/database/), ERP and CRM platforms, business intelligence, and increasingly cloud and SaaS applications.
+IT covers the systems, software, and infrastructure that manage business data and enable enterprise operations: [email](/docs/node-red/notification/email/) and [databases](/docs/node-red/database/), ERP and CRM platforms, business intelligence, and increasingly cloud and SaaS applications.
IT prioritizes data confidentiality and integrity, stays connected to the internet by default, and receives patches on monthly or quarterly cycles over standardized protocols like TCP/IP, HTTP, and HTTPS. Equipment typically cycles out every 3-5 years, and businesses can usually tolerate minutes-to-hours of downtime for maintenance.
diff --git a/src/blog/2025/09/what-is-takt-time.md b/src/blog/2025/09/what-is-takt-time.md
index 5c54b4f9b2..ed159a0279 100644
--- a/src/blog/2025/09/what-is-takt-time.md
+++ b/src/blog/2025/09/what-is-takt-time.md
@@ -140,11 +140,11 @@ In an ideal lean environment: **Cycle Time should sit at roughly 90–95% of Tak
## Implementing Takt Time Monitoring with FlowFuse
-Understanding takt time in theory is one thing; putting it into practice requires the right tools. [FlowFuse](/) connects to your existing systems, whether [PLCs](/blog/2025/10/plc-to-mqtt-using-flowfuse/), [databases](/node-red/database/), or ERP software, to calculate takt time automatically in real time instead of on static spreadsheets. Before starting, [create a FlowFuse account](https://app.flowfuse.com/account/create) if you don't already have one.
+Understanding takt time in theory is one thing; putting it into practice requires the right tools. [FlowFuse](/) connects to your existing systems, whether [PLCs](/blog/2025/10/plc-to-mqtt-using-flowfuse/), [databases](/docs/node-red/database/), or ERP software, to calculate takt time automatically in real time instead of on static spreadsheets. Before starting, [create a FlowFuse account](https://app.flowfuse.com/account/create) if you don't already have one.
### Step 1: Connect to Your Data Sources
-FlowFuse supports connections to industrial systems through its library of [protocol](/node-red/protocol/) and [database](/node-red/database/) nodes, pulling customer orders from your [ERP system](/blog/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/), production schedules from MES, and real-time counts from PLCs.
+FlowFuse supports connections to industrial systems through its library of [protocol](/docs/node-red/protocol/) and [database](/docs/node-red/database/) nodes, pulling customer orders from your [ERP system](/blog/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/), production schedules from MES, and real-time counts from PLCs.
For this demo, simulate customer orders with an Inject node:
diff --git a/src/blog/2025/10/building-mcp-server-using-flowfuse.md b/src/blog/2025/10/building-mcp-server-using-flowfuse.md
index dc294b3e27..b8aef0c872 100644
--- a/src/blog/2025/10/building-mcp-server-using-flowfuse.md
+++ b/src/blog/2025/10/building-mcp-server-using-flowfuse.md
@@ -41,7 +41,7 @@ Before you begin, ensure you have the following:
* **A running FlowFuse Enterprise instance.** If you do not have one, [contact us](/contact-us/) to discuss Enterprise options and get started.
-* **Ensure the `@flowfuse-nodes/nr-mcp-server-nodes` package is installed**. This will add the [MCP nodes](/node-red/flowfuse/mcp/) to your Node palette in your instance editor.
+* **Ensure the `@flowfuse-nodes/nr-mcp-server-nodes` package is installed**. This will add the [MCP nodes](/docs/flowfuse-nodes/mcp/) to your Node palette in your instance editor.
> **Note:** The MCP nodes (@flowfuse-nodes/nr-mcp-server-nodes) are only available on the Enterprise tier.
diff --git a/src/blog/2025/10/how-to-log-plc-data-csv-files.md b/src/blog/2025/10/how-to-log-plc-data-csv-files.md
index db7b69fa3d..f2d8823245 100644
--- a/src/blog/2025/10/how-to-log-plc-data-csv-files.md
+++ b/src/blog/2025/10/how-to-log-plc-data-csv-files.md
@@ -197,7 +197,7 @@ try {
}
```
-3. Connect it to your notification system to alert when space is critical, for notification you can use [email](/node-red/notification/email/), [telegram](/node-red/notification/telegram/), discord with [FlowFuse](/node-red/notification/discord/).
+3. Connect it to your notification system to alert when space is critical, for notification you can use [email](/docs/node-red/notification/email/), [telegram](/docs/node-red/notification/telegram/), discord with [FlowFuse](/docs/node-red/notification/discord/).
4. Deploy the flow
Now you'll get warnings before disk space becomes critical, giving you time to archive old data or expand storage.
diff --git a/src/blog/2025/11/building-hmi-for-equipment-control.md b/src/blog/2025/11/building-hmi-for-equipment-control.md
index 6f1e4a91a5..9c0a37e049 100644
--- a/src/blog/2025/11/building-hmi-for-equipment-control.md
+++ b/src/blog/2025/11/building-hmi-for-equipment-control.md
@@ -101,7 +101,7 @@ Your HMI needs to talk to your PLC to read equipment status and send control com
Choose the node that matches your PLC:
-- **[node-red-contrib-modbus](/node-red/protocol/modbus/)** – Modbus RTU/TCP PLCs and devices
+- **[node-red-contrib-modbus](/docs/node-red/protocol/modbus/)** – Modbus RTU/TCP PLCs and devices
- **[node-red-contrib-s7](/blog/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/)** – Siemens S7-300/400/1200/1500
- **[node-red-contrib-opcua](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/)** – OPC UA servers (universal industrial standard)
- **[node-red-contrib-cip-ethernet-ip](/blog/2025/10/using-ethernet-ip-with-flowfuse/)** – Allen-Bradley/Rockwell PLCs
diff --git a/src/blog/2025/11/flowfuse+llm+mcp-equals-text-driven-operations.md b/src/blog/2025/11/flowfuse+llm+mcp-equals-text-driven-operations.md
index 40ddfd2ba7..efc47596be 100644
--- a/src/blog/2025/11/flowfuse+llm+mcp-equals-text-driven-operations.md
+++ b/src/blog/2025/11/flowfuse+llm+mcp-equals-text-driven-operations.md
@@ -31,7 +31,7 @@ the right questions.
## The Context Challenge
Data capture involves integrating various machine protocols (like
-[OPC-UA](/node-red/protocol/opc-ua/) or [Modbus](/node-red/protocol/modbus/)),
+[OPC-UA](/docs/node-red/protocol/opc-ua/) or [Modbus](/docs/node-red/protocol/modbus/)),
transporting, combining, and visualizing the information. While low-code tools
like Node-RED have decreased the implementation time to mere hours, the full
problem isn't solved: what happens *after* the data is collected?
@@ -66,7 +66,7 @@ functionality (tools) they want to expose to the LLM.
* **Resources** are read-only, like sensor readings, employee staff lists, vacation calendars, or specification sheets (e.g., upper and lower temperature limits).
* **Tools** are functions that allow the LLM to perform an action or change a state in the physical world.
-By feeding this context into an MCP server (such as the official [FlowFuse MCP node](/node-red/flowfuse/mcp/)),
+By feeding this context into an MCP server (such as the official [FlowFuse MCP node](/docs/flowfuse-nodes/mcp/)),
the LLM transforms into a powerful operational partner.
For example, an operator can ask: "Can you show me the last five temp sensor readings recorded?".
diff --git a/src/blog/2025/11/industrial-data-validation-guide.md b/src/blog/2025/11/industrial-data-validation-guide.md
index 61daea7295..d52de5adfa 100644
--- a/src/blog/2025/11/industrial-data-validation-guide.md
+++ b/src/blog/2025/11/industrial-data-validation-guide.md
@@ -306,7 +306,7 @@ Once installed, you'll find the "telegram sender" and "telegram receiver" nodes
### Creating Your Telegram Bot and Getting Your Chat ID
-Before you can send alerts, you need to create a Telegram bot and get your Chat ID. We have a detailed guide that walks you through the entire process: [How to Create a Telegram Bot and Find Your Chat ID](/node-red/notification/telegram/#creating-a-bot-in-telegram)
+Before you can send alerts, you need to create a Telegram bot and get your Chat ID. We have a detailed guide that walks you through the entire process: [How to Create a Telegram Bot and Find Your Chat ID](/docs/node-red/notification/telegram/#creating-a-bot-in-telegram)
Once you have your **bot token** and **Chat ID**, come back here to continue with the alert setup.
diff --git a/src/blog/2025/12/flowfuse-release-2-25.md b/src/blog/2025/12/flowfuse-release-2-25.md
index 2d6cfdba98..7782afd729 100644
--- a/src/blog/2025/12/flowfuse-release-2-25.md
+++ b/src/blog/2025/12/flowfuse-release-2-25.md
@@ -21,7 +21,7 @@ This release comes with some big updates for the FlowFuse Expert.

_[FlowFuse Expert Interface]_
-Our [MCP nodes](/node-red/flowfuse/mcp/) make is super easy to extend AI Agents with tools and resources in your environment. With this release, we're introducing the ability to interact your MCP servers directly within the FlowFuse Expert.
+Our [MCP nodes](/docs/flowfuse-nodes/mcp/) make is super easy to extend AI Agents with tools and resources in your environment. With this release, we're introducing the ability to interact your MCP servers directly within the FlowFuse Expert.
Previously, when setting up an MCP server in FlowFuse, you would first designate your tools, resources, and servers in side of Node-RED using the MCP nodes. Then, to interact with them, you needed a separate tool like VSCode to query and perform operations.
diff --git a/src/blog/2025/12/mttf-vs-mtbf-vs-mttr.md b/src/blog/2025/12/mttf-vs-mtbf-vs-mttr.md
index 8d7de3cd8f..155c726826 100644
--- a/src/blog/2025/12/mttf-vs-mtbf-vs-mttr.md
+++ b/src/blog/2025/12/mttf-vs-mtbf-vs-mttr.md
@@ -187,4 +187,4 @@ Stop guessing when parts will fail. Track installation dates and failure times,
Use that data to schedule replacements during planned downtime instead of waiting for a 2am breakdown. Unplanned failures typically cost 3-5x more than scheduled replacements once overtime labor, rush shipping, and lost production are factored in, so the only MTTF numbers that really matter are the ones you measure yourself.
-As your equipment base grows, manual tracking becomes cumbersome. [FlowFuse](/) automates this by connecting to [PLCs](/blog/2025/12/what-is-plc/), [SCADA](/use-cases/scada/) systems, [MES](/use-cases/mes/) platforms, and your CMMS, pulling operating hours directly from equipment through industrial protocols like [Modbus](/node-red/protocol/modbus/), [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/), then capturing failure events to recalculate MTTF in real-time across your entire facility.
+As your equipment base grows, manual tracking becomes cumbersome. [FlowFuse](/) automates this by connecting to [PLCs](/blog/2025/12/what-is-plc/), [SCADA](/use-cases/scada/) systems, [MES](/use-cases/mes/) platforms, and your CMMS, pulling operating hours directly from equipment through industrial protocols like [Modbus](/docs/node-red/protocol/modbus/), [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/), then capturing failure events to recalculate MTTF in real-time across your entire facility.
diff --git a/src/blog/2026/01/why-modbus-still-exist.md b/src/blog/2026/01/why-modbus-still-exist.md
index b20c089fcc..4f1f43a9b6 100644
--- a/src/blog/2026/01/why-modbus-still-exist.md
+++ b/src/blog/2026/01/why-modbus-still-exist.md
@@ -120,7 +120,7 @@ For decades, industrial networks lived behind air gaps and Modbus's lack of auth
The moment you connect to external networks, Modbus's plaintext communication and zero authentication become indefensible. An attacker with network access can send arbitrary commands to any Modbus device. No password, no certificate, no audit trail.
-[OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/) provides TLS encryption, certificate-based authentication, role-based access control, and detailed audit logging. [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/) with [Sparkplug B](/blog/2024/08/using-mqtt-sparkplugb-with-node-red/) adds lightweight security for IIoT deployments. These aren't nice-to-have features when you're connecting critical infrastructure to the internet: they're requirements. Companies implementing Industry 4.0 initiatives, remote operations, or cloud-based analytics cannot build on [Modbus](/node-red/protocol/modbus/). The protocol has no security model to extend.
+[OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/) provides TLS encryption, certificate-based authentication, role-based access control, and detailed audit logging. [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/) with [Sparkplug B](/blog/2024/08/using-mqtt-sparkplugb-with-node-red/) adds lightweight security for IIoT deployments. These aren't nice-to-have features when you're connecting critical infrastructure to the internet: they're requirements. Companies implementing Industry 4.0 initiatives, remote operations, or cloud-based analytics cannot build on [Modbus](/docs/node-red/protocol/modbus/). The protocol has no security model to extend.
Modbus RTU at 115.2 kbaud can theoretically handle around 80-100 transactions per second under ideal conditions, but real-world installations see more like 40-60 due to device processing time and network overhead. That's fine for a few dozen devices with slow-changing process variables.
diff --git a/src/blog/2026/02/edge-ai-is-80-percent-pipeline-and-20-percent-ai.md b/src/blog/2026/02/edge-ai-is-80-percent-pipeline-and-20-percent-ai.md
index 7460503941..cdf2302c5f 100644
--- a/src/blog/2026/02/edge-ai-is-80-percent-pipeline-and-20-percent-ai.md
+++ b/src/blog/2026/02/edge-ai-is-80-percent-pipeline-and-20-percent-ai.md
@@ -54,7 +54,7 @@ Take what happens when you want to put a model in production. You have a data sc
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.
-[FlowFuse's ONNX nodes](/node-red/flowfuse/ai/onxx/) 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 [deployed a motor anomaly detector this way](/blog/2026/02/motor-anomaly-detector-ai/), 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.
+[FlowFuse's ONNX nodes](/docs/flowfuse-nodes/ai/onxx/) 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 [deployed a motor anomaly detector this way](/blog/2026/02/motor-anomaly-detector-ai/), 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.
The same logic applies to the [FlowFuse Expert](/docs/user/expert/). 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.
diff --git a/src/blog/2026/02/mapping-mtconnect-streams.md b/src/blog/2026/02/mapping-mtconnect-streams.md
index 6ae476c224..f2089f7377 100644
--- a/src/blog/2026/02/mapping-mtconnect-streams.md
+++ b/src/blog/2026/02/mapping-mtconnect-streams.md
@@ -75,7 +75,7 @@ Bringing MTConnect data into FlowFuse is the first step toward a live dashboard.
The Solution Engine node (`node-red-contrib-solution-engine`) makes this straightforward because it lets you access any data point directly by its dataItemId, without having to worry about parsing XML or navigating nested structures.
-You can install the node by following the instructions in the FlowFuse documentation: [Using the Palette Manager](/node-red/getting-started/library/#using-the-palette-manager).
+You can install the node by following the instructions in the FlowFuse documentation: [Using the Palette Manager](/docs/node-red/getting-started/library/#using-the-palette-manager).
Once installed, follow these steps to start collecting data:
diff --git a/src/blog/2026/02/motor-anomaly-detector-ai.md b/src/blog/2026/02/motor-anomaly-detector-ai.md
index 09fe3ecc34..85cb178f2f 100644
--- a/src/blog/2026/02/motor-anomaly-detector-ai.md
+++ b/src/blog/2026/02/motor-anomaly-detector-ai.md
@@ -429,7 +429,7 @@ The flow has five stages: receive the payload, extract features, scale and prepa
Add an **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.
-If you are using the built-in FlowFuse MQTT broker, use the [FlowFuse MQTT nodes](/node-red/flowfuse/mqtt/) , these connect automatically when dragged into the flow.
+If you are using the built-in FlowFuse MQTT broker, use the [FlowFuse MQTT nodes](/docs/flowfuse-nodes/mqtt/) , these connect automatically when dragged into the flow.
**2. Extract Features**
@@ -548,7 +548,7 @@ Once deployed, the flow should look like this:
**5. Act on the Result**
-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 [FlowFuse Dashboard](/platform/dashboard/) package can visualise the anomaly score over time with a clear motor state indicator, and alerting nodes can notify your team directly, via [Telegram](/node-red/notification/email/), [email](/node-red/notification/telegram/), or [SIGNL4](/blueprints/other/mobile-alerting/) for structured mobile alerts with on-call scheduling and acknowledgement tracking.
+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 [FlowFuse Dashboard](/platform/dashboard/) package can visualise the anomaly score over time with a clear motor state indicator, and alerting nodes can notify your team directly, via [Telegram](/docs/node-red/notification/email/), [email](/docs/node-red/notification/telegram/), or [SIGNL4](/blueprints/other/mobile-alerting/) for structured mobile alerts with on-call scheduling and acknowledgement tracking.
### What the Output Looks Like
diff --git a/src/blog/2026/02/mqtt-influxdb-tutorial.md b/src/blog/2026/02/mqtt-influxdb-tutorial.md
index 0278fe6d08..2c2de67ab8 100644
--- a/src/blog/2026/02/mqtt-influxdb-tutorial.md
+++ b/src/blog/2026/02/mqtt-influxdb-tutorial.md
@@ -80,7 +80,7 @@ This article takes a different approach. Using FlowFuse (the enterprise platform
Before you start, make sure you have the following in place.
- A running FlowFuse instance. If you don't have one yet, [sign up](https://app.flowfuse.com/account/create) to get started.
-- An MQTT broker. FlowFuse Pro and Enterprise include a [built-in broker](/docs/user/teambroker/), and the [MQTT nodes](/node-red/flowfuse/mqtt/) configure themselves automatically. If you are using an external broker, keep your host, port, and credentials handy.
+- An MQTT broker. FlowFuse Pro and Enterprise include a [built-in broker](/docs/user/teambroker/), and the [MQTT nodes](/docs/flowfuse-nodes/mqtt/) configure themselves automatically. If you are using an external broker, keep your host, port, and credentials handy.
- A running InfluxDB instance, either local or on InfluxDB Cloud. Keep your URL, token, organization, and bucket name handy.
## Building the Pipeline
diff --git a/src/blog/2026/02/shop-floor-to-ai-signals-context-decisions.md b/src/blog/2026/02/shop-floor-to-ai-signals-context-decisions.md
index de02b91fc9..948bdf92f0 100644
--- a/src/blog/2026/02/shop-floor-to-ai-signals-context-decisions.md
+++ b/src/blog/2026/02/shop-floor-to-ai-signals-context-decisions.md
@@ -129,7 +129,7 @@ Crucially, in a Unified Namespace, context is added at the moment data enters th
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.
-Through [FlowFuse MCP nodes](/node-red/flowfuse/mcp/), 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.
+Through [FlowFuse MCP nodes](/docs/flowfuse-nodes/mcp/), 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.
[FlowFuse AI Expert](/ai/) operates on the same MCP-backed context layer. Operators and engineers can ask questions in natural language (*"Is Line 3 behaving normally?"*, *"Have we seen this vibration pattern before?"*, *"What changed before the last failure?"*) and receive answers grounded in the live Unified Namespace.
diff --git a/src/blog/2026/04/diagnosing-modbus-degradation.md b/src/blog/2026/04/diagnosing-modbus-degradation.md
index dddbd9319e..a444834930 100644
--- a/src/blog/2026/04/diagnosing-modbus-degradation.md
+++ b/src/blog/2026/04/diagnosing-modbus-degradation.md
@@ -144,7 +144,7 @@ The four metrics worth tracking continuously are transaction success rate, respo
### Putting it into practice
-Instrument your polling layer to write a timestamped record on every transaction: device address, function code, success or failure, response time in milliseconds, exception code on failures, and error type (timeout vs. CRC vs. exception response). Write those records to a [time-series store](/node-red/database/). [InfluxDB](/node-red/database/influxdb/) is a common choice for industrial deployments; a [PostgreSQL](/blog/2025/08/getting-started-with-flowfuse-tables/) table with a timestamp index works fine too.
+Instrument your polling layer to write a timestamped record on every transaction: device address, function code, success or failure, response time in milliseconds, exception code on failures, and error type (timeout vs. CRC vs. exception response). Write those records to a [time-series store](/docs/node-red/database/). [InfluxDB](/docs/node-red/database/influxdb/) is a common choice for industrial deployments; a [PostgreSQL](/blog/2025/08/getting-started-with-flowfuse-tables/) table with a timestamp index works fine too.
A reasonable retention strategy is to keep raw transaction records for 7 days and roll up to hourly aggregates (per-device success rate, median response time, CRC error count) for 90 days of trend data.
diff --git a/src/blog/2026/06/event-driven-downtime-escalation-workflow.md b/src/blog/2026/06/event-driven-downtime-escalation-workflow.md
index f5a4ac0a0b..10df193f88 100644
--- a/src/blog/2026/06/event-driven-downtime-escalation-workflow.md
+++ b/src/blog/2026/06/event-driven-downtime-escalation-workflow.md
@@ -81,11 +81,11 @@ flowchart TD
Here's what that looks like built in [FlowFuse](/):
-- **Catch the stop signal:** Most PLCs expose machine state through [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/) or [Modbus](/node-red/protocol/modbus/), and FlowFuse has nodes for both. Point one at the right tag, and the moment the state flips to stopped, it lands in your flow as a timestamped message.
+- **Catch the stop signal:** Most PLCs expose machine state through [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua/) or [Modbus](/docs/node-red/protocol/modbus/), and FlowFuse has nodes for both. Point one at the right tag, and the moment the state flips to stopped, it lands in your flow as a timestamped message.
- **Track how long it's been down:** A couple of [timer nodes](/blog/2025/12/node-red-timer/) mark the passage of time: past two minutes, past ten, whatever windows fit your line.
-- **Branch by severity:** A switch node routes the message: short stops go straight to a [database](/blog/2025/08/getting-started-with-flowfuse-tables/); longer ones also notify the supervisor over [email](/node-red/notification/email/) or [Telegram](/node-red/notification/telegram/); and if it's still unresolved at the next threshold, maintenance gets the same message, escalating further up if needed.
+- **Branch by severity:** A switch node routes the message: short stops go straight to a [database](/blog/2025/08/getting-started-with-flowfuse-tables/); longer ones also notify the supervisor over [email](/docs/node-red/notification/email/) or [Telegram](/docs/node-red/notification/telegram/); and if it's still unresolved at the next threshold, maintenance gets the same message, escalating further up if needed.
- **Close the record:** When the "running" message comes through, a final node writes the resolution time back into that record: start, every escalation, and end, all filled in by the flow itself.
diff --git a/src/blog/2026/06/flowfuse-release-2-31.md b/src/blog/2026/06/flowfuse-release-2-31.md
index c253ba0e4c..5d164686af 100644
--- a/src/blog/2026/06/flowfuse-release-2-31.md
+++ b/src/blog/2026/06/flowfuse-release-2-31.md
@@ -51,7 +51,7 @@ FlowFuse now offers certified nodes: vetted, FlowFuse-supported nodes you can ad
- Gemini
- Ollama, for models you run yourself
- These first LLM nodes handle single-shot, text-in/text-out calls; see the [LLM nodes documentation](/node-red/flowfuse/ai/llm-nodes/) for details. Multimodal input, conversation history, and tool calling are planned for follow-up iterations. They join the FlowFuse AI Nodes package we [shipped in 2.23](/changelog/2025/10/onnx-nodes/), which already includes the ONNX Inference, Image Classification, Object Detection, and Image Depth Estimation nodes for running vision and custom models on-device, with no external API calls.
+ These first LLM nodes handle single-shot, text-in/text-out calls; see the [LLM nodes documentation](/docs/flowfuse-nodes/ai/llm-nodes/) for details. Multimodal input, conversation history, and tool calling are planned for follow-up iterations. They join the FlowFuse AI Nodes package we [shipped in 2.23](/changelog/2025/10/onnx-nodes/), which already includes the ONNX Inference, Image Classification, Object Detection, and Image Depth Estimation nodes for running vision and custom models on-device, with no external API calls.
Certified Nodes are available to FlowFuse Cloud customers as of this release, with Self-Hosted Enterprise support arriving in the next release (2.32). [Get in touch with sales](/contact-us/?subject=Certified%20Nodes) or your customer success manager, and we'll enable the ones your team needs.
diff --git a/src/blog/2026/06/opcua-to-influxdb.md b/src/blog/2026/06/opcua-to-influxdb.md
index 94db9b0f6d..fd23281160 100644
--- a/src/blog/2026/06/opcua-to-influxdb.md
+++ b/src/blog/2026/06/opcua-to-influxdb.md
@@ -90,7 +90,7 @@ You'll install two packages: the InfluxDB nodes and the FlowFuse Edge Certified
2. From the menu (top right), select **Manage palette**, then switch to the **Install** tab.
3. Search for **`node-red-contrib-influxdb`** and click **Install**. These nodes write to and query InfluxDB.
4. Switch the catalog using the top dropdown to **FlowFuse Edge Certified Nodes**. Once sales enables FlowFuse Edge Certified Nodes for your team, this catalog shows up here; restart any existing instance so it picks up the updated catalogue.
-5. Search for **`@flowfuse-certified-nodes/opcua`** and click **Install**. See the [OPC UA node documentation](/node-red/flowfuse/edge/opcua/) for the full node set and configuration options.
+5. Search for **`@flowfuse-certified-nodes/opcua`** and click **Install**. See the [OPC UA node documentation](/docs/flowfuse-nodes/edge/opcua/) for the full node set and configuration options.

_Install the OPC UA certified package from the FlowFuse Edge Certified Nodes catalog._
diff --git a/src/blog/2026/06/process-rtsp-camera-feeds-at-the-edge.md b/src/blog/2026/06/process-rtsp-camera-feeds-at-the-edge.md
index cb779b559d..0f0e668d5d 100644
--- a/src/blog/2026/06/process-rtsp-camera-feeds-at-the-edge.md
+++ b/src/blog/2026/06/process-rtsp-camera-feeds-at-the-edge.md
@@ -155,7 +155,7 @@ That's a live line view anyone can pull up in a browser, with no NVR login and n
A live view is a real win, but notice what you have now: the camera's output is a PNG buffer moving through your flow, one message per frame. Once a frame is just another message, you can do more than display it. You can ask what's in it.
-That's what the [**FlowFuse AI** nodes](/node-red/flowfuse/ai/) are for. They run vision models locally, inside your flow, with nothing sent to an outside service. The **Object Detection** node takes a PNG buffer as its input, which is exactly what the camera node outputs, so you wire the camera straight into it, no conversion step in between. From there the flow stops watching and starts acting: counting material on the conveyor, flagging a person near the flotation cells, or catching a stopped belt before the line backs up. Each detection comes back as structured data, a label, a confidence score, and a position, which you handle like any other signal in FlowFuse.
+That's what the [**FlowFuse AI** nodes](/docs/flowfuse-nodes/ai/) are for. They run vision models locally, inside your flow, with nothing sent to an outside service. The **Object Detection** node takes a PNG buffer as its input, which is exactly what the camera node outputs, so you wire the camera straight into it, no conversion step in between. From there the flow stops watching and starts acting: counting material on the conveyor, flagging a person near the flotation cells, or catching a stopped belt before the line backs up. Each detection comes back as structured data, a label, a confidence score, and a position, which you handle like any other signal in FlowFuse.
## Recording frames to disk
diff --git a/src/blog/2026/07/calibration-management-dashboard.md b/src/blog/2026/07/calibration-management-dashboard.md
index 08ace58802..0b3a2a9c4c 100644
--- a/src/blog/2026/07/calibration-management-dashboard.md
+++ b/src/blog/2026/07/calibration-management-dashboard.md
@@ -380,7 +380,7 @@ A dashboard in the quality office doesn't reach the person about to pick up a to
4. Add a `simple-ocr` node, then a `link out` / `link in` pair to carry the result down to the next row of the canvas.
-> **Tip:** OCR keeps this tutorial hardware-free, but if your labels have a barcode or QR code, scanning one is faster and far more reliable than reading printed text. Swap the camera and OCR nodes for a [barcode scanner](/node-red/peripheral/barcodescanner/) and feed the decoded ID straight into "Set Tool ID," and everything downstream will keep working. You can also drop "Extract Tool ID" and "Scan Valid?", since a scanner either returns an ID or nothing.
+> **Tip:** OCR keeps this tutorial hardware-free, but if your labels have a barcode or QR code, scanning one is faster and far more reliable than reading printed text. Swap the camera and OCR nodes for a [barcode scanner](/docs/node-red/peripheral/barcodescanner/) and feed the decoded ID straight into "Set Tool ID," and everything downstream will keep working. You can also drop "Extract Tool ID" and "Scan Valid?", since a scanner either returns an ID or nothing.
5. Add a `function` node named "Extract Tool ID":
@@ -547,4 +547,4 @@ Deploy and open `/tool-check` on a device with a camera. Hold a label reading `T
You've built a calibration application that answers two different questions from the same data. The office screen tells a quality engineer how the plant stands and what needs collecting this week. The station screen tells an operator whether the tool in their hand is fit to use. Both read the due date, so neither can drift from the other.
-Right now it runs on generated data. To go live, remove the simulator flow and point the queries at your own register. Just alias your column names in the `SELECT` lists, and everything downstream keeps working. If your records sit in a CMMS or ERP behind an API, swap the query nodes for `http request` nodes and nothing else changes. From there, the obvious next steps are ones the register already supports: email the overdue list to the calibration lab each Monday, or [notify](/node-red/notification/) someone the moment a critical gauge goes overdue instead of waiting for a screen to be looked at. The same approach works for [defect tracking](/blog/2026/07/defect-and-quality-monitoring/) and [downtime](/blog/2026/07/build-downtime-logger/) too, and for the other [manufacturing dashboards](/blog/2026/08/manufacturing-dashboard-examples/) teams build on the same data. It also pairs with floor-level checks such as a [layered process audit](/blog/2026/08/layered-process-audit/), where a tool's calibration status is exactly the kind of item this dashboard can hand the auditor an answer for instead of a lookup. See how manufacturers are using it on our [automotive solutions page](/industries/automotive/).
+Right now it runs on generated data. To go live, remove the simulator flow and point the queries at your own register. Just alias your column names in the `SELECT` lists, and everything downstream keeps working. If your records sit in a CMMS or ERP behind an API, swap the query nodes for `http request` nodes and nothing else changes. From there, the obvious next steps are ones the register already supports: email the overdue list to the calibration lab each Monday, or [notify](/docs/node-red/notification/) someone the moment a critical gauge goes overdue instead of waiting for a screen to be looked at. The same approach works for [defect tracking](/blog/2026/07/defect-and-quality-monitoring/) and [downtime](/blog/2026/07/build-downtime-logger/) too, and for the other [manufacturing dashboards](/blog/2026/08/manufacturing-dashboard-examples/) teams build on the same data. It also pairs with floor-level checks such as a [layered process audit](/blog/2026/08/layered-process-audit/), where a tool's calibration status is exactly the kind of item this dashboard can hand the auditor an answer for instead of a lookup. See how manufacturers are using it on our [automotive solutions page](/industries/automotive/).
diff --git a/src/blog/2026/07/defect-and-quality-monitoring.md b/src/blog/2026/07/defect-and-quality-monitoring.md
index 7d41229ba2..c62799eb98 100644
--- a/src/blog/2026/07/defect-and-quality-monitoring.md
+++ b/src/blog/2026/07/defect-and-quality-monitoring.md
@@ -409,6 +409,6 @@ Deploy and open the dashboard. Every widget populates from the seeded data at on
You've built a working quality dashboard: a `defects` table, a single query that computes every KPI in one pass, and a page of stat cards, a Pareto chart, trend and breakdown charts, an SLA table, and a status funnel, all filtering live by line, shift, and date.
-Right now it runs on the simulator, but that was only ever a stand-in for your real data. To go live, remove the simulator flow and point the query at your own `defects` table. Everything downstream keeps working, because the dashboard only ever reads from that one query. Your defects don't live in PostgreSQL? That's fine too. FlowFuse connects to MySQL, MongoDB, InfluxDB, and more, as our [database integration guides](/node-red/database/) show.
+Right now it runs on the simulator, but that was only ever a stand-in for your real data. To go live, remove the simulator flow and point the query at your own `defects` table. Everything downstream keeps working, because the dashboard only ever reads from that one query. Your defects don't live in PostgreSQL? That's fine too. FlowFuse connects to MySQL, MongoDB, InfluxDB, and more, as our [database integration guides](/docs/node-red/database/) show.
That's the real point. FlowFuse lets you build the exact application your floor needs quickly, without deep engineering knowledge or writing code, wired to the systems you already run instead of forcing your process to fit a fixed tool. This tutorial happened to build defect tracking, but the same approach covers the full range of [manufacturing dashboards](/blog/2026/08/manufacturing-dashboard-examples/), production monitoring, OEE, and the wider quality picture. See how manufacturers are already putting it to work on our [automotive solutions page](/industries/automotive/).
diff --git a/src/blog/2026/08/layered-process-audit.md b/src/blog/2026/08/layered-process-audit.md
index 51b2dae741..f3ed643d23 100644
--- a/src/blog/2026/08/layered-process-audit.md
+++ b/src/blog/2026/08/layered-process-audit.md
@@ -141,7 +141,7 @@ Manufacturers can start an LPA program with paper forms or spreadsheets, but man
A digital LPA can help schedule audits, record findings, notify responsible people, and track actions through closure.
-Connecting the audit with production data provides another benefit. With FlowFuse, manufacturers can connect an LPA application to machines, [PLCs](/landing/plc/), sensors, [databases](/node-red/database/), and other industrial systems.
+Connecting the audit with production data provides another benefit. With FlowFuse, manufacturers can connect an LPA application to machines, [PLCs](/landing/plc/), sensors, [databases](/docs/node-red/database/), and other industrial systems.
This allows audit results to be viewed alongside production information such as downtime, machine states, or process parameters. Instead of only recording that a process failed, teams can investigate what was happening around the failure.
diff --git a/src/blog/2026/08/what-is-scada.md b/src/blog/2026/08/what-is-scada.md
index 3afa47f34d..4d3112adff 100644
--- a/src/blog/2026/08/what-is-scada.md
+++ b/src/blog/2026/08/what-is-scada.md
@@ -62,7 +62,7 @@ Programmable Logic Controllers and Remote Terminal Units take the raw signals fr
### Communication Networks
-Communication networks carry data between field devices, controllers, and the central SCADA software, using protocols like [Modbus](/node-red/protocol/modbus/), [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua), [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), or [Ethernet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/) depending on the equipment and environment. This layer determines how reliably and securely data moves across a facility, and it becomes especially important when connecting older equipment to newer IT/OT platforms.
+Communication networks carry data between field devices, controllers, and the central SCADA software, using protocols like [Modbus](/docs/node-red/protocol/modbus/), [OPC UA](/blog/2025/07/reading-and-writing-plc-data-using-opc-ua), [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/), or [Ethernet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/) depending on the equipment and environment. This layer determines how reliably and securely data moves across a facility, and it becomes especially important when connecting older equipment to newer IT/OT platforms.
### HMI And Central Monitoring Software
diff --git a/src/blog/2026/09/industrial-ai-agent.md b/src/blog/2026/09/industrial-ai-agent.md
index 495a79aedd..2005ea3f49 100644
--- a/src/blog/2026/09/industrial-ai-agent.md
+++ b/src/blog/2026/09/industrial-ai-agent.md
@@ -73,7 +73,7 @@ Ask why they don't use the FlowFuse Expert built into the platform, and the answ
FlowFuse now acts as an MCP server. Your own AI agent connects to it as a client and because the AI agent is yours, so is the choice of model it uses.
-That means the AI agent your company already approved can work the platform directly: look at your teams and applications, check what your instances are doing, read runtime logs, query your [FlowFuse Tables](/node-red/flowfuse/flowfuse-tables/) data, create applications and instances, and build and edit the flows inside your instances.
+That means the AI agent your company already approved can work the platform directly: look at your teams and applications, check what your instances are doing, read runtime logs, query your [FlowFuse Tables](/docs/flowfuse-nodes/flowfuse-tables/) data, create applications and instances, and build and edit the flows inside your instances.
## How an AI agent connects to FlowFuse
diff --git a/src/changelog/2026/01/mcp-rbacs.md b/src/changelog/2026/01/mcp-rbacs.md
index f19a69a4c8..236cecf0af 100644
--- a/src/changelog/2026/01/mcp-rbacs.md
+++ b/src/changelog/2026/01/mcp-rbacs.md
@@ -1,34 +1,34 @@
----
-title: "FlowFuse Expert: MCP-Powered Insights with RBACs"
-description: "You can now limit which user roles see which Model Context Protocol (MCP) tools when querying your MCP servers with the FlowFuse Expert."
+---
+title: "FlowFuse Expert: MCP-Powered Insights with RBACs"
+description: "You can now limit which user roles see which Model Context Protocol (MCP) tools when querying your MCP servers with the FlowFuse Expert."
date: 2026-01-14 13:00:00.0
-release: "2.26"
-authors: ['steve-mclaughlin']
-tags:
- - changelog
----
-
-Following on the the introduction of [FlowFuse Expert MCP-Powered Insights](/changelog/2025/12/ff-expert-mcp-insights/) we have added annotations to the FlowFuse MCP nodes and linked them up with the FlowFuse roles.
-This 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.
-
-### MCP Tools Node
-
-To facilitate a level of role based access control in the Expert, we have leveraged the new Granular RBACs and MCP annotations to introduce 2 means of filtering and control:
-1. By [application](/docs/user/concepts/#application) - as introduced in FlowFuse 1.5
-2. By pre-defined role level mapping to the annotation hints. For instance, users with a 'Viewer' role will only be exposed to tools marked with the Read Only hint, while Destructive tools are reserved for 'Owners'. The screenshot below indicates more clearly what this mapping is.
-
-To facilitate this, we have the next version of our [MCP Server Nodes](/node-red/flowfuse/mcp/) will introduce the 4 MCP standard annotations:
-
-- **Read Only Hint**: Lets the MCP Client know that the tool is read-only and does not modify any data or state. This hint can be used by LLMs to understand that invoking this tool will not have side effects, making it safe for exploratory queries or information retrieval.
-- **Destructive Hint**: Lets the MCP Client know that the tool performs destructive actions that may modify or delete data. This hint can be used by LLMs to exercise caution when invoking this tool, as it may have irreversible effects.
-- **Idempotent Hint**: Lets the MCP Client know that the tool is idempotent, meaning that multiple invocations with the same parameters will have the same effect as a single invocation. This hint can be used by LLMs to understand that it is safe to retry or repeat calls to this tool without causing unintended side effects.
-- **Open World Hint**: Lets the MCP Client know that the tool operates in an open-world context, meaning it may interact with external systems or data sources beyond the immediate environment. This hint can be used by LLMs to understand that invoking this tool may involve uncertainties or dependencies on external factors.
-
-These annotations are not exclusive to FlowFuse or FlowFuse Roles, they also are useful for your own Agents and LLMs.
-
-_Note: Hints do not enforce behavior. The actual behavior of the tool depends on how it is used in your flows_
-
-{data-zoomable}
-_MCP Server Tool Node with new annotations_
-
-_Note: This feature is currently in Beta. We are actively expanding these capabilities and would love to hear how you are using MCP to extend your systems._
+release: "2.26"
+authors: ['steve-mclaughlin']
+tags:
+ - changelog
+---
+
+Following on the the introduction of [FlowFuse Expert MCP-Powered Insights](/changelog/2025/12/ff-expert-mcp-insights/) we have added annotations to the FlowFuse MCP nodes and linked them up with the FlowFuse roles.
+This 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.
+
+### MCP Tools Node
+
+To facilitate a level of role based access control in the Expert, we have leveraged the new Granular RBACs and MCP annotations to introduce 2 means of filtering and control:
+1. By [application](/docs/user/concepts/#application) - as introduced in FlowFuse 1.5
+2. By pre-defined role level mapping to the annotation hints. For instance, users with a 'Viewer' role will only be exposed to tools marked with the Read Only hint, while Destructive tools are reserved for 'Owners'. The screenshot below indicates more clearly what this mapping is.
+
+To facilitate this, we have the next version of our [MCP Server Nodes](/docs/flowfuse-nodes/mcp/) will introduce the 4 MCP standard annotations:
+
+- **Read Only Hint**: Lets the MCP Client know that the tool is read-only and does not modify any data or state. This hint can be used by LLMs to understand that invoking this tool will not have side effects, making it safe for exploratory queries or information retrieval.
+- **Destructive Hint**: Lets the MCP Client know that the tool performs destructive actions that may modify or delete data. This hint can be used by LLMs to exercise caution when invoking this tool, as it may have irreversible effects.
+- **Idempotent Hint**: Lets the MCP Client know that the tool is idempotent, meaning that multiple invocations with the same parameters will have the same effect as a single invocation. This hint can be used by LLMs to understand that it is safe to retry or repeat calls to this tool without causing unintended side effects.
+- **Open World Hint**: Lets the MCP Client know that the tool operates in an open-world context, meaning it may interact with external systems or data sources beyond the immediate environment. This hint can be used by LLMs to understand that invoking this tool may involve uncertainties or dependencies on external factors.
+
+These annotations are not exclusive to FlowFuse or FlowFuse Roles, they also are useful for your own Agents and LLMs.
+
+_Note: Hints do not enforce behavior. The actual behavior of the tool depends on how it is used in your flows_
+
+{data-zoomable}
+_MCP Server Tool Node with new annotations_
+
+_Note: This feature is currently in Beta. We are actively expanding these capabilities and would love to hear how you are using MCP to extend your systems._
diff --git a/src/customer-stories/aperia-technologies-plc-replacement.md b/src/customer-stories/aperia-technologies-plc-replacement.md
index 00ea20c094..ea78449f3d 100644
--- a/src/customer-stories/aperia-technologies-plc-replacement.md
+++ b/src/customer-stories/aperia-technologies-plc-replacement.md
@@ -54,7 +54,7 @@ This manual overhead was limiting Aperia's ability to scale manufacturing operat
## The FlowFuse Solution
-Aperia's answer was to remove the proprietary PLC from the center of the architecture entirely. Instead, smart fixtures run Node-RED on an edge Windows instance, managed by FlowFuse. At the hardware layer, Node-RED talks directly to sensors, actuators, scanners, and motors using standard industrial protocols: [Modbus TCP](/node-red/protocol/modbus/)/[RTU](/blog/2025/09/using-modbus-with-flowfuse/), [RS232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), [raw TCP](/node-red/core-nodes/tcp-in/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/). At the software layer, it triggers label printing, logs serialized telemetry to [MongoDB](/node-red/database/mongodb/), syncs maintenance events to Limble, and pulls data from NetSuite ERP. [FlowFuse functions as the orchestration layer](/use-cases/it-ot-middleware/) that ties hardware protocols and enterprise APIs into one canvas, through two strategic implementations:
+Aperia's answer was to remove the proprietary PLC from the center of the architecture entirely. Instead, smart fixtures run Node-RED on an edge Windows instance, managed by FlowFuse. At the hardware layer, Node-RED talks directly to sensors, actuators, scanners, and motors using standard industrial protocols: [Modbus TCP](/docs/node-red/protocol/modbus/)/[RTU](/blog/2025/09/using-modbus-with-flowfuse/), [RS232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), [raw TCP](/docs/node-red/core-nodes/tcp-in/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/). At the software layer, it triggers label printing, logs serialized telemetry to [MongoDB](/docs/node-red/database/mongodb/), syncs maintenance events to Limble, and pulls data from NetSuite ERP. [FlowFuse functions as the orchestration layer](/use-cases/it-ot-middleware/) that ties hardware protocols and enterprise APIs into one canvas, through two strategic implementations:
### Turning Idle Test Equipment into R&D Capacity
@@ -88,7 +88,7 @@ Aperia's solution was a vision-integrated screw assembly station connected to th
The solution architecture includes:
- **Edge Control Layer:** Node-RED running on edge Windows instances, managed by FlowFuse, in place of proprietary PLC logic
- - [**Industrial Protocol Support**](/node-red/protocol/)**:** Modbus TCP/RTU, RS232, raw TCP, and EtherNet/IP for direct communication with sensors, actuators, scanners, and motors
+ - [**Industrial Protocol Support**](/docs/node-red/protocol/)**:** Modbus TCP/RTU, RS232, raw TCP, and EtherNet/IP for direct communication with sensors, actuators, scanners, and motors
- **Enterprise System Integration:** MongoDB for serialized telemetry, DronaHQ for dashboards, NetSuite for ERP data, and Limble for maintenance event syncing
- **Alerting:** Slack API integration for instant anomaly and downtime alerts
- **Deployment Management:** FlowFuse pipelines for pushing device configuration updates to full device groups across the factory floor
diff --git a/src/customer-stories/energinet-streamlining-critical-infrastructure-data-management.md b/src/customer-stories/energinet-streamlining-critical-infrastructure-data-management.md
index 87d967918e..0451c57ee2 100644
--- a/src/customer-stories/energinet-streamlining-critical-infrastructure-data-management.md
+++ b/src/customer-stories/energinet-streamlining-critical-infrastructure-data-management.md
@@ -70,8 +70,8 @@ FlowFuse serves as a production infrastructure connecting Energinet's operationa
### Operational System Delivery:
-- Time-Series Analytics: Governed delivery to [InfluxDB](/node-red/database/influxdb/) databases supporting trend analysis, predictive maintenance, and renewable integration planning
-- Operational Databases: Structured routing to [MySQL](/node-red/database/mysql/) systems for compliance reporting and operational record-keeping
+- Time-Series Analytics: Governed delivery to [InfluxDB](/docs/node-red/database/influxdb/) databases supporting trend analysis, predictive maintenance, and renewable integration planning
+- Operational Databases: Structured routing to [MySQL](/docs/node-red/database/mysql/) systems for compliance reporting and operational record-keeping
- Real-Time Monitoring: Live data feeds to Grafana visualization platforms enabling 24/7 grid surveillance and incident response
### Enterprise Governance Meeting TSO Standards
diff --git a/src/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing.md b/src/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing.md
index 72cd575098..f015853e96 100644
--- a/src/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing.md
+++ b/src/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing.md
@@ -50,7 +50,7 @@ Manufacturing environments, particularly those involving electro-coating process
#### Hardware Challenges and Solutions
-Abrasive Technology initially utilized [Raspberry Pis](/node-red/hardware/raspberry-pi-4/) for hosting Node-RED instances, linking them to industrialized touch screens for [Human-Machine Interfaces (HMIs)](/blog/2025/11/building-hmi-for-equipment-control/). While Raspberry Pis proved reliable for many applications, certain projects pushed hardware capabilities. Dudley highlights: "I have found them to be very reliable, but on the current project, I'm absolutely bumping up against its hardware capabilities." The anticipation of the [Pi5](/node-red/hardware/raspberry-pi-5/), with enhanced capabilities, holds promise, and Dudley is eagerly awaiting its arrival.
+Abrasive Technology initially utilized [Raspberry Pis](/docs/node-red/hardware/raspberry-pi-4/) for hosting Node-RED instances, linking them to industrialized touch screens for [Human-Machine Interfaces (HMIs)](/blog/2025/11/building-hmi-for-equipment-control/). While Raspberry Pis proved reliable for many applications, certain projects pushed hardware capabilities. Dudley highlights: "I have found them to be very reliable, but on the current project, I'm absolutely bumping up against its hardware capabilities." The anticipation of the [Pi5](/docs/node-red/hardware/raspberry-pi-5/), with enhanced capabilities, holds promise, and Dudley is eagerly awaiting its arrival.
In addition to Raspberry Pis, Abrasive Technology currently operates a server running Node-RED locally alongside instances on FlowFuse. The company is actively transitioning its Node-RED instances from locally maintained servers to FlowFuse Cloud to make it easier to manage them. Dudley notes, "We also currently have a server Running Node-RED locally as well as instances on FlowFuse. Our search for a better way to host and manage our Node-RED instances is what started our transition to using FlowFuse."
diff --git a/src/customer-stories/leveraging-node-red-and-flowfuse-to-revolutionize-irrigation.md b/src/customer-stories/leveraging-node-red-and-flowfuse-to-revolutionize-irrigation.md
index 88898788d2..6f18882080 100644
--- a/src/customer-stories/leveraging-node-red-and-flowfuse-to-revolutionize-irrigation.md
+++ b/src/customer-stories/leveraging-node-red-and-flowfuse-to-revolutionize-irrigation.md
@@ -55,7 +55,7 @@ Recognizing the need for a technological overhaul, PIDD embraced Node-RED, a flo
#### Evolving Hardware Landscape
-Initially relying on [Raspberry Pi](/node-red/hardware/raspberry-pi-4/) devices, PIDD faced challenges related to heat, especially in Arizona's high-temperature environment. To address this, they decided to transition to [PLCs](/blog/2025/12/what-is-plc/), with the Groov EPIC system being the preferred choice for improved stability in industrial use.
+Initially relying on [Raspberry Pi](/docs/node-red/hardware/raspberry-pi-4/) devices, PIDD faced challenges related to heat, especially in Arizona's high-temperature environment. To address this, they decided to transition to [PLCs](/blog/2025/12/what-is-plc/), with the Groov EPIC system being the preferred choice for improved stability in industrial use.
#### Impact on Operations
@@ -69,7 +69,7 @@ The adoption of Node-RED and FlowFuse had a profound impact on PIDD's operations
PIDD's forward-thinking approach extends to their future plans:
- Transition to PLCs: The organization is moving towards PLCs, specifically the Groov EPIC system used by [Opto 22](/customer-stories/opto22-embraces-node-red/), for enhanced stability in challenging environmental conditions.
- Command and Control with Ignition: Consideration is given to using [Ignition](/vs/ignition/) for command and control, complemented by [MQTT](/blog/2024/06/how-to-use-mqtt-in-node-red/) for data communication.
-- Data Retention Strategy: PIDD aims to implement a robust data retention strategy, possibly involving relational databases like [MySQL](/node-red/database/mysql/) or [Postgres](/node-red/database/postgresql/), to store and retrieve historical data.
+- Data Retention Strategy: PIDD aims to implement a robust data retention strategy, possibly involving relational databases like [MySQL](/docs/node-red/database/mysql/) or [Postgres](/docs/node-red/database/postgresql/), to store and retrieve historical data.
#### Conclusion
diff --git a/src/customer-stories/opto22-embraces-node-red.md b/src/customer-stories/opto22-embraces-node-red.md
index 5c1f86d36a..355c7dc384 100644
--- a/src/customer-stories/opto22-embraces-node-red.md
+++ b/src/customer-stories/opto22-embraces-node-red.md
@@ -30,7 +30,7 @@ Opto 22 is a US-based industrial automation hardware and software manufacturer b
-Opto 22 has a history of innovation in the industrial hardware industry, being the first vendor to standardize on Ethernet and TCP/IP connections, being one of the original vendors behind the [OPC standard](/node-red/flowfuse/edge/opcua/), and participating in the steering committee of the more recent [Sparkplug standard](/blog/2024/08/using-mqtt-sparkplugb-with-node-red/).
+Opto 22 has a history of innovation in the industrial hardware industry, being the first vendor to standardize on Ethernet and TCP/IP connections, being one of the original vendors behind the [OPC standard](/docs/flowfuse-nodes/edge/opcua/), and participating in the steering committee of the more recent [Sparkplug standard](/blog/2024/08/using-mqtt-sparkplugb-with-node-red/).
In 2014, Opto 22 was introducing its first Linux-based hardware called the Opto groov Appliance. At this time, Benson Hougland, VP of Marketing and Product Strategy at Opto 22, and Ben Orchard, Application Engineer at Opto 22 saw a need to make it easier to connect Opto 22 hardware with other devices, sensors, and actuators, in addition to connecting with emerging data services in the cloud. Through lots of research and experimentation, they discovered Node-RED and found it a natural fit for process control engineers who were used to ‘programming’ with flowcharts and diagrams. When Opto 22 introduced its groov EPIC edge programmable industrial controller platform to market in 2018, Node-RED came pre-installed on the device.
@@ -42,9 +42,9 @@ Node-RED is being used by Opto 22 customers in a range of use cases. Some of the
1. **Collecting operational data and storing this data in a [SQL database](/use-cases/data-integration/)**. According to Hougland, ‘The ability to collect sensor data, control variables, etc., and store this data in a SQL database is very easy with Node-RED. There are other middleware solutions available but Node-RED makes it dead simple.’
2. **Accessing external data from the control system to make control decisions**. It is often the case that industrial equipment needs access to external data to make control system decisions. For example, weather data is used in lots of applications to determine the efficiency of machinery. Opto 22 has a customer that manages wind turbines in California. The electrical utility will charge a negative tariff if the turbines generate power for the grid when the grid is over-saturated. Therefore, the Opto 22 customer has deployed Opto groov hardware at each turbine that uses Node-RED to access, every 5 minutes, the spot price for the grid, to determine if the turbine should be generating power or not. The customer realized immediate ROI by reducing their negative tariff charges.
-3. **Creating alerts and notifications for industrial events**. Manufacturers often need to send alerts or notifications when a piece of equipment fails or needs servicing. Node-RED makes it very easy to send an alert or notification to any communication channel. Node-RED nodes are available for sending notifications via [email](/node-red/notification/email/), text, text-to-speech, Slack, changing stack lights, etc.
-4. **Reading [Modbus](/node-red/flowfuse/edge/modbus/) data.** Modbus is a widely used protocol for industrial devices that is supported on groov EPIC. Opto 22 customers are using Node-RED to transform Modbus registry data into human-readable, contextual data that can be used by other applications on the EPIC or sent to the [cloud for processing](/use-cases/data-integration/).
-5. **Bridging new equipment with legacy equipment.** Manufacturing plants have significant investments in legacy equipment that sometimes isn’t easy to access. This makes it difficult to introduce new technologies, like vision analytics, into a [manufacturing line](/use-cases/shop-floor-communication/). Opto 22 customer, [Intrinsics](https://opto22.com/products/groov-case-studies/case-study-intrinsics) wanted to improve their quality control using cameras and vision analytics. They added a [camera](/node-red/flowfuse/edge/rtsp/) to the manufacturing line that sent images directly to a cloud vision analytics service that evaluates a pass or fail. However, the [PLC](/blog/2025/12/what-is-plc/) controlling the line could not receive the pass/fall notification. Therefore, they added a groov Rio, running Node-RED that received the pass/fail message from the analytics service and was able to connect with the legacy PLC to forward the control decision.
+3. **Creating alerts and notifications for industrial events**. Manufacturers often need to send alerts or notifications when a piece of equipment fails or needs servicing. Node-RED makes it very easy to send an alert or notification to any communication channel. Node-RED nodes are available for sending notifications via [email](/docs/node-red/notification/email/), text, text-to-speech, Slack, changing stack lights, etc.
+4. **Reading [Modbus](/docs/flowfuse-nodes/edge/modbus/) data.** Modbus is a widely used protocol for industrial devices that is supported on groov EPIC. Opto 22 customers are using Node-RED to transform Modbus registry data into human-readable, contextual data that can be used by other applications on the EPIC or sent to the [cloud for processing](/use-cases/data-integration/).
+5. **Bridging new equipment with legacy equipment.** Manufacturing plants have significant investments in legacy equipment that sometimes isn’t easy to access. This makes it difficult to introduce new technologies, like vision analytics, into a [manufacturing line](/use-cases/shop-floor-communication/). Opto 22 customer, [Intrinsics](https://opto22.com/products/groov-case-studies/case-study-intrinsics) wanted to improve their quality control using cameras and vision analytics. They added a [camera](/docs/flowfuse-nodes/edge/rtsp/) to the manufacturing line that sent images directly to a cloud vision analytics service that evaluates a pass or fail. However, the [PLC](/blog/2025/12/what-is-plc/) controlling the line could not receive the pass/fall notification. Therefore, they added a groov Rio, running Node-RED that received the pass/fail message from the analytics service and was able to connect with the legacy PLC to forward the control decision.
‘Node-RED makes it so easy to extend and innovate on a hardware platform,’ comments Ben Orchard. ‘The industrial automation industry has a history of offering proprietary drivers to add functionality to a hardware platform. People are tired of this approach. Using Node-RED only requires that you have a web browser to get access to your data and put the data anywhere. It really promotes a lot of freedom and innovation. Node-RED’s open approach is definitely the present and the future for our industry.’
diff --git a/src/customer-stories/scaling-industrial-iot-operations-while-maintaining-competitive-edge.md b/src/customer-stories/scaling-industrial-iot-operations-while-maintaining-competitive-edge.md
index aa1db81743..bb83ce6205 100644
--- a/src/customer-stories/scaling-industrial-iot-operations-while-maintaining-competitive-edge.md
+++ b/src/customer-stories/scaling-industrial-iot-operations-while-maintaining-competitive-edge.md
@@ -55,7 +55,7 @@ FlowFuse processes and routes production data to multiple systems including:
- **[MES](/use-cases/mes/) Integration:** Providing real-time machine status, runtime data, and downtime analysis
- **SAP Connectivity:** Enabling bidirectional [data integration](/use-cases/data-integration/) for production planning and resource management
-- **[InfluxDB](/node-red/database/influxdb/) Storage:** Long-term historical data storage for trend analysis and optimization
+- **[InfluxDB](/docs/node-red/database/influxdb/) Storage:** Long-term historical data storage for trend analysis and optimization
- **Live Dashboards:** Real-time production visualization and monitoring for operators and management
- **Custom HMI Systems:** User interfaces for machine operators to input process parameters and quality data
- **SaaS Energy Management System:** Optenda collects power consumption of some machines directly from the FlowFuse Broker
@@ -91,7 +91,7 @@ FlowFuse processes and routes production data to multiple systems including:
The solution architecture includes:
-- **Device Agent Deployment:** Shop floor-level agents connecting directly to manufacturing equipment via [OPC-UA](/node-red/flowfuse/edge/opcua/) and other industrial protocols
+- **Device Agent Deployment:** Shop floor-level agents connecting directly to manufacturing equipment via [OPC-UA](/docs/flowfuse-nodes/edge/opcua/) and other industrial protocols
- **Encrypted Cloud Connectivity:** Secure communication between edge devices and FlowFuse cloud platform
- **Data Processing Pipelines:** Node-RED flows handling data transformation, validation, and routing to multiple downstream systems
- **Multi-System Integration:** Connections to MES, SAP, InfluxDB, custom dashboards, and HMI systems
diff --git a/src/customer-stories/stfi-future-of-textile-powered-by-node-red.md b/src/customer-stories/stfi-future-of-textile-powered-by-node-red.md
index dbacba60fd..d80648278f 100644
--- a/src/customer-stories/stfi-future-of-textile-powered-by-node-red.md
+++ b/src/customer-stories/stfi-future-of-textile-powered-by-node-red.md
@@ -32,7 +32,7 @@ The Model Factory consists of a number of different manufacturing applications,
{data-zoomable}
_Figure 2: [Job Control](https://youtu.be/cgtHO1OVkV8?si=oTpLigbmfqlZ-8Bi&t=98) with State-Machine and OPC-UA-Connection to the machines like a [laser cutter](https://youtu.be/eUkZ8R1tNM4?si=uOwL-XGf0uKkXdVL) (ⒸSTFI)_
-The [job control system](https://youtu.be/cgtHO1OVkV8?si=oTpLigbmfqlZ-8Bi&t=98) is a set of state machines built in Node-RED, see figure 2. Each state machine represents a different machine like a [mobile robot](https://youtu.be/Z_e6EcT2mQs?si=DfxQS0K16bcrpixi) and includes different communication nodes to the different pieces of equipment on the [factory floor](/use-cases/shop-floor-communication/). Node-RED was well suited for developing these state machines since state machines are well represented in flow based programming and also Node-RED provides support for the different communication protocols like [OPC UA](/node-red/flowfuse/edge/opcua/). Node-RED was also used to control the automated guided vehicle and the collaborative robot UR10e within the mobile robot, see figure 3.
+The [job control system](https://youtu.be/cgtHO1OVkV8?si=oTpLigbmfqlZ-8Bi&t=98) is a set of state machines built in Node-RED, see figure 2. Each state machine represents a different machine like a [mobile robot](https://youtu.be/Z_e6EcT2mQs?si=DfxQS0K16bcrpixi) and includes different communication nodes to the different pieces of equipment on the [factory floor](/use-cases/shop-floor-communication/). Node-RED was well suited for developing these state machines since state machines are well represented in flow based programming and also Node-RED provides support for the different communication protocols like [OPC UA](/docs/flowfuse-nodes/edge/opcua/). Node-RED was also used to control the automated guided vehicle and the collaborative robot UR10e within the mobile robot, see figure 3.
{data-zoomable}
_Figure 3: Within the [mobile Robot](https://youtu.be/Z_e6EcT2mQs?si=DfxQS0K16bcrpixi), control the Automated Guided Vehicle and the Universal Robot via OPC UA and Real-Time Data Exchange (ⒸSTFI)_
diff --git a/src/node-red/index.njk b/src/node-red/index.njk
index 431edba5dc..6bfc1aff84 100644
--- a/src/node-red/index.njk
+++ b/src/node-red/index.njk
@@ -310,13 +310,13 @@ sitemapPriority: 0.8
- SEE MORE
+ SEE MOREGET IMPLEMENTATION SUPPORT
diff --git a/src/webinars/2026/industrial-iot-solutions.md b/src/webinars/2026/industrial-iot-solutions.md
index fa29e2fd4a..952ac49da0 100644
--- a/src/webinars/2026/industrial-iot-solutions.md
+++ b/src/webinars/2026/industrial-iot-solutions.md
@@ -10,7 +10,7 @@ meta:
Industrial IoT (IIoT) is the use of connected sensors and devices to collect data from industrial operations and feed it into software for monitoring and real-time action. FlowFuse is the platform that runs on top of that data: the flows that connect it, the dashboards that display it, and the governance that keeps it under control.
- question: "What's the difference between FlowFuse Hub, Edge, and Fleet?"
answer: >
- Hub integrates enterprise systems and APIs for IT teams. Edge connects PLCs and machines across sites for OT teams. Fleet manages Node-RED across thousands of distributed devices. All three run on the same platform; the difference is which deployment shape each one is built for.
+ Hub integrates enterprise systems and APIs for IT teams. Edge connects PLCs and machines across sites for OT teams. Fleet manages Node-RED across thousands of distributed devices. All three run on the same platform; the difference is which deployment shape each one is built for.
- question: "How do I know which industrial IoT solution is right for my setup?"
answer: >
It comes down to where your applications live: a data center (Hub), a plant floor (Edge), or thousands of field devices (Fleet). We'll walk through all three live in the webinar and help map your specific setup to one of them. Bring your architecture and we'll sort it live in the Q&A.
@@ -44,7 +44,7 @@ Join Kristopher Sandoval (Product Marketing), Jamie Strusz (Engineering), Drew G
## Which industrial IoT solution fits my setup?
-- FlowFuse Hub integrates [enterprise systems](/landing/enterprise-integration/) and [APIs](/node-red/core-nodes/http-request/), FlowFuse Edge connects [PLCs](/landing/plc/) and machines across sites, FlowFuse Fleet manages [Node-RED](/node-red/) across thousands of devices. We'll map your setup to one of the three, live.
+- FlowFuse Hub integrates [enterprise systems](/landing/enterprise-integration/) and [APIs](/docs/node-red/core-nodes/http-request/), FlowFuse Edge connects [PLCs](/landing/plc/) and machines across sites, FlowFuse Fleet manages [Node-RED](/node-red/) across thousands of devices. We'll map your setup to one of the three, live.
- Build faster with Industrial AI: [FlowFuse Expert](/docs/user/expert/) opens its [MCP server](/blog/2025/10/building-mcp-server-using-flowfuse/) to 3rd party agents, such as Cursor, Claude Code, Gemini CLI, and even in-house agents. Any MCP-speaking agent works. Point your AI agent at your FlowFuse instance and it builds, debugs, and fixes flows directly.
- See what's running, everywhere: [role-based access control](/docs/user/role-based-access-control/), [audit logging](/docs/user/logs/#audit-log), and [one place to manage every instance and device](/blog/2024/10/managing-node-red-instances-in-centralize-platfrom/), no matter which product you run.
- Read a [dashboard](/platform/dashboard/) at a glance: new [built-in themes](https://dashboard.flowfuse.com/nodes/config/ui-theme.html) with WCAG AA contrast, [charts](https://dashboard.flowfuse.com/nodes/widgets/ui-chart) that render instead of leaving empty boxes, and readable dates instead of raw timestamps.
diff --git a/src/webinars/2026/modbus-communications-modernization.md b/src/webinars/2026/modbus-communications-modernization.md
index d258127976..f9f28a80c8 100644
--- a/src/webinars/2026/modbus-communications-modernization.md
+++ b/src/webinars/2026/modbus-communications-modernization.md
@@ -44,7 +44,7 @@ This is the core of the session: a practical framework for deciding when to leav
Around the halfway mark, Kristopher switches over to a live demo in FlowFuse and Node-RED: raw Modbus registers going in one end, and a named, typed, dashboarded data stream coming out the other. If you'd rather follow along with a written walkthrough afterward, our [Using Modbus with FlowFuse](/blog/2025/09/using-modbus-with-flowfuse/) guide covers the same setup step by step.
-Also worth a look once you're in the recording: the live Q&A, where attendees bring in real issues, from Modbus TCP connections that quietly go blank to how much visibility you can actually get into what's on the bus. If any of that sounds familiar, our posts on [Modbus polling best practices](/blog/2026/04/modbus-polling-best-practices/) and [diagnosing Modbus degradation](/blog/2026/04/diagnosing-modbus-degradation/) dig further into the same failure modes. And if you're still weighing Modbus against the alternatives, [why OPC UA isn't replacing Modbus yet](/blog/2026/03/why-opcua-is-not-replacing-modbus-yet/) and [Modbus TCP vs. Modbus RTU](/blog/2026/02/modbus-tcp-vs-modbus-rtu/) are good next reads, and the [Modbus protocol page](/node-red/protocol/modbus/) has the full Node-RED node documentation whenever you're ready to build.
+Also worth a look once you're in the recording: the live Q&A, where attendees bring in real issues, from Modbus TCP connections that quietly go blank to how much visibility you can actually get into what's on the bus. If any of that sounds familiar, our posts on [Modbus polling best practices](/blog/2026/04/modbus-polling-best-practices/) and [diagnosing Modbus degradation](/blog/2026/04/diagnosing-modbus-degradation/) dig further into the same failure modes. And if you're still weighing Modbus against the alternatives, [why OPC UA isn't replacing Modbus yet](/blog/2026/03/why-opcua-is-not-replacing-modbus-yet/) and [Modbus TCP vs. Modbus RTU](/blog/2026/02/modbus-tcp-vs-modbus-rtu/) are good next reads, and the [Modbus protocol page](/docs/node-red/protocol/modbus/) has the full Node-RED node documentation whenever you're ready to build.
## Who This Session Is For
diff --git a/src/webinars/2026/the-agile-factory-on-the-connected-edge.md b/src/webinars/2026/the-agile-factory-on-the-connected-edge.md
index bee86babe4..a5b4360931 100644
--- a/src/webinars/2026/the-agile-factory-on-the-connected-edge.md
+++ b/src/webinars/2026/the-agile-factory-on-the-connected-edge.md
@@ -25,8 +25,8 @@ In this session, Rhythm Agarwal (Senior Manufacturing Test Engineer at Aperia) a
## How Aperia Replaced Traditional PLCs with Edge Applications
- **Extends the shop floor, doesn't replace it.** Smart fixtures run on FlowFuse instead of a PLC, so existing equipment keeps working, just connected.
-- **Speaks every protocol.** Sensors, scanners, and actuators connect over [MODBUS-TCP](/node-red/protocol/modbus/), [RS-232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), and [raw TCP](/node-red/core-nodes/tcp-in/), all feeding into one data flow.
-- **Logs data the moment it happens.** Every station writes to a shared [MongoDB](/node-red/database/mongodb/) backend in real time, not on a delay.
+- **Speaks every protocol.** Sensors, scanners, and actuators connect over [MODBUS-TCP](/docs/node-red/protocol/modbus/), [RS-232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), and [raw TCP](/docs/node-red/core-nodes/tcp-in/), all feeding into one data flow.
+- **Logs data the moment it happens.** Every station writes to a shared [MongoDB](/docs/node-red/database/mongodb/) backend in real time, not on a delay.
- **One fix, applied everywhere.** Code changes roll out to entire device groups from a single pipeline, with full version control, not machine by machine.
## Three Manufacturing Applications Built on the Edge
From 7ccace08565d92c5a51c1708f60030cc2887846d Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 18:18:07 +0200
Subject: [PATCH 06/17] docs: fix the anchors that Nuxt slugs differently from
Eleventy
Eleventy's markdown-it-anchor and @nuxt/content do not agree on heading ids, so
every in-page link written against the old slugs broke on the move.
"## 5. Configure a Connection" was #5.-configure-a-connection and is now
#_5-configure-a-connection: the dot is dropped and a leading digit gains an
underscore, since an HTML id may not start with a digit. Confirmed against the
deploy preview's rendered ids rather than inferred. 38 anchors across 6 files.
Also drops a stale %3F from an anchor targeting "## What is Modbus?" - the
question mark is not part of the slug - and repoints the last link to
/node-red/learn/, which no longer exists: its content is the /docs/node-red/
index now.
Found the rest of the class locally instead of one CI run at a time, by slugging
every heading in the migrated tree and checking each internal anchor against it.
All 56 resolve.
---
.../flowfuse-nodes/edge/modbus.md | 2 +-
.../flowfuse-nodes/edge/opcua.md | 54 +++++++++----------
nuxt/pages/integrations/opcua.vue | 12 ++---
...y-node-red-with-flowfuse-to-balenacloud.md | 2 +-
src/blog/2024/01/import-a-file.md | 2 +-
src/blog/2024/01/send-a-file.md | 2 +-
src/node-red/index.njk | 2 +-
7 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/modbus.md b/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
index a1ecda2585..b1431fa5cb 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
@@ -158,7 +158,7 @@ The Modbus-Read node polls a fixed location on a repeating interval — the node
### Configuration
- **FC (Function Code)** — which table to read: Coils, Discrete Inputs, Holding Registers, or Input Registers (FC 1–4).
-- **Address** — the starting zero-based address in that table — see [Register addressing](#what-is-modbus%3F) if your device manual or documentation uses 4xxxx-style numbering.
+- **Address** — the starting zero-based address in that table — see [Register addressing](#what-is-modbus) if your device manual or documentation uses 4xxxx-style numbering.
- **Quantity** — how many consecutive coils/registers to read in one request.
- **Poll rate** — how often to repeat the read (milliseconds).
- **Unit ID** — overrides the connection's default unit ID for this node, when needed.
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/opcua.md b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
index 72327db9a0..0b227f32f7 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
@@ -14,24 +14,24 @@ This is a **FlowFuse Certified Node**. Unlike community nodes, which vary in qua
## Table of contents
-1. [Overview](#1.-overview)
-2. [Get the certified node in FlowFuse](#2.-get-the-certified-node-in-flowfuse)
-3. [The node set](#3.-the-node-set)
-4. [NodeIds and how to address data](#4.-nodeids-and-how-to-address-data)
-5. [Configure a connection](#5.-configure-a-connection)
-6. [Read](#6.-read)
-7. [Write](#7.-write)
-8. [Extension Object](#8.-extension-object)
-9. [Call](#9.-call)
-10. [Monitor](#10.-monitor)
-11. [Monitor Event](#11.-monitor-event)
-12. [Browse](#12.-browse)
-13. [Explore](#13.-explore)
-14. [History Read](#14.-history-read)
-15. [File Operation](#15.-file-operation)
-16. [Hosting an OPC UA server](#16.-hosting-an-opc-ua-server)
-17. [Network requirements](#17.-network-requirements)
-18. [Troubleshooting](#18.-troubleshooting)
+1. [Overview](#_1-overview)
+2. [Get the certified node in FlowFuse](#_2-get-the-certified-node-in-flowfuse)
+3. [The node set](#_3-the-node-set)
+4. [NodeIds and how to address data](#_4-nodeids-and-how-to-address-data)
+5. [Configure a connection](#_5-configure-a-connection)
+6. [Read](#_6-read)
+7. [Write](#_7-write)
+8. [Extension Object](#_8-extension-object)
+9. [Call](#_9-call)
+10. [Monitor](#_10-monitor)
+11. [Monitor Event](#_11-monitor-event)
+12. [Browse](#_12-browse)
+13. [Explore](#_13-explore)
+14. [History Read](#_14-history-read)
+15. [File Operation](#_15-file-operation)
+16. [Hosting an OPC UA server](#_16-hosting-an-opc-ua-server)
+17. [Network requirements](#_17-network-requirements)
+18. [Troubleshooting](#_18-troubleshooting)
## 1. Overview
@@ -61,14 +61,14 @@ A filling machine exposes `FillLevel`, `LineSpeed`, and an alarm hierarchy throu
#### Where this shows up in practice
-Real deployments usually combine several of the nodes on a single shared connection (see [The node set](#3.-the-node-set) for what each one does):
+Real deployments usually combine several of the nodes on a single shared connection (see [The node set](#_3-the-node-set) for what each one does):
-- **Unified Namespace and broker integration**: subscribe to the variables that matter with [Monitor](#10.-monitor) and publish changes to MQTT, so every system in the plant consumes the same live equipment data instead of integrating with each machine separately.
-- **Supervisory control**: fetch current values on demand with [Read](#6.-read), push setpoints and recipe parameters back with [Write](#7.-write), and invoke machine operations such as starting a batch or acknowledging an alarm with [Call](#9.-call). Writes change real-world state, so gate them behind validation or an operator confirmation step.
-- **Alarm-driven maintenance**: subscribe to a server's alarms and events with [Monitor Event](#11.-monitor-event), filter by type and severity on the server side, and route what remains to notifications, dashboards, or logs, so the maintenance team hears about a fault from the machine, not from the operator.
-- **Shift reports, trends, and audits**: pull raw or server-aggregated values over a time range with [History Read](#14.-history-read) to build end-of-shift reports, quality trends, and incident timelines from data the equipment already recorded.
-- **Fast commissioning**: map a machine's entire address space with [Explore](#13.-explore) and feed the result straight into a Monitor node, turning hours of manual tag mapping into a single deploy.
-- **Exposing flow data to SCADA and historians**: on self-hosted FlowFuse, [host an OPC UA server](#16.-hosting-an-opc-ua-server) so external OPC UA clients, including another FlowFuse instance, can read the data your flows produce. This is not available on FlowFuse Cloud.
+- **Unified Namespace and broker integration**: subscribe to the variables that matter with [Monitor](#_10-monitor) and publish changes to MQTT, so every system in the plant consumes the same live equipment data instead of integrating with each machine separately.
+- **Supervisory control**: fetch current values on demand with [Read](#_6-read), push setpoints and recipe parameters back with [Write](#_7-write), and invoke machine operations such as starting a batch or acknowledging an alarm with [Call](#_9-call). Writes change real-world state, so gate them behind validation or an operator confirmation step.
+- **Alarm-driven maintenance**: subscribe to a server's alarms and events with [Monitor Event](#_11-monitor-event), filter by type and severity on the server side, and route what remains to notifications, dashboards, or logs, so the maintenance team hears about a fault from the machine, not from the operator.
+- **Shift reports, trends, and audits**: pull raw or server-aggregated values over a time range with [History Read](#_14-history-read) to build end-of-shift reports, quality trends, and incident timelines from data the equipment already recorded.
+- **Fast commissioning**: map a machine's entire address space with [Explore](#_13-explore) and feed the result straight into a Monitor node, turning hours of manual tag mapping into a single deploy.
+- **Exposing flow data to SCADA and historians**: on self-hosted FlowFuse, [host an OPC UA server](#_16-hosting-an-opc-ua-server) so external OPC UA clients, including another FlowFuse instance, can read the data your flows produce. This is not available on FlowFuse Cloud.
## 2. Get the Certified Node in FlowFuse
@@ -243,7 +243,7 @@ Auto-accepting the server certificate is convenient but means the client does no
-Client and server nodes in your instance share one PKI store. On FlowFuse it lives under `/opcua-for-flow-fuse/PKI`. For trust decisions to survive restarts and redeploys, that directory must be on persistent storage, see [Hosting an OPC UA server](#16.-hosting-an-opc-ua-server) for the storage details, which apply to client connections too.
+Client and server nodes in your instance share one PKI store. On FlowFuse it lives under `/opcua-for-flow-fuse/PKI`. For trust decisions to survive restarts and redeploys, that directory must be on persistent storage, see [Hosting an OPC UA server](#_16-hosting-an-opc-ua-server) for the storage details, which apply to client connections too.
If the client certificate is not yet trusted by the server, **Check Connection** reports the handshake failure and reminds you to add the client certificate to the server's trusted list:
@@ -568,7 +568,7 @@ The Monitor Event node subscribes to events and alarms rather than value changes
- **Where Clause**, a server-side filter selecting which events to receive. Far more efficient than post-filtering in a Function node.
- **Select Clause**: comma-separated fields to retrieve (e.g. `EventId, Time, Message, Severity`). The `...` button opens a graphical selector that browses the event type hierarchy. Request only the fields you use: smaller messages, less processing. Common sets: basic `EventId,Time,Message,Severity`; alarms add `SourceName,ActiveState,AckedState`; audits add `ActionTimeStamp,ClientUserId`.
-Match the subscription to the event rate: a slower publishing interval suits alarms and audits, a faster one suits high-frequency process events. For mixed workloads, use separate Monitor Event nodes on different subscriptions (see [Configure a connection](#5.-configure-a-connection)) so critical alarms don't queue behind noisy low-priority events.
+Match the subscription to the event rate: a slower publishing interval suits alarms and audits, a faster one suits high-frequency process events. For mixed workloads, use separate Monitor Event nodes on different subscriptions (see [Configure a connection](#_5-configure-a-connection)) so critical alarms don't queue behind noisy low-priority events.
### Where Clause syntax
diff --git a/nuxt/pages/integrations/opcua.vue b/nuxt/pages/integrations/opcua.vue
index 402f0b779a..357332e0dc 100644
--- a/nuxt/pages/integrations/opcua.vue
+++ b/nuxt/pages/integrations/opcua.vue
@@ -48,42 +48,42 @@ const capabilities = [
title: 'Structured Information Model',
description: 'Browse a real address space of objects, types, and relationships instead of a flat register list. Data arrives with a name and structure attached, not a bare tag number.',
linkText: 'Browse & explore the address space',
- url: '/docs/flowfuse-nodes/edge/opcua/#12.-browse',
+ url: '/docs/flowfuse-nodes/edge/opcua/#_12-browse',
},
{
icon: 'i-heroicons-bolt',
title: 'Real-Time Subscriptions',
description: 'Subscribe to nodes and get notified only when values change. No polling every tag on a fixed interval, no wasted controller scan budget.',
linkText: 'Monitor node reference',
- url: '/docs/flowfuse-nodes/edge/opcua/#10.-monitor',
+ url: '/docs/flowfuse-nodes/edge/opcua/#_10-monitor',
},
{
icon: 'i-heroicons-clock',
title: 'Historical Access & Logging',
description: 'Read a server\'s native History (HA) service, or log live values into InfluxDB or TimescaleDB. Trends stay queryable long after the PLC buffer rolls over.',
linkText: 'History Read node reference',
- url: '/docs/flowfuse-nodes/edge/opcua/#14.-history-read',
+ url: '/docs/flowfuse-nodes/edge/opcua/#_14-history-read',
},
{
icon: 'i-heroicons-bell-alert',
title: 'Alarms & Events',
description: 'Subscribe to condition-based alarms natively, filtered by type and severity on the server side. Stop polling fault bits and reconstructing state changes after the fact.',
linkText: 'Monitor Event node reference',
- url: '/docs/flowfuse-nodes/edge/opcua/#11.-monitor-event',
+ url: '/docs/flowfuse-nodes/edge/opcua/#_11-monitor-event',
},
{
icon: 'i-heroicons-shield-check',
title: 'Security Built Into the Spec',
description: 'X.509 certificate authentication, user authentication, and SignAndEncrypt come with the standard. Manage the certificate trust list yourself instead of leaving it on vendor defaults.',
linkText: 'Configure a secure connection',
- url: '/docs/flowfuse-nodes/edge/opcua/#5.-configure-a-connection',
+ url: '/docs/flowfuse-nodes/edge/opcua/#_5-configure-a-connection',
},
{
icon: 'i-heroicons-server-stack',
title: 'Client + Server, One Runtime',
description: 'Read and write a third-party server as a client. On self-hosted FlowFuse, expose your own information model as a server, side by side.',
linkText: 'Hosting an OPC UA server',
- url: '/docs/flowfuse-nodes/edge/opcua/#16.-hosting-an-opc-ua-server',
+ url: '/docs/flowfuse-nodes/edge/opcua/#_16-hosting-an-opc-ua-server',
},
]
diff --git a/src/blog/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud.md b/src/blog/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud.md
index 2d43a66f50..0fa5b2a1da 100644
--- a/src/blog/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud.md
+++ b/src/blog/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud.md
@@ -59,7 +59,7 @@ Integrating InfluxDB enables effective data storage and management:
### Data Generation and Management
For testing, we have created a flow to get you started. Follow this [link](https://flows.nodered.org/flow/66f37bb739b6cdb0c7ad3a4e2edd68ef) and import it. There are four sets of flows for you to begin with. The first is for data generation. The second is a manual data generation flow. The third is key as it initiates the creation of a database, in this case, **mydb**. The last flow is a simple query that pulls data from InfluxDB.
-1. Import the flows into your FlowFuse instance of Node-RED and deploy. Follow these [instructions](/blog/2023/03/3-quick-node-red-tips-5/#2.-import-helpful-example-flows-provided-with-custom-nodes) for importing and exporting.
+1. Import the flows into your FlowFuse instance of Node-RED and deploy. Follow these [instructions](/blog/2023/03/3-quick-node-red-tips-5/#_2-import-helpful-example-flows-provided-with-custom-nodes) for importing and exporting.
2. Return to Flowfuse, go to your instance, and create another [snapshot](/docs/user/snapshots/#create-a-snapshot).
3. Ensure that you **Set Target Snapshot**.
diff --git a/src/blog/2024/01/import-a-file.md b/src/blog/2024/01/import-a-file.md
index 6567e8a124..edba5b1a08 100644
--- a/src/blog/2024/01/import-a-file.md
+++ b/src/blog/2024/01/import-a-file.md
@@ -30,7 +30,7 @@ This simple flow allows the user to visualize data from a CSV in the [Node-RED D
### Instructions ###
1. Install Node-RED Dashboard 2.0. Follow these [instructions](/blog/2024/03/dashboard-getting-started/) to install.
-2. Import Flow - to import the flow into your Node-RED instance follow these [instructions](/blog/2023/03/3-quick-node-red-tips-5/#1.-copy-and-share-your-flows-using-export-and-import).
+2. Import Flow - to import the flow into your Node-RED instance follow these [instructions](/blog/2023/03/3-quick-node-red-tips-5/#_1-copy-and-share-your-flows-using-export-and-import).
3. Access Dashboard - To access the dashboard, navigate to the `https://.flowfuse.cloud/dashboard`.
This dashboard is currently configured to take in CSV files and transform them into a single message that is sent to the table for visualization. Simultaneously the data from the import is stored locally in the flow context. From there, the button can be used to trigger the sending of the data from the flow context to the next destination. In this case, it is a simple debug node.
diff --git a/src/blog/2024/01/send-a-file.md b/src/blog/2024/01/send-a-file.md
index d1bb878604..56e17c5beb 100644
--- a/src/blog/2024/01/send-a-file.md
+++ b/src/blog/2024/01/send-a-file.md
@@ -132,7 +132,7 @@ Once we have a file ready to be sent, we now need to configure the receiving sid
A link to the flow can be found [here](https://flows.nodered.org/flow/effb53752e5d6f767b3c7e5d41a4a6e8).
-To import the flow, follow these [instructions](/blog/2023/03/3-quick-node-red-tips-5/#1.-copy-and-share-your-flows-using-export-and-import).
+To import the flow, follow these [instructions](/blog/2023/03/3-quick-node-red-tips-5/#_1-copy-and-share-your-flows-using-export-and-import).
A Simple HTTP In node can be used in the form of a Post, ensuring the configuration allows for a file.
diff --git a/src/node-red/index.njk b/src/node-red/index.njk
index 6bfc1aff84..6c0f024e98 100644
--- a/src/node-red/index.njk
+++ b/src/node-red/index.njk
@@ -316,7 +316,7 @@ sitemapPriority: 0.8
|Quick Tips
- SEE MORE
+ SEE MOREGET IMPLEMENTATION SUPPORT
From b46fa5d9bd595fc3bb2886996a904eecdd69fba2 Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Mon, 7 Sep 2026 18:39:07 +0200
Subject: [PATCH 07/17] docs: fix four defects the code review found in the
migration
1. protectMustaches did the opposite of its job on the common case. It checked
only the character immediately either side of a mustache, so one in the MIDDLE
of a longer code span had no backtick beside it, got re-wrapped, and split the
span in two: `
Hello {{payload.name}}!
` became
`
Hello `{{payload.name}}`!
`, leaving the mustache bare for MDC to bind and
turning the prose around it into bogus inline code. It now splits the text on
fences and on code spans and only rewrites what falls between them. Two of the
committed pages carried the damage and are regenerated.
2. The Function node's page and the Function category index resolved to the same
URL. slugFor('Function') is `function`, which is also its category directory, so
core-nodes/function.md and core-nodes/function/index.md both became
/docs/node-red/core-nodes/function/ and @nuxt/content took whichever it indexed
last. The legacy redirect for that path is the node, so the per-category pages
are gone and their grouping is headings on the section index instead. The
redirect map loses the five bare category paths with it: those were never pages
under Eleventy either, since the templates were pagination-only.
3. {% caution %} lost its callout. docs-markdown's convertCallouts knows note,
warning and critical and then strips every remaining Nunjucks tag, so a caution
block silently became an ordinary paragraph. The OPC UA guide flags "server
hosting is not supported on FlowFuse Cloud" with it. Converted locally, in the
same markup shape, before the stripping pass.
4. Two redirects still pointed at moved paths and so double-hopped:
/node-red/core-nodes/mqtt/ and /education/ now go straight to /docs/.
Each of the three code defects gets a regression test naming the failure, since
all three were silent: none produced a build error and CI was green on all of them.
---
.../flowfuse-nodes/edge/opcua.md | 3 +-
.../node-red/getting-started/string.md | 3 +-
nuxt/lib/core-nodes-sync.mjs | 54 +++++++++----------
nuxt/lib/core-nodes-sync.test.mjs | 35 ++++++++++--
nuxt/lib/library-markdown.mjs | 52 ++++++++++++++----
nuxt/lib/library-markdown.test.mjs | 24 +++++++++
nuxt/redirects-node-red.ts | 10 ++--
nuxt/redirects.ts | 4 +-
8 files changed, 130 insertions(+), 55 deletions(-)
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/opcua.md b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
index 0b227f32f7..b2925619b0 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
@@ -5,7 +5,6 @@ navOrder: 50
meta:
description: 'Connect a FlowFuse instance to industrial OPC UA servers: read, write, monitor, call methods, browse, read history, work with files, or host your own OPC UA server. A FlowFuse Certified Node.'
---
-
# OPC UA for FlowFuse - FlowFuse Certified Node
Connect a FlowFuse instance to industrial OPC UA servers. Read and write values, monitor changes in real time, call methods, browse the address space, read history, work with files, or host your own OPC UA server, all from within your flows.
@@ -809,7 +808,9 @@ Use the Browse node to discover available File objects, and the Read node to ins
On **self-hosted FlowFuse**, the certified node can run an OPC UA server inside your instance using only Function nodes, no `settings.js` edit, no external module declaration, no extra npm install.
+
Caution
Server hosting is **not supported on FlowFuse Cloud**, Cloud exposes HTTP/HTTPS only and cannot expose the arbitrary TCP port (`opc.tcp://`) a server needs. Use a self-hosted FlowFuse instance and ensure the chosen port is reachable through your container and network configuration.
+
When the palette loads, it publishes a bootstrap helper in the Node-RED global context. Retrieve it and destructure `{ bootstrap, opcua }`: `bootstrap` carries the server helpers and `opcua` re-exports the full `node-opcua` namespace. This works even with `functionExternalModules: false`.
diff --git a/nuxt/content-guides/node-red/getting-started/string.md b/nuxt/content-guides/node-red/getting-started/string.md
index 9ce47960e6..bf272383fe 100644
--- a/nuxt/content-guides/node-red/getting-started/string.md
+++ b/nuxt/content-guides/node-red/getting-started/string.md
@@ -6,7 +6,6 @@ meta:
description: Learn essential string operations in Node-RED including converting between strings and numbers, splitting and concatenating text, parsing JSON, extracting substrings, trimming whitespace, and more. Step-by-step guide with practical examples.
keywords: node-red strings, working with strings in node-red, node-red string operations, node-red string manipulation, node-red text processing, node-red change node strings, node-red jsonata string functions, node-red template node strings, node-red function node strings, node-red parsing strings, node-red string conversion, node-red split string, node-red concatenate strings
---
-
# Strings in Node-RED: Convert String to Number, Split, Concatenate, Trim, and More
Strings are one of the most common data types in Node-RED. Whether you're converting sensor values, parsing API responses, or building dynamic messages, understanding string operations is essential for building reliable flows.
@@ -81,7 +80,7 @@ Combining strings is common when building messages, URLs, or formatted output.
4. Click **Done**
5. Connect to where you need the processed data
-Each `{{variableName}}` is replaced with actual data. For example, the template `Hello `{{payload.name}}`, your order #`{{payload.orderId}}` has shipped to `{{payload.city}}`.` with data containing name "Sarah", orderId "12345", and city "Portland" produces: `Hello Sarah, your order #12345 has shipped to Portland.`
+Each `{{variableName}}` is replaced with actual data. For example, the template `Hello {{payload.name}}, your order #{{payload.orderId}} has shipped to {{payload.city}}.` with data containing name "Sarah", orderId "12345", and city "Portland" produces: `Hello Sarah, your order #12345 has shipped to Portland.`
::render-flow
```json
diff --git a/nuxt/lib/core-nodes-sync.mjs b/nuxt/lib/core-nodes-sync.mjs
index 51da101c93..ee38ea2ee1 100644
--- a/nuxt/lib/core-nodes-sync.mjs
+++ b/nuxt/lib/core-nodes-sync.mjs
@@ -161,27 +161,29 @@ export function renderCoreNodePage (node, { useCase, help, navOrder }) {
return `---\n${fm}\n---\n\n${body}\n`
}
-/** The category index, listing its nodes. */
-export function renderCategoryPage (category, nodes) {
- const meta = CATEGORIES[category]
- const list = nodes
- .map(n => `- [${n.name}](/docs/node-red/core-nodes/${n.slug}/)${n.description ? `: ${n.description}` : ''}`)
- .join('\n')
-
- return `---
-title: "Node-RED ${meta.title} nodes"
-navTitle: "${meta.title}"
-navOrder: ${meta.order}
-meta:
- description: "Reference for the ${meta.title.toLowerCase()} nodes in the default Node-RED palette."
----
-
-# ${meta.title} nodes
-
-The ${meta.title.toLowerCase()} nodes in Node-RED's default palette.
-
-${list}
-`
+/**
+ * The node list for the section index, grouped by palette category.
+ *
+ * Deliberately NOT one page per category. A category page at
+ * core-nodes// collides with the node page at core-nodes/.md
+ * whenever a node shares its category's name, and one does: the Function node sits in
+ * the `function` category, so both resolved to /docs/node-red/core-nodes/function/ and
+ * @nuxt/content picked whichever it indexed last. The old Eleventy site had no category
+ * route at all, and the legacy redirect for that URL points at the Function NODE, so the
+ * node page keeps the URL and the grouping becomes headings on the section index.
+ */
+export function renderCategorySections (nodes) {
+ return Object.entries(CATEGORIES)
+ .sort((a, b) => a[1].order - b[1].order)
+ .filter(([category]) => nodes.some(n => n.category === category))
+ .map(([category, meta]) => {
+ const list = nodes
+ .filter(n => n.category === category)
+ .map(n => `- [${n.name}](/docs/node-red/core-nodes/${n.slug}/)${n.description ? `: ${n.description}` : ''}`)
+ .join('\n')
+ return `## ${meta.title}\n\n${list}`
+ })
+ .join('\n\n')
}
/**
@@ -228,11 +230,7 @@ meta:
Every node in Node-RED's default palette, grouped the way the editor groups them. Each
page opens with why you would reach for that node, then mirrors the node's built-in help.
-${Object.entries(CATEGORIES)
- .sort((a, b) => a[1].order - b[1].order)
- .filter(([c]) => nodes.some(n => n.category === c))
- .map(([c, m]) => `- [${m.title}](/docs/node-red/core-nodes/${c}/)`)
- .join('\n')}
+${renderCategorySections(nodes)}
`, 'utf8')
let count = 1
@@ -240,10 +238,6 @@ ${Object.entries(CATEGORIES)
const inCategory = nodes.filter(n => n.category === category)
if (!inCategory.length) continue
- mkdirSync(join(outDir, category), { recursive: true })
- writeFileSync(join(outDir, category, 'index.md'),
- renderCategoryPage(category, inCategory), 'utf8')
-
for (const node of inCategory) {
const useCasePath = join(repoRoot, 'src/_includes/core-nodes', `${node.slug}-use-case.md`)
const useCase = existsSync(useCasePath)
diff --git a/nuxt/lib/core-nodes-sync.test.mjs b/nuxt/lib/core-nodes-sync.test.mjs
index d4d4c7804f..4a46cb1911 100644
--- a/nuxt/lib/core-nodes-sync.test.mjs
+++ b/nuxt/lib/core-nodes-sync.test.mjs
@@ -148,7 +148,30 @@ test('a sync missing any node help fails loudly and names the node', () => {
)
})
-test('a full sync writes a page per node plus the section and category indexes', () => {
+test('a node whose name matches its category does not collide with a category page', () => {
+ // The Function node lives in the `function` category. A category page at
+ // core-nodes/function/ and the node page at core-nodes/function.md both resolve to
+ // /docs/node-red/core-nodes/function/, and @nuxt/content picks whichever it indexed
+ // last. The legacy redirect for that URL is the NODE, so no category pages are
+ // emitted at all.
+ const root = mkdtempSync(join(tmpdir(), 'core-nodes-collide-'))
+ const catalogue = { function: [{ xpath: 'function', name: 'Function', file: '10-function' }] }
+
+ syncCoreNodes({
+ repoRoot: root,
+ nuxtRoot: join(root, 'nuxt'),
+ coreNodes: catalogue,
+ help: { 'function/function': '
f
' },
+ logger: { info () {} },
+ })
+
+ const entries = readdirSync(join(root, 'nuxt/content/docs/node-red/core-nodes')).sort()
+
+ assert.deepEqual(entries, ['function.md', 'index.md'])
+ assert.ok(!entries.includes('function'), 'no category directory may shadow the node page')
+})
+
+test('a full sync writes a page per node plus the section index', () => {
const root = mkdtempSync(join(tmpdir(), 'core-nodes-'))
const help = { 'common/inject': '
i
', 'network/mqtt-in': '
m
' }
@@ -167,7 +190,13 @@ test('a full sync writes a page per node plus the section and category indexes',
// index.md, not README.md: this writes straight into the content tree rather than
// through guides-sync, which is what renames README on the way in. README.md here
// prerenders as /docs/node-red/core-nodes/README/ and 404s the build.
- assert.deepEqual(entries, ['common', 'index.md', 'inject.md', 'mqtt-in.md', 'network'])
+ //
+ // And no per-category directories: one node page per node, flat, plus the section
+ // index. See the collision note on renderCategorySections.
+ assert.deepEqual(entries, ['index.md', 'inject.md', 'mqtt-in.md'])
assert.match(readFileSync(join(dir, 'index.md'), 'utf8'), /navTitle: "Core nodes"/)
- assert.match(readFileSync(join(dir, 'common/index.md'), 'utf8'), /\/docs\/node-red\/core-nodes\/inject\//)
+ // The section index carries the per-category grouping the category pages used to.
+ const index = readFileSync(join(dir, 'index.md'), 'utf8')
+ assert.match(index, /## Common/)
+ assert.match(index, /\/docs\/node-red\/core-nodes\/inject\//)
})
diff --git a/nuxt/lib/library-markdown.mjs b/nuxt/lib/library-markdown.mjs
index afd0c406f5..8fe6345ab9 100644
--- a/nuxt/lib/library-markdown.mjs
+++ b/nuxt/lib/library-markdown.mjs
@@ -60,21 +60,31 @@ export function resolveTitleInterpolation (content, title) {
* into an inline code span, which MDC does not interpolate, and which is how the rest of
* the docs already write `msg.payload`.
*
- * A mustache already inside a code span or a fenced block is left alone: fences are
- * skipped wholesale, and an existing backtick immediately around the match is honoured.
+ * A mustache that is already inside a code span or a fenced block is left exactly as it
+ * is. Both are found by splitting the text on them, NOT by looking at the characters
+ * either side of the match. An earlier version checked only for an adjacent backtick,
+ * which silently broke the common case: a mustache in the MIDDLE of a longer span, such
+ * as `
Hello {{payload.name}}!
`, has no backtick beside it, so it was re-wrapped
+ * and the surrounding span was split in two, leaving the mustache as bare text and
+ * turning the prose around it into bogus inline code. That is the exact failure this
+ * function exists to prevent, so it is pinned by tests against the real fragments.
*/
export function protectMustaches (content) {
- const segments = content.split(/(^```[\s\S]*?^```)/gm)
+ // Fences first, since a fence may contain backticks and must survive byte for byte.
+ return content.split(/(^```[\s\S]*?^```)/gm).map((block, blockIndex) => {
+ // Odd indices are the captured fenced blocks.
+ if (blockIndex % 2 === 1) return block
- return segments.map((segment, i) => {
- // Odd indices are the captured fenced blocks. Node-RED syntax shown in a fence is
- // already safe and must keep its exact bytes.
- if (i % 2 === 1) return segment
+ // The raw guards are Eleventy-only and go regardless of where they sit, including
+ // when they wrap a code span from the outside.
+ const unguarded = block.replace(/\{%-?\s*(?:end)?raw\s*-?%\}\r?\n?/g, '')
- return segment
- .replace(/\{%-?\s*(?:end)?raw\s*-?%\}\r?\n?/g, '')
- .replace(/(`?)(\{\{[^{}\n]*\}\})(`?)/g, (whole, before, mustache, after) =>
- (before && after) ? whole : `\`${mustache}\``)
+ // Then split on inline code spans, so only the text between them is rewritten.
+ return unguarded.split(/(`[^`\n]*`)/g).map((part, partIndex) =>
+ partIndex % 2 === 1
+ ? part
+ : part.replace(/\{\{[^{}\n]*\}\}/g, (mustache) => `\`${mustache}\``)
+ ).join('')
}).join('')
}
@@ -92,6 +102,25 @@ export function rewriteLibraryPaths (content) {
.replace(/(["'(])\/node-red\/(?=[a-z0-9])/g, '$1/docs/node-red/')
}
+/**
+ * Convert the `{% caution %}` callout, which docs-markdown does not know about.
+ *
+ * `convertCallouts` handles note, warning and critical, then strips every remaining
+ * Nunjucks tag, so a caution block loses its box and becomes an ordinary paragraph. The
+ * library uses it, and .eleventy.js treated it as a first-class callout with its own
+ * icon: the OPC UA guide flags "server hosting is not supported on FlowFuse Cloud" with
+ * it, which must not read as body text. Emits the same markup shape convertCallouts
+ * does, so style.docs.css already covers it. Run BEFORE convertCallouts.
+ */
+export function convertCautionCallouts (content) {
+ return content.replace(
+ /\{%-?\s*caution\s*-?%\}([\s\S]*?)\{%-?\s*endcaution\s*-?%\}/g,
+ (_, body) => '
' +
+ '
Caution
' +
+ `
\n\n${body.trim()}\n\n
`
+ )
+}
+
/**
* Everything, in the one order that works.
*
@@ -102,6 +131,7 @@ export function processLibraryMarkdown (content, { title } = {}) {
out = resolveTitleInterpolation(out, title)
out = protectMustaches(out)
out = rewriteLibraryPaths(out)
+ out = convertCautionCallouts(out)
out = joinHtmlBlocks(out)
// Last: this strips every Nunjucks tag still standing.
return convertCallouts(out)
diff --git a/nuxt/lib/library-markdown.test.mjs b/nuxt/lib/library-markdown.test.mjs
index 31c8d1135e..ddf56c52a2 100644
--- a/nuxt/lib/library-markdown.test.mjs
+++ b/nuxt/lib/library-markdown.test.mjs
@@ -89,6 +89,30 @@ test('a flow JSON fence is not mangled by mustache protection', () => {
assert.doesNotMatch(out, /`\{\{msg\.payload\}\}`/)
})
+test('a caution callout keeps its box instead of becoming a bare paragraph', () => {
+ // The regression this guards: docs-markdown's convertCallouts knows note, warning and
+ // critical, then strips every remaining Nunjucks tag, so {% caution %} lost its
+ // styling silently. The OPC UA guide uses it for "not supported on FlowFuse Cloud".
+ const out = processLibraryMarkdown('{% caution %}\nNot supported on Cloud.\n{% endcaution %}')
+
+ assert.match(out, /ff-callout--caution/)
+ assert.match(out, /Not supported on Cloud\./)
+})
+
+test('a mustache in the middle of a longer code span is left alone', () => {
+ // The regression this guards: checking only for an adjacent backtick re-wrapped this
+ // and split the span in two, leaving the mustache bare and MDC free to bind it.
+ const out = protectMustaches('a template like `
Hello {{payload.name}}!
`.')
+
+ assert.equal(out, 'a template like `
Hello {{payload.name}}!
`.')
+})
+
+test('a raw guard wrapping a code span from the outside still leaves the span whole', () => {
+ const out = protectMustaches('like this: {% raw %}`
`.')
+})
+
test('library links move to the docs tree', () => {
assert.equal(rewriteLibraryPaths('[x](/node-red/protocol/modbus/)'), '[x](/docs/node-red/protocol/modbus/)')
assert.equal(rewriteLibraryPaths('src="/node-red/hardware/images/a.png"'), 'src="/docs/node-red/hardware/images/a.png"')
diff --git a/nuxt/redirects-node-red.ts b/nuxt/redirects-node-red.ts
index 52e3949744..b125a1fe01 100644
--- a/nuxt/redirects-node-red.ts
+++ b/nuxt/redirects-node-red.ts
@@ -7,7 +7,10 @@
// 404, which is what a path-preserving splat would do for every typo and stale deep link.
//
// The core-node entries are derived from src/_data/coreNodes.json, since those pages were
-// paginated out of that catalogue and never existed as files.
+// paginated out of that catalogue and never existed as files. Only real node slugs are
+// listed: a bare category path like /node-red/core-nodes/common/ was never a page either,
+// because the .njk templates were pagination-only. /node-red/core-nodes/function/ IS
+// listed, because that was the Function node's own URL.
import type { NitroRouteRules } from 'nitropack'
const moved: Record = {
@@ -16,7 +19,6 @@ const moved: Record = {
'/node-red/core-nodes/catch/': '/docs/node-red/core-nodes/catch/',
'/node-red/core-nodes/change/': '/docs/node-red/core-nodes/change/',
'/node-red/core-nodes/comment/': '/docs/node-red/core-nodes/comment/',
- '/node-red/core-nodes/common/': '/docs/node-red/core-nodes/common/',
'/node-red/core-nodes/complete/': '/docs/node-red/core-nodes/complete/',
'/node-red/core-nodes/csv/': '/docs/node-red/core-nodes/csv/',
'/node-red/core-nodes/debug/': '/docs/node-red/core-nodes/debug/',
@@ -34,15 +36,11 @@ const moved: Record = {
'/node-red/core-nodes/link/': '/docs/node-red/core-nodes/link/',
'/node-red/core-nodes/mqtt-in/': '/docs/node-red/core-nodes/mqtt-in/',
'/node-red/core-nodes/mqtt-out/': '/docs/node-red/core-nodes/mqtt-out/',
- '/node-red/core-nodes/network/': '/docs/node-red/core-nodes/network/',
- '/node-red/core-nodes/parsers/': '/docs/node-red/core-nodes/parsers/',
'/node-red/core-nodes/range/': '/docs/node-red/core-nodes/range/',
'/node-red/core-nodes/read-file/': '/docs/node-red/core-nodes/read-file/',
- '/node-red/core-nodes/sequence/': '/docs/node-red/core-nodes/sequence/',
'/node-red/core-nodes/sort/': '/docs/node-red/core-nodes/sort/',
'/node-red/core-nodes/split/': '/docs/node-red/core-nodes/split/',
'/node-red/core-nodes/status/': '/docs/node-red/core-nodes/status/',
- '/node-red/core-nodes/storage/': '/docs/node-red/core-nodes/storage/',
'/node-red/core-nodes/switch/': '/docs/node-red/core-nodes/switch/',
'/node-red/core-nodes/tcp-in/': '/docs/node-red/core-nodes/tcp-in/',
'/node-red/core-nodes/template/': '/docs/node-red/core-nodes/template/',
diff --git a/nuxt/redirects.ts b/nuxt/redirects.ts
index 928764e01c..9e5ad3701f 100644
--- a/nuxt/redirects.ts
+++ b/nuxt/redirects.ts
@@ -49,7 +49,7 @@ export const redirects: Record = {
'/landing/technology-migration-1/': { redirect: { to: '/vs/kepware/', statusCode: 301 } },
'/landing/technology-migration-2/': { redirect: { to: '/vs/kepware/', statusCode: 301 } },
'/blog/2025/10/the-ai-orchestation-hype/': { redirect: { to: '/blog/2025/10/the-ai-orchestration-hype/', statusCode: 301 } },
- '/node-red/core-nodes/mqtt/': { redirect: { to: '/node-red/core-nodes/mqtt-in/', statusCode: 301 } },
+ '/node-red/core-nodes/mqtt/': { redirect: { to: '/docs/node-red/core-nodes/mqtt-in/', statusCode: 301 } },
'/blueprints/manufacturing/manufacturing-support-request/': { redirect: { to: '/blueprints/manufacturing/andon-system/', statusCode: 301 } },
'/blueprints/other/remote-buffering/': { redirect: { to: '/blueprints/getting-started/store-and-forward/', statusCode: 301 } },
'/vs/tulip/': { redirect: { to: '/landing/tulip/', statusCode: 301 } },
@@ -99,7 +99,7 @@ export const redirects: Record = {
'/cloud/': { redirect: { to: 'https://app.flowfuse.com/account/create/', statusCode: 301 } },
'/legal/terms/': { redirect: { to: '/terms/', statusCode: 301 } },
'/book-a-demo/': { redirect: { to: '/book-demo/', statusCode: 301 } },
- '/education/': { redirect: { to: '/node-red/learn/', statusCode: 301 } },
+ '/education/': { redirect: { to: '/docs/node-red/', statusCode: 301 } },
'/handbook/marketing/education/': { redirect: { to: '/handbook/marketing/', statusCode: 301 } },
'/handbook/sales/org/account-executives/': { redirect: { to: '/handbook/sales/sales-team/', statusCode: 301 } },
'/handbook/sales/org/': { redirect: { to: '/handbook/sales/', statusCode: 301 } },
From 35d1a80f68298c4ff272bb7180ee9ce36625a536 Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Tue, 8 Sep 2026 11:01:21 +0200
Subject: [PATCH 08/17] docs: keep the nav labels and the browser titles the
move was meant to preserve
Two defects from the review of #5750, both invisible to the build.
The nav labels: every nested directory index took the eleventyNavigation key of its
parent instead of its own, so getting-started/{editor,library,programming}/README.md
all came out as "Getting started", nested underneath the "Getting started" that was
already there. Their own keys were Editor, Node-RED Library and Programming, with
orders 1, 3 and 7, and those are restored. Programming takes 8 rather than 7, which
Eleventy had it tie with Date & Time on.
The browser titles: the Eleventy layout picked a title as metaTitle || navTitle ||
meta.title, the migration moved meta.title to the new top-level title: and dropped
metaTitle, and the docs page titles a page navTitle || title. navTitle is the sidebar
label, so every moved page's collapsed to it: "Using MySQL with Node-RED
(2026 Updated)" became "MySQL", "Node-RED - Inject Node" became "Inject". These 121
URLs 301 onto the new ones and carry their ranking with them, so the title has to
survive the move. Each page now carries its Eleventy title in metaTitle, which the
docs page prefers, and core-nodes-sync emits the same field for the 39 generated
pages. All 82 fit the 60-character guidance the site documents for the field. No page
from FlowFuse/flowfuse sets metaTitle, so product docs titles are untouched, and
metaTitle is declared in the docs collection schema because @nuxt/content strips an
undeclared key, which would have made the whole change a silent no-op.
The section index keeps the "Node-RED documentation" keyword from /node-red/learn/,
the one page this PR replaces rather than moves.
Both defects get a check over the real guides tree, because neither broke a build,
a link or a test: nav labels have to be distinguishable from their siblings and from
the parent they are indented under, and a page whose title is longer than its nav
label has to say which of the two Google gets.
---
nuxt/content-guides/flowfuse-nodes/README.md | 1 +
.../flowfuse-nodes/ai/README.md | 1 +
.../flowfuse-nodes/ai/depth-estimation.md | 1 +
.../flowfuse-nodes/ai/image-classification.md | 1 +
.../flowfuse-nodes/ai/llm-nodes.md | 1 +
.../flowfuse-nodes/ai/object-detection.md | 1 +
nuxt/content-guides/flowfuse-nodes/ai/onxx.md | 1 +
.../flowfuse-nodes/edge/README.md | 1 +
.../flowfuse-nodes/edge/cip-suite.md | 1 +
.../flowfuse-nodes/edge/modbus.md | 1 +
.../flowfuse-nodes/edge/opcua.md | 1 +
.../flowfuse-nodes/edge/rtsp.md | 1 +
.../flowfuse-nodes/flowfuse-tables/README.md | 1 +
.../flowfuse-nodes/flowfuse-tables/query.md | 1 +
.../flowfuse-nodes/hub/README.md | 1 +
.../flowfuse-nodes/hub/redis.md | 1 +
.../flowfuse-nodes/mcp/README.md | 1 +
.../flowfuse-nodes/mcp/mcp-prompt.md | 1 +
.../flowfuse-nodes/mcp/mcp-resource.md | 1 +
.../flowfuse-nodes/mcp/mcp-response.md | 1 +
.../flowfuse-nodes/mcp/mcp-tool.md | 1 +
.../flowfuse-nodes/mqtt/README.md | 1 +
.../flowfuse-nodes/mqtt/mqtt-in.md | 1 +
.../flowfuse-nodes/mqtt/mqtt-out.md | 1 +
nuxt/content-guides/node-red/README.md | 35 ++---
.../node-red/database/README.md | 1 +
.../node-red/database/dynamodb.md | 1 +
.../node-red/database/firebase.md | 1 +
.../node-red/database/influxdb.md | 1 +
.../node-red/database/mongodb.md | 1 +
.../content-guides/node-red/database/mysql.md | 1 +
.../node-red/database/postgresql.md | 1 +
.../content-guides/node-red/database/redis.md | 1 +
.../node-red/database/sqlite.md | 1 +
.../node-red/database/timescaledb.md | 1 +
.../node-red/getting-started/README.md | 1 +
.../node-red/getting-started/date-and-time.md | 1 +
.../node-red/getting-started/editor/README.md | 5 +-
.../node-red/getting-started/editor/header.md | 1 +
.../getting-started/editor/palette.md | 1 +
.../getting-started/editor/sidebar.md | 1 +
.../getting-started/editor/workspace.md | 1 +
.../getting-started/library/README.md | 5 +-
.../getting-started/node-red-android.md | 1 +
.../getting-started/node-red-messages.md | 1 +
.../node-red/getting-started/node-red-port.md | 1 +
.../getting-started/programming/README.md | 5 +-
.../programming/data-tranformation.md | 1 +
.../programming/debugging-flows.md | 1 +
.../getting-started/programming/if-else.md | 1 +
.../getting-started/programming/loop.md | 1 +
.../node-red/getting-started/string.md | 1 +
.../getting-started/update-node-red.md | 1 +
.../node-red/hardware/README.md | 1 +
.../node-red/hardware/armxy-bl340.md | 1 +
.../opto-22-groove-rio-7-mm2001-10.md | 1 +
.../node-red/hardware/raspberry-pi-4.md | 1 +
.../node-red/hardware/raspberry-pi-5.md | 1 +
.../node-red/hardware/robustel-eg5120.md | 1 +
.../node-red/hardware/siemens-iot-2050.md | 1 +
.../integration-technologies/README.md | 1 +
.../integration-technologies/graphql.md | 1 +
.../node-red/integration-technologies/rest.md | 1 +
.../integration-technologies/webhook.md | 1 +
.../node-red/keyboard/README.md | 1 +
.../node-red/notification/README.md | 1 +
.../node-red/notification/discord.md | 1 +
.../node-red/notification/email.md | 1 +
.../node-red/notification/telegram.md | 1 +
.../node-red/peripheral/README.md | 1 +
.../node-red/peripheral/ardiuno.md | 1 +
.../node-red/peripheral/barcodescanner.md | 1 +
.../node-red/peripheral/esp32.md | 1 +
.../node-red/peripheral/webcam.md | 1 +
.../node-red/protocol/README.md | 1 +
nuxt/content-guides/node-red/protocol/amqp.md | 1 +
.../content-guides/node-red/protocol/lwm2m.md | 1 +
.../node-red/protocol/modbus.md | 1 +
nuxt/content-guides/node-red/protocol/mqtt.md | 1 +
.../node-red/protocol/opc-ua.md | 1 +
.../node-red/protocol/websocket.md | 1 +
.../node-red/terminology/README.md | 1 +
nuxt/content.config.ts | 5 +
nuxt/lib/core-nodes-sync.mjs | 6 +
nuxt/lib/core-nodes-sync.test.mjs | 14 ++
nuxt/lib/guides-frontmatter.mjs | 105 +++++++++++++++
nuxt/lib/guides-frontmatter.test.mjs | 127 ++++++++++++++++++
nuxt/pages/docs/[...slug].vue | 10 +-
88 files changed, 371 insertions(+), 24 deletions(-)
create mode 100644 nuxt/lib/guides-frontmatter.mjs
create mode 100644 nuxt/lib/guides-frontmatter.test.mjs
diff --git a/nuxt/content-guides/flowfuse-nodes/README.md b/nuxt/content-guides/flowfuse-nodes/README.md
index 38c7aebaaf..912acfb45d 100644
--- a/nuxt/content-guides/flowfuse-nodes/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/README.md
@@ -1,5 +1,6 @@
---
title: FlowFuse Nodes
+metaTitle: "FlowFuse Nodes"
navTitle: FlowFuse Node-RED nodes
navOrder: 4
navGroup: FlowFuse User Manuals
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/README.md b/nuxt/content-guides/flowfuse-nodes/ai/README.md
index 4343869c49..b158257b44 100644
--- a/nuxt/content-guides/flowfuse-nodes/ai/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/README.md
@@ -1,5 +1,6 @@
---
title: FlowFuse AI Nodes
+metaTitle: "FlowFuse AI Nodes"
navTitle: AI
navOrder: 1
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md b/nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md
index 063d6d8ab8..da9ff950aa 100644
--- a/nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/depth-estimation.md
@@ -1,5 +1,6 @@
---
title: Depth Estimation
+metaTitle: "Depth Estimation"
navTitle: Depth Estimation
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/image-classification.md b/nuxt/content-guides/flowfuse-nodes/ai/image-classification.md
index f952019fec..102fbecfcf 100644
--- a/nuxt/content-guides/flowfuse-nodes/ai/image-classification.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/image-classification.md
@@ -1,5 +1,6 @@
---
title: Image Classification
+metaTitle: "Image Classification"
navTitle: Image Classification
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md b/nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md
index c8d93055db..b838957d2e 100644
--- a/nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/llm-nodes.md
@@ -1,5 +1,6 @@
---
title: LLM Nodes
+metaTitle: "LLM Nodes"
navTitle: LLM Nodes
navOrder: 5
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/object-detection.md b/nuxt/content-guides/flowfuse-nodes/ai/object-detection.md
index eadc2f4697..e6d077decb 100644
--- a/nuxt/content-guides/flowfuse-nodes/ai/object-detection.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/object-detection.md
@@ -1,5 +1,6 @@
---
title: Object Detection
+metaTitle: "Object Detection"
navTitle: Object Detection
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/ai/onxx.md b/nuxt/content-guides/flowfuse-nodes/ai/onxx.md
index 978a84ed20..905e6e0f4c 100644
--- a/nuxt/content-guides/flowfuse-nodes/ai/onxx.md
+++ b/nuxt/content-guides/flowfuse-nodes/ai/onxx.md
@@ -1,5 +1,6 @@
---
title: ONXX
+metaTitle: "ONXX"
navTitle: ONXX
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/README.md b/nuxt/content-guides/flowfuse-nodes/edge/README.md
index c9f63a6b0a..3cde9f7818 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/README.md
@@ -1,5 +1,6 @@
---
title: Edge Certified Nodes
+metaTitle: "Edge Certified Nodes"
navTitle: Edge
navOrder: 4
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md b/nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md
index fcdb887c7c..cff9eff92e 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/cip-suite.md
@@ -1,5 +1,6 @@
---
title: CIP Suite, EtherNet/IP Nodes
+metaTitle: "CIP Suite, EtherNet/IP Nodes"
navTitle: CIP Suite, EtherNet/IP Nodes
navOrder: 2
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/modbus.md b/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
index b1431fa5cb..827936c55b 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/modbus.md
@@ -1,5 +1,6 @@
---
title: Modbus
+metaTitle: "Modbus"
navTitle: Modbus
navOrder: 3
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/opcua.md b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
index b2925619b0..560e527341 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/opcua.md
@@ -1,5 +1,6 @@
---
title: OPC UA for FlowFuse - FlowFuse Certified Node
+metaTitle: "OPC UA for FlowFuse - FlowFuse Certified Node"
navTitle: OPC UA
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/edge/rtsp.md b/nuxt/content-guides/flowfuse-nodes/edge/rtsp.md
index a767e7cc9b..f8ddd50e66 100644
--- a/nuxt/content-guides/flowfuse-nodes/edge/rtsp.md
+++ b/nuxt/content-guides/flowfuse-nodes/edge/rtsp.md
@@ -1,5 +1,6 @@
---
title: RTSP Video Feed
+metaTitle: "RTSP Video Feed"
navTitle: RTSP Video Feed
navOrder: 1
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md
index 077010b3bc..ab9ecc92a2 100644
--- a/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/README.md
@@ -1,5 +1,6 @@
---
title: FlowFuse Tables
+metaTitle: "FlowFuse Tables"
navTitle: FlowFuse Tables
navOrder: 3
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md
index 342b75a70b..602a123371 100644
--- a/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md
+++ b/nuxt/content-guides/flowfuse-nodes/flowfuse-tables/query.md
@@ -1,5 +1,6 @@
---
title: Query
+metaTitle: "Query"
navTitle: Query
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/hub/README.md b/nuxt/content-guides/flowfuse-nodes/hub/README.md
index f59e8d958b..765c51767d 100644
--- a/nuxt/content-guides/flowfuse-nodes/hub/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/hub/README.md
@@ -1,5 +1,6 @@
---
title: Hub Certified Nodes
+metaTitle: "Hub Certified Nodes"
navTitle: Hub
navOrder: 5
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/hub/redis.md b/nuxt/content-guides/flowfuse-nodes/hub/redis.md
index 983c9358df..d7cce99785 100644
--- a/nuxt/content-guides/flowfuse-nodes/hub/redis.md
+++ b/nuxt/content-guides/flowfuse-nodes/hub/redis.md
@@ -1,5 +1,6 @@
---
title: Redis
+metaTitle: "Redis"
navTitle: Redis
navOrder: 1
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mcp/README.md b/nuxt/content-guides/flowfuse-nodes/mcp/README.md
index 141b4a7e87..ce9ed32de9 100644
--- a/nuxt/content-guides/flowfuse-nodes/mcp/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/README.md
@@ -1,5 +1,6 @@
---
title: MCP Nodes
+metaTitle: "MCP Nodes"
navTitle: MCP
navOrder: 2
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md
index ba3175ef3a..a3375ee07f 100644
--- a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-prompt.md
@@ -1,5 +1,6 @@
---
title: MCP Prompt
+metaTitle: "MCP Prompt"
navTitle: MCP Prompt
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md
index 0d81c31244..72337b224f 100644
--- a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-resource.md
@@ -1,5 +1,6 @@
---
title: MCP Resource
+metaTitle: "MCP Resource"
navTitle: MCP Resource
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md
index 7d5a3e691a..5d1294462a 100644
--- a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-response.md
@@ -1,5 +1,6 @@
---
title: MCP Response
+metaTitle: "MCP Response"
navTitle: MCP Responses
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md
index 73d366c76b..17581e08e3 100644
--- a/nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md
+++ b/nuxt/content-guides/flowfuse-nodes/mcp/mcp-tool.md
@@ -1,5 +1,6 @@
---
title: MCP Tool
+metaTitle: "MCP Tool"
navTitle: MCP Tool
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mqtt/README.md b/nuxt/content-guides/flowfuse-nodes/mqtt/README.md
index 998cf4d690..2e80e7c22b 100644
--- a/nuxt/content-guides/flowfuse-nodes/mqtt/README.md
+++ b/nuxt/content-guides/flowfuse-nodes/mqtt/README.md
@@ -1,5 +1,6 @@
---
title: MQTT Nodes
+metaTitle: "MQTT Nodes"
navTitle: FlowFuse MQTT Nodes
navOrder: 5
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md
index 6eaba1f802..a5776965cb 100644
--- a/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md
+++ b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-in.md
@@ -1,5 +1,6 @@
---
title: MQTT In
+metaTitle: "MQTT In"
navTitle: MQTT In
navOrder: 50
meta:
diff --git a/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md
index 728af1bc78..c8155d177a 100644
--- a/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md
+++ b/nuxt/content-guides/flowfuse-nodes/mqtt/mqtt-out.md
@@ -1,5 +1,6 @@
---
title: MQTT Out
+metaTitle: "MQTT Out"
navTitle: MQTT Out
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/README.md b/nuxt/content-guides/node-red/README.md
index e93360e573..fd6af1e8ad 100644
--- a/nuxt/content-guides/node-red/README.md
+++ b/nuxt/content-guides/node-red/README.md
@@ -1,20 +1,21 @@
---
title: "Using Node-RED"
+metaTitle: "Node-RED Documentation"
navTitle: "Using Node-RED"
navOrder: 1
navGroup: "Node-RED"
navGroupOrder: 7
meta:
- description: "Reference for working with Node-RED itself: connecting it to databases, protocols, hardware and notification services, and what each core node does."
- keywords: node-red, node red, core nodes, database, hardware, protocols, industrial iot
+ description: "Reference documentation for using Node-RED with databases, communication protocols, hardware, peripheral devices, notification services and integration technologies."
+ keywords: node-red, node red, node-red documentation, node-red core nodes, node-red database, node-red protocols, node-red hardware, industrial iot
---
# Using Node-RED
-Node-RED is the open-source runtime FlowFuse runs, governs and scales. This section is
-reference material about Node-RED itself: how to connect it to a database, speak an
-industrial protocol, run it on a particular piece of hardware, and what each core node
-does.
+Node-RED is the open-source runtime FlowFuse runs, governs and scales. This section
+provides reference documentation for working with Node-RED itself: connecting it to
+databases, communication protocols, hardware and notification services, and
+understanding what each core node does.
It is here to answer a question you already have. It is not a path into FlowFuse, and it
does not need to be read in order. If you are setting FlowFuse up, start with
@@ -35,27 +36,27 @@ message. [Getting started](/docs/node-red/getting-started/)
## Core nodes
-What each node in the default palette does, and a worked reason to reach for it.
-[Core nodes](/docs/node-red/core-nodes/)
+Reference documentation for each node in the default Node-RED palette, with a worked
+reason to reach for it. [Core nodes](/docs/node-red/core-nodes/)
## Communication protocols
-Modbus, OPC UA, MQTT, AMQP, WebSocket and LwM2M, for talking to controllers and edge
-equipment. [Communication protocols](/docs/node-red/protocol/)
+Modbus, OPC UA, MQTT, AMQP, WebSocket and LwM2M, for connecting Node-RED to controllers
+and edge equipment. [Communication protocols](/docs/node-red/protocol/)
## Databases
-Reading and writing SQL, NoSQL and time-series databases, one guide per database.
-[Databases](/docs/node-red/database/)
+Reading and writing SQL, NoSQL and time-series databases with Node-RED, one guide per
+database. [Databases](/docs/node-red/database/)
## Integration technologies
-Webhooks, REST APIs and GraphQL, for wiring Node-RED to the rest of your systems.
+Webhooks, REST APIs and GraphQL, for connecting Node-RED to the rest of your systems.
[Integration technologies](/docs/node-red/integration-technologies/)
## Notification services
-Sending alerts by email, Telegram and Discord.
+Sending alerts from Node-RED by email, Telegram and Discord.
[Notification services](/docs/node-red/notification/)
## Hardware
@@ -65,13 +66,13 @@ Running Node-RED on a Raspberry Pi, a Siemens IoT2050, and other industrial gate
## Peripheral devices
-Webcams, barcode scanners and other things plugged into the machine Node-RED runs on.
+Webcams, barcode scanners and other devices connected to the machine Node-RED runs on.
[Peripheral devices](/docs/node-red/peripheral/)
## Reference
-[Terminology](/docs/node-red/terminology/) for the words that come up in Node-RED
-discussions, and [keyboard shortcuts](/docs/node-red/keyboard/) for the editor.
+[Terminology](/docs/node-red/terminology/) for the words used in Node-RED documentation,
+and [keyboard shortcuts](/docs/node-red/keyboard/) for the editor.
::callout{icon="i-lucide-arrow-right"}
**Running Node-RED for an organisation?** Access control, version history, deployment
diff --git a/nuxt/content-guides/node-red/database/README.md b/nuxt/content-guides/node-red/database/README.md
index 1e0d7ee805..431fc70ec1 100644
--- a/nuxt/content-guides/node-red/database/README.md
+++ b/nuxt/content-guides/node-red/database/README.md
@@ -1,5 +1,6 @@
---
title: Node-RED Database Integration Guides
+metaTitle: "Node-RED Database Integration Guides"
navTitle: Databases
navOrder: 5
meta:
diff --git a/nuxt/content-guides/node-red/database/dynamodb.md b/nuxt/content-guides/node-red/database/dynamodb.md
index f3bf0e7811..a7c5369118 100644
--- a/nuxt/content-guides/node-red/database/dynamodb.md
+++ b/nuxt/content-guides/node-red/database/dynamodb.md
@@ -1,5 +1,6 @@
---
title: Using DynamoDB with Node-RED (2026 Updated)
+metaTitle: "Using DynamoDB with Node-RED (2026 Updated)"
navTitle: DynamoDB
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/firebase.md b/nuxt/content-guides/node-red/database/firebase.md
index 656db4f9b1..aec3b0ae80 100644
--- a/nuxt/content-guides/node-red/database/firebase.md
+++ b/nuxt/content-guides/node-red/database/firebase.md
@@ -1,5 +1,6 @@
---
title: Using Firebase with Node-RED (2026 Updated)
+metaTitle: "Using Firebase with Node-RED (2026 Updated)"
navTitle: Firebase
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/influxdb.md b/nuxt/content-guides/node-red/database/influxdb.md
index 4931a4f8bb..8d46d93a06 100644
--- a/nuxt/content-guides/node-red/database/influxdb.md
+++ b/nuxt/content-guides/node-red/database/influxdb.md
@@ -1,5 +1,6 @@
---
title: Using InfluxDB with Node-RED (2026 Updated)
+metaTitle: "Using InfluxDB with Node-RED (2026 Updated)"
navTitle: InfluxDB
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/mongodb.md b/nuxt/content-guides/node-red/database/mongodb.md
index d556c90640..90321575d5 100644
--- a/nuxt/content-guides/node-red/database/mongodb.md
+++ b/nuxt/content-guides/node-red/database/mongodb.md
@@ -1,5 +1,6 @@
---
title: Using MongoDB With Node-RED (2026 Updated)
+metaTitle: "Using MongoDB With Node-RED (2026 Updated)"
navTitle: MongoDB
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/mysql.md b/nuxt/content-guides/node-red/database/mysql.md
index 30f30658bf..e37a985ba6 100644
--- a/nuxt/content-guides/node-red/database/mysql.md
+++ b/nuxt/content-guides/node-red/database/mysql.md
@@ -1,5 +1,6 @@
---
title: Using MySQL with Node-RED (2026 Updated)
+metaTitle: "Using MySQL with Node-RED (2026 Updated)"
navTitle: MySQL
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/postgresql.md b/nuxt/content-guides/node-red/database/postgresql.md
index ff65ac89ab..4726ec6b6b 100644
--- a/nuxt/content-guides/node-red/database/postgresql.md
+++ b/nuxt/content-guides/node-red/database/postgresql.md
@@ -1,5 +1,6 @@
---
title: Using PostgreSQL with Node-RED (2026 Updated)
+metaTitle: "Using PostgreSQL with Node-RED (2026 Updated)"
navTitle: PostgreSQL
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/redis.md b/nuxt/content-guides/node-red/database/redis.md
index e606df113c..ef9a24b905 100644
--- a/nuxt/content-guides/node-red/database/redis.md
+++ b/nuxt/content-guides/node-red/database/redis.md
@@ -1,5 +1,6 @@
---
title: Using Redis with Node-RED (2026 Updated)
+metaTitle: "Using Redis with Node-RED (2026 Updated)"
navTitle: Redis
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/sqlite.md b/nuxt/content-guides/node-red/database/sqlite.md
index e34fcc3d5d..f2991dae83 100644
--- a/nuxt/content-guides/node-red/database/sqlite.md
+++ b/nuxt/content-guides/node-red/database/sqlite.md
@@ -1,5 +1,6 @@
---
title: Using SQLite with Node-RED (2026 Updated)
+metaTitle: "Using SQLite with Node-RED (2026 Updated)"
navTitle: SQLite
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/database/timescaledb.md b/nuxt/content-guides/node-red/database/timescaledb.md
index 6bce94c071..c7623204c6 100644
--- a/nuxt/content-guides/node-red/database/timescaledb.md
+++ b/nuxt/content-guides/node-red/database/timescaledb.md
@@ -1,5 +1,6 @@
---
title: Using TimescaleDB with Node-RED (2026 Updated)
+metaTitle: "Using TimescaleDB with Node-RED (2026 Updated)"
navTitle: TimescaleDB
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/README.md b/nuxt/content-guides/node-red/getting-started/README.md
index 491d39aa42..7e94a94d6f 100644
--- a/nuxt/content-guides/node-red/getting-started/README.md
+++ b/nuxt/content-guides/node-red/getting-started/README.md
@@ -1,5 +1,6 @@
---
title: Getting Started with Node-RED
+metaTitle: "Getting Started with Node-RED"
navTitle: Getting started
navOrder: 2
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/date-and-time.md b/nuxt/content-guides/node-red/getting-started/date-and-time.md
index 7a5f743c35..31958da125 100644
--- a/nuxt/content-guides/node-red/getting-started/date-and-time.md
+++ b/nuxt/content-guides/node-red/getting-started/date-and-time.md
@@ -1,5 +1,6 @@
---
title: Working with Dates and Times in Node-RED
+metaTitle: "Working with Dates and Times in Node-RED"
navTitle: Date & Time
navOrder: 7
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/editor/README.md b/nuxt/content-guides/node-red/getting-started/editor/README.md
index d77cb136f2..e5d2be814f 100644
--- a/nuxt/content-guides/node-red/getting-started/editor/README.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/README.md
@@ -1,7 +1,8 @@
---
title: Getting Started with the Node-RED Editor
-navTitle: Getting started
-navOrder: 2
+metaTitle: "Getting Started with the Node-RED Editor"
+navTitle: Editor
+navOrder: 1
meta:
description: Learn about the powerful features of Node-RED Editor.
keywords: node red editor, node-red editor, nodered flow, node-red flows, nodered workspace
diff --git a/nuxt/content-guides/node-red/getting-started/editor/header.md b/nuxt/content-guides/node-red/getting-started/editor/header.md
index 6872ec3971..bfab10e44f 100644
--- a/nuxt/content-guides/node-red/getting-started/editor/header.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/header.md
@@ -1,5 +1,6 @@
---
title: Node-RED Editor Header component
+metaTitle: "Node-RED Editor Header component"
navTitle: Header
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/editor/palette.md b/nuxt/content-guides/node-red/getting-started/editor/palette.md
index 6f1d5d5194..56c40bb7b4 100644
--- a/nuxt/content-guides/node-red/getting-started/editor/palette.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/palette.md
@@ -1,5 +1,6 @@
---
title: Node-RED Editor Palette
+metaTitle: "Node-RED Editor Palette"
navTitle: Palette
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/editor/sidebar.md b/nuxt/content-guides/node-red/getting-started/editor/sidebar.md
index 85d1ce8fea..b2b83c1165 100644
--- a/nuxt/content-guides/node-red/getting-started/editor/sidebar.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/sidebar.md
@@ -1,5 +1,6 @@
---
title: Node-RED Editor Sidebar component
+metaTitle: "Node-RED Editor Sidebar component"
navTitle: Sidebar
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/editor/workspace.md b/nuxt/content-guides/node-red/getting-started/editor/workspace.md
index 3f0b97fb58..958c15f26f 100644
--- a/nuxt/content-guides/node-red/getting-started/editor/workspace.md
+++ b/nuxt/content-guides/node-red/getting-started/editor/workspace.md
@@ -1,5 +1,6 @@
---
title: Node-RED Editor Workspace
+metaTitle: "Node-RED Editor Workspace"
navTitle: Workspace
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/library/README.md b/nuxt/content-guides/node-red/getting-started/library/README.md
index 8b22c126b3..68e0f4fe04 100644
--- a/nuxt/content-guides/node-red/getting-started/library/README.md
+++ b/nuxt/content-guides/node-red/getting-started/library/README.md
@@ -1,7 +1,8 @@
---
title: Node-RED Library – A Curated and Actively Maintained List of Nodes
-navTitle: Getting started
-navOrder: 2
+metaTitle: "Node-RED Library: Curated List of Nodes"
+navTitle: Node-RED Library
+navOrder: 3
meta:
description: Browse the Node-RED Library for community-built nodes and integrations. FlowFuse's curated catalog offers tested, documented, enterprise-ready solutions with professional support for critical deployments.
keywords: node-red library, node-red nodes, node-red integrations, community nodes, node-red packages, custom nodes, node-red flows
diff --git a/nuxt/content-guides/node-red/getting-started/node-red-android.md b/nuxt/content-guides/node-red/getting-started/node-red-android.md
index 7d2f6954ec..f6920142ac 100644
--- a/nuxt/content-guides/node-red/getting-started/node-red-android.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-android.md
@@ -1,5 +1,6 @@
---
title: Installing Node-RED on Android
+metaTitle: "Installing Node-RED on Android"
navTitle: Node-RED On Android
navOrder: 4
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/node-red-messages.md b/nuxt/content-guides/node-red/getting-started/node-red-messages.md
index 04f49cc7f3..9ba15cdd68 100644
--- a/nuxt/content-guides/node-red/getting-started/node-red-messages.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-messages.md
@@ -1,5 +1,6 @@
---
title: Understanding Node-RED Messages
+metaTitle: "Understanding Node-RED Messages"
navTitle: Working with Messages
navOrder: 5
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/node-red-port.md b/nuxt/content-guides/node-red/getting-started/node-red-port.md
index fa8778f9f3..9787a510ea 100644
--- a/nuxt/content-guides/node-red/getting-started/node-red-port.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-port.md
@@ -1,5 +1,6 @@
---
title: Node-RED Port (localhost:1880)
+metaTitle: "Node-RED Port (localhost:1880)"
navTitle: Port Configuration
navOrder: 2
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/programming/README.md b/nuxt/content-guides/node-red/getting-started/programming/README.md
index e6611c8828..6b4fe635c8 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/README.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/README.md
@@ -1,7 +1,8 @@
---
title: Node-RED Programming
-navTitle: Getting started
-navOrder: 2
+metaTitle: "Node-RED Programming"
+navTitle: Programming
+navOrder: 8
meta:
description: Master Node-RED programming fundamentals including flows, nodes, messages, conditional logic, and data manipulation. Learn essential concepts for building sophisticated visual programming solutions.
keywords: Node-RED programming, visual programming, Node-RED flows, Node-RED nodes, message passing, conditional logic, data manipulation, flow-based programming, Node-RED tutorial, IoT programming
diff --git a/nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md b/nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md
index 8cc235e160..3e16217393 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/data-tranformation.md
@@ -1,5 +1,6 @@
---
title: How to Filter, Map, Sort, and Reduce Data in Node-RED
+metaTitle: "How to Filter, Map, Sort, and Reduce Data in Node-RED"
navTitle: Data Tranformation
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md b/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
index cf5c9f04d9..6259a0185f 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
@@ -1,5 +1,6 @@
---
title: How to Debug Node-RED Flows Using Debugger
+metaTitle: "How to Debug Node-RED Flows Using Debugger"
navTitle: Debugging
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/programming/if-else.md b/nuxt/content-guides/node-red/getting-started/programming/if-else.md
index 8c94d3292b..d60341c28b 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/if-else.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/if-else.md
@@ -1,5 +1,6 @@
---
title: How to Use If-Else Logic in Node-RED
+metaTitle: "How to Use If-Else Logic in Node-RED"
navTitle: If-Else
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/programming/loop.md b/nuxt/content-guides/node-red/getting-started/programming/loop.md
index 43bf9b975d..65574b1501 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/loop.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/loop.md
@@ -1,5 +1,6 @@
---
title: How to implement loops in Node-RED flows
+metaTitle: "How to implement loops in Node-RED flows"
navTitle: Loop
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/string.md b/nuxt/content-guides/node-red/getting-started/string.md
index bf272383fe..3f3348e49d 100644
--- a/nuxt/content-guides/node-red/getting-started/string.md
+++ b/nuxt/content-guides/node-red/getting-started/string.md
@@ -1,5 +1,6 @@
---
title: 'Strings in Node-RED: Convert String to Number, Split, Concatenate, Trim, and More'
+metaTitle: "Strings in Node-RED: Convert, Split & Trim"
navTitle: String
navOrder: 6
meta:
diff --git a/nuxt/content-guides/node-red/getting-started/update-node-red.md b/nuxt/content-guides/node-red/getting-started/update-node-red.md
index 6e53ab2ba9..02c3e2c477 100644
--- a/nuxt/content-guides/node-red/getting-started/update-node-red.md
+++ b/nuxt/content-guides/node-red/getting-started/update-node-red.md
@@ -1,5 +1,6 @@
---
title: How to Update Node-RED
+metaTitle: "How to Update Node-RED"
navTitle: Update Node-RED
navOrder: 3
meta:
diff --git a/nuxt/content-guides/node-red/hardware/README.md b/nuxt/content-guides/node-red/hardware/README.md
index a3f8dd3ab2..42cecdf2ac 100644
--- a/nuxt/content-guides/node-red/hardware/README.md
+++ b/nuxt/content-guides/node-red/hardware/README.md
@@ -1,5 +1,6 @@
---
title: Setting Up Node-RED on Different Hardware
+metaTitle: "Setting Up Node-RED on Different Hardware"
navTitle: Hardware
navOrder: 8
meta:
diff --git a/nuxt/content-guides/node-red/hardware/armxy-bl340.md b/nuxt/content-guides/node-red/hardware/armxy-bl340.md
index 9637f669b3..41fd339b94 100644
--- a/nuxt/content-guides/node-red/hardware/armxy-bl340.md
+++ b/nuxt/content-guides/node-red/hardware/armxy-bl340.md
@@ -1,5 +1,6 @@
---
title: Setting Node-RED on BLIIOT ARMxy BL340
+metaTitle: "Setting Node-RED on BLIIOT ARMxy BL340"
navTitle: BLIIOT ARMxy BL340
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md b/nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
index 066f10837b..3f0932daa1 100644
--- a/nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
+++ b/nuxt/content-guides/node-red/hardware/opto-22-groove-rio-7-mm2001-10.md
@@ -1,5 +1,6 @@
---
title: Setting up Node-RED on Opto-22 Groov Rio R7
+metaTitle: "Setting up Node-RED on Opto-22 Groov Rio R7"
navTitle: Opto-22 Groov Epic
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md b/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md
index 4d92daff94..c43584b8dc 100644
--- a/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md
+++ b/nuxt/content-guides/node-red/hardware/raspberry-pi-4.md
@@ -1,5 +1,6 @@
---
title: Setting Node-RED on Raspberry Pi 4
+metaTitle: "Setting Node-RED on Raspberry Pi 4"
navTitle: Raspberry Pi 4
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md b/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md
index 89af34c7e1..a523bf0c82 100644
--- a/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md
+++ b/nuxt/content-guides/node-red/hardware/raspberry-pi-5.md
@@ -1,5 +1,6 @@
---
title: Setting Up Node-RED on Raspberry Pi 5
+metaTitle: "Setting Up Node-RED on Raspberry Pi 5"
navTitle: Raspberry Pi 5
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/hardware/robustel-eg5120.md b/nuxt/content-guides/node-red/hardware/robustel-eg5120.md
index 48ebabaed8..4864b1475a 100644
--- a/nuxt/content-guides/node-red/hardware/robustel-eg5120.md
+++ b/nuxt/content-guides/node-red/hardware/robustel-eg5120.md
@@ -1,5 +1,6 @@
---
title: Setting Node-RED on Robustel EG5120
+metaTitle: "Setting Node-RED on Robustel EG5120"
navTitle: Robustel EG5120
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/hardware/siemens-iot-2050.md b/nuxt/content-guides/node-red/hardware/siemens-iot-2050.md
index 8270a1b6a2..66eda7a402 100644
--- a/nuxt/content-guides/node-red/hardware/siemens-iot-2050.md
+++ b/nuxt/content-guides/node-red/hardware/siemens-iot-2050.md
@@ -1,5 +1,6 @@
---
title: Run Node-RED on Siemens IoT2050
+metaTitle: "Run Node-RED on Siemens IoT2050"
navTitle: Siemens IoT2050
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/integration-technologies/README.md b/nuxt/content-guides/node-red/integration-technologies/README.md
index 590fd40c9f..c5006825ff 100644
--- a/nuxt/content-guides/node-red/integration-technologies/README.md
+++ b/nuxt/content-guides/node-red/integration-technologies/README.md
@@ -1,5 +1,6 @@
---
title: Using Different Technologies for Building Applications with Node-RED.
+metaTitle: "Building Applications with Node-RED"
navTitle: Integration technologies
navOrder: 6
meta:
diff --git a/nuxt/content-guides/node-red/integration-technologies/graphql.md b/nuxt/content-guides/node-red/integration-technologies/graphql.md
index e801eab9bd..c5e4ee18d9 100644
--- a/nuxt/content-guides/node-red/integration-technologies/graphql.md
+++ b/nuxt/content-guides/node-red/integration-technologies/graphql.md
@@ -1,5 +1,6 @@
---
title: Integrating GraphQL APIs in Node-RED
+metaTitle: "Integrating GraphQL APIs in Node-RED"
navTitle: GraphQL API
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/integration-technologies/rest.md b/nuxt/content-guides/node-red/integration-technologies/rest.md
index 9066569389..43d988a41e 100644
--- a/nuxt/content-guides/node-red/integration-technologies/rest.md
+++ b/nuxt/content-guides/node-red/integration-technologies/rest.md
@@ -1,5 +1,6 @@
---
title: Creating REST API's with Node-RED
+metaTitle: "Creating REST API's with Node-RED"
navTitle: REST API
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/integration-technologies/webhook.md b/nuxt/content-guides/node-red/integration-technologies/webhook.md
index 43e4b985e3..e6e8432d05 100644
--- a/nuxt/content-guides/node-red/integration-technologies/webhook.md
+++ b/nuxt/content-guides/node-red/integration-technologies/webhook.md
@@ -1,5 +1,6 @@
---
title: Using Webhook with Node-RED
+metaTitle: "Using Webhook with Node-RED"
navTitle: Webhook
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/keyboard/README.md b/nuxt/content-guides/node-red/keyboard/README.md
index 265bfe535d..ffd65ee868 100644
--- a/nuxt/content-guides/node-red/keyboard/README.md
+++ b/nuxt/content-guides/node-red/keyboard/README.md
@@ -1,5 +1,6 @@
---
title: Node-RED Keyboard Shortcuts
+metaTitle: "Node-RED Keyboard Shortcuts"
navTitle: Keyboard shortcuts
navOrder: 11
meta:
diff --git a/nuxt/content-guides/node-red/notification/README.md b/nuxt/content-guides/node-red/notification/README.md
index 386a913e0c..d8c4b5216c 100644
--- a/nuxt/content-guides/node-red/notification/README.md
+++ b/nuxt/content-guides/node-red/notification/README.md
@@ -1,5 +1,6 @@
---
title: Notification Services in Node-RED
+metaTitle: "Notification Services in Node-RED"
navTitle: Notification services
navOrder: 7
meta:
diff --git a/nuxt/content-guides/node-red/notification/discord.md b/nuxt/content-guides/node-red/notification/discord.md
index 2cd65028e0..b8ddc77ca1 100644
--- a/nuxt/content-guides/node-red/notification/discord.md
+++ b/nuxt/content-guides/node-red/notification/discord.md
@@ -1,5 +1,6 @@
---
title: Sending and receiving Discord messages with Node-RED
+metaTitle: "Sending and receiving Discord messages with Node-RED"
navTitle: Discord
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/notification/email.md b/nuxt/content-guides/node-red/notification/email.md
index a116e20e7f..158394c086 100644
--- a/nuxt/content-guides/node-red/notification/email.md
+++ b/nuxt/content-guides/node-red/notification/email.md
@@ -1,5 +1,6 @@
---
title: Sending and receiving emails with Node-RED
+metaTitle: "Sending and receiving emails with Node-RED"
navTitle: Email
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/notification/telegram.md b/nuxt/content-guides/node-red/notification/telegram.md
index 6e0387831d..abdfb63ea2 100644
--- a/nuxt/content-guides/node-red/notification/telegram.md
+++ b/nuxt/content-guides/node-red/notification/telegram.md
@@ -1,5 +1,6 @@
---
title: Sending and receiving Telegram messages with Node-RED
+metaTitle: "Sending and receiving Telegram messages with Node-RED"
navTitle: Telegram
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/peripheral/README.md b/nuxt/content-guides/node-red/peripheral/README.md
index 6e2071b23a..cb8ade0103 100644
--- a/nuxt/content-guides/node-red/peripheral/README.md
+++ b/nuxt/content-guides/node-red/peripheral/README.md
@@ -1,5 +1,6 @@
---
title: Integrating Node-RED with Peripheral Devices
+metaTitle: "Integrating Node-RED with Peripheral Devices"
navTitle: Peripheral devices
navOrder: 9
meta:
diff --git a/nuxt/content-guides/node-red/peripheral/ardiuno.md b/nuxt/content-guides/node-red/peripheral/ardiuno.md
index 5e3eafb2ce..c2386fce16 100644
--- a/nuxt/content-guides/node-red/peripheral/ardiuno.md
+++ b/nuxt/content-guides/node-red/peripheral/ardiuno.md
@@ -1,5 +1,6 @@
---
title: Connecting Arduino to Node-RED
+metaTitle: "Connecting Arduino to Node-RED"
navTitle: Arduino
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/peripheral/barcodescanner.md b/nuxt/content-guides/node-red/peripheral/barcodescanner.md
index c10b7a7cb8..127a2f64dd 100644
--- a/nuxt/content-guides/node-red/peripheral/barcodescanner.md
+++ b/nuxt/content-guides/node-red/peripheral/barcodescanner.md
@@ -1,5 +1,6 @@
---
title: How to connect a barcode scanner to your Node-RED application
+metaTitle: "How to Connect a Barcode Scanner to Node-RED"
navTitle: Barcode Scanner
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/peripheral/esp32.md b/nuxt/content-guides/node-red/peripheral/esp32.md
index b77381d55c..39276356a7 100644
--- a/nuxt/content-guides/node-red/peripheral/esp32.md
+++ b/nuxt/content-guides/node-red/peripheral/esp32.md
@@ -1,5 +1,6 @@
---
title: Connect ESP32 with Node-RED using MQTT
+metaTitle: "Connect ESP32 with Node-RED using MQTT"
navTitle: ESP32
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/peripheral/webcam.md b/nuxt/content-guides/node-red/peripheral/webcam.md
index 83338b8386..96ee07bc4f 100644
--- a/nuxt/content-guides/node-red/peripheral/webcam.md
+++ b/nuxt/content-guides/node-red/peripheral/webcam.md
@@ -1,5 +1,6 @@
---
title: Using webcam with Node-RED
+metaTitle: "Using webcam with Node-RED"
navTitle: Webcam
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/protocol/README.md b/nuxt/content-guides/node-red/protocol/README.md
index 0d6526dac3..b5a89a7a71 100644
--- a/nuxt/content-guides/node-red/protocol/README.md
+++ b/nuxt/content-guides/node-red/protocol/README.md
@@ -1,5 +1,6 @@
---
title: Using Different Protocols for Building Applications with Node-RED
+metaTitle: "Using Different Protocols with Node-RED"
navTitle: Communication protocols
navOrder: 4
meta:
diff --git a/nuxt/content-guides/node-red/protocol/amqp.md b/nuxt/content-guides/node-red/protocol/amqp.md
index 13a73bc99b..abc014545c 100644
--- a/nuxt/content-guides/node-red/protocol/amqp.md
+++ b/nuxt/content-guides/node-red/protocol/amqp.md
@@ -1,5 +1,6 @@
---
title: Using AMQP with Node-RED
+metaTitle: "Using AMQP with Node-RED"
navTitle: AMQP
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/protocol/lwm2m.md b/nuxt/content-guides/node-red/protocol/lwm2m.md
index e38b3fd645..0e520d8f86 100644
--- a/nuxt/content-guides/node-red/protocol/lwm2m.md
+++ b/nuxt/content-guides/node-red/protocol/lwm2m.md
@@ -1,5 +1,6 @@
---
title: Using LwM2M with Node-RED
+metaTitle: "Using LwM2M with Node-RED"
navTitle: LwM2M
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/protocol/modbus.md b/nuxt/content-guides/node-red/protocol/modbus.md
index ab199ee10c..78cf95b692 100644
--- a/nuxt/content-guides/node-red/protocol/modbus.md
+++ b/nuxt/content-guides/node-red/protocol/modbus.md
@@ -1,5 +1,6 @@
---
title: Using Modbus with Node-RED
+metaTitle: "Using Modbus with Node-RED"
navTitle: Modbus
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/protocol/mqtt.md b/nuxt/content-guides/node-red/protocol/mqtt.md
index aa8dac16a1..08982507e2 100644
--- a/nuxt/content-guides/node-red/protocol/mqtt.md
+++ b/nuxt/content-guides/node-red/protocol/mqtt.md
@@ -1,5 +1,6 @@
---
title: Using MQTT with Node-RED
+metaTitle: "Using MQTT with Node-RED"
navTitle: MQTT
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/protocol/opc-ua.md b/nuxt/content-guides/node-red/protocol/opc-ua.md
index eb65a522bf..36cbe748ba 100644
--- a/nuxt/content-guides/node-red/protocol/opc-ua.md
+++ b/nuxt/content-guides/node-red/protocol/opc-ua.md
@@ -1,5 +1,6 @@
---
title: Building Secure OPC-UA Server in Node-RED.
+metaTitle: "Building Secure OPC-UA Server in Node-RED."
navTitle: OPC-UA
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/protocol/websocket.md b/nuxt/content-guides/node-red/protocol/websocket.md
index b9cf2abf56..f6c5ddf114 100644
--- a/nuxt/content-guides/node-red/protocol/websocket.md
+++ b/nuxt/content-guides/node-red/protocol/websocket.md
@@ -1,5 +1,6 @@
---
title: Using Websocket with Node-RED
+metaTitle: "Using Websocket with Node-RED"
navTitle: Websocket
navOrder: 50
meta:
diff --git a/nuxt/content-guides/node-red/terminology/README.md b/nuxt/content-guides/node-red/terminology/README.md
index c6e8ecb531..97eae7cd8c 100644
--- a/nuxt/content-guides/node-red/terminology/README.md
+++ b/nuxt/content-guides/node-red/terminology/README.md
@@ -1,5 +1,6 @@
---
title: Node-RED Terminology
+metaTitle: "Node-RED Terminology"
navTitle: Terminology
navOrder: 10
meta:
diff --git a/nuxt/content.config.ts b/nuxt/content.config.ts
index 2980ad4135..3212253a2d 100644
--- a/nuxt/content.config.ts
+++ b/nuxt/content.config.ts
@@ -29,6 +29,11 @@ export default defineContentConfig({
source: 'docs/**/*.md',
schema: z.object({
navTitle: z.string().optional(),
+ // The browser/search-result title, when the sidebar label is too short to
+ // serve as one. Read by nuxt/pages/docs/[...slug].vue; like navGroupOrder
+ // below, an undeclared key is stripped from frontmatter, which would make
+ // that read silently undefined.
+ metaTitle: z.string().optional(),
navGroup: z.string().optional(),
// Read by useDocsNav to rank the sidebar group headings; without it
// declared here @nuxt/content strips the key from frontmatter.
diff --git a/nuxt/lib/core-nodes-sync.mjs b/nuxt/lib/core-nodes-sync.mjs
index ee38ea2ee1..078d4d4b43 100644
--- a/nuxt/lib/core-nodes-sync.mjs
+++ b/nuxt/lib/core-nodes-sync.mjs
@@ -141,6 +141,11 @@ export async function fetchCoreNodeHelp ({ coreNodes, fetchImpl = fetch, retries
export function renderCoreNodePage (node, { useCase, help, navOrder }) {
const fm = [
`title: "Node-RED ${node.name} node"`,
+ // The and search-result title, kept byte-identical to the Eleventy page
+ // this replaces ("Node-RED - Inject Node"), because these 39 URLs 301 onto the new
+ // ones and carry their ranking with them. Without it the docs page falls back to
+ // navTitle and the title becomes the bare node name, "Inject".
+ `metaTitle: "Node-RED - ${node.name} Node"`,
`navTitle: "${node.name}"`,
`navOrder: ${navOrder}`,
'meta:',
@@ -219,6 +224,7 @@ export function syncCoreNodes ({ repoRoot, nuxtRoot, coreNodes, help, logger = c
// /docs/node-red/core-nodes/README/ that prerendered as 404s and failed the build.
writeFileSync(join(outDir, 'index.md'), `---
title: "Node-RED core nodes"
+metaTitle: "Node-RED Core Nodes"
navTitle: "Core nodes"
navOrder: 3
meta:
diff --git a/nuxt/lib/core-nodes-sync.test.mjs b/nuxt/lib/core-nodes-sync.test.mjs
index 4a46cb1911..a16cfb03c9 100644
--- a/nuxt/lib/core-nodes-sync.test.mjs
+++ b/nuxt/lib/core-nodes-sync.test.mjs
@@ -135,6 +135,20 @@ test('a page leads with the FlowFuse use case and puts the mirrored help under i
assert.match(out, /
upstream<\/p>/)
})
+test("a node page's browser title is the one the Eleventy page it replaces had", () => {
+ // /node-red/core-nodes/mqtt-in/ 301s onto /docs/node-red/core-nodes/mqtt-in/, so the
+ // title has to survive the move or the redirect hands Google a different page. The
+ // docs page prefers metaTitle over navTitle for exactly this; without it the title
+ // would be the bare node name.
+ const out = renderCoreNodePage(
+ { name: 'MQTT In', description: 'Subscribes to a topic' },
+ { useCase: '', help: '
upstream
', navOrder: 1 }
+ )
+
+ assert.match(out, /metaTitle: "Node-RED - MQTT In Node"/)
+ assert.match(out, /navTitle: "MQTT In"/)
+})
+
test('a sync missing any node help fails loudly and names the node', () => {
assert.throws(
() => syncCoreNodes({
diff --git a/nuxt/lib/guides-frontmatter.mjs b/nuxt/lib/guides-frontmatter.mjs
new file mode 100644
index 0000000000..7eff94d289
--- /dev/null
+++ b/nuxt/lib/guides-frontmatter.mjs
@@ -0,0 +1,105 @@
+// Frontmatter checks over the guides tree, for the two ways a page's titles go wrong
+// silently. Both of these shipped in the Node-RED library migration and neither produced
+// a build error, a broken link or a failing test - the only symptom was the rendered page
+// being wrong, which is why they are asserted here rather than left to review.
+//
+// Test-only, so unlike guides-sync.mjs this may depend on node_modules.
+
+import { join } from 'node:path'
+
+import { listGuideFiles } from './guides-sync.mjs'
+import { readFrontmatter } from './meta-title-length.mjs'
+
+/** Every markdown page under the guides tree, with its parsed frontmatter. */
+export function readGuidePages (guidesDir) {
+ return listGuideFiles(guidesDir)
+ .filter(relPath => relPath.endsWith('.md'))
+ .map(relPath => ({ relPath, frontmatter: readFrontmatter(join(guidesDir, relPath)) || {} }))
+}
+
+// A directory index, under either name. The guides are authored as README.md and
+// guides-sync renames them to index.md on the way into the content tree, so both spellings
+// have to count: recognising only one leaves every nested index in a bucket of its own,
+// and the duplicate-label check below then passes for the wrong reason.
+const INDEX_NAMES = new Set(['README.md', 'index.md'])
+
+/**
+ * The nav node a page labels: an index titles the directory it sits in, any other page
+ * titles itself. `node-red/getting-started/README.md` and `node-red/getting-started.md`
+ * would both label `node-red/getting-started`.
+ */
+function navNodeFor (relPath) {
+ const segments = relPath.split('/')
+ if (INDEX_NAMES.has(segments.at(-1))) return segments.slice(0, -1).join('/')
+ return [...segments.slice(0, -1), segments.at(-1).replace(/\.md$/, '')].join('/')
+}
+
+/**
+ * Groups of nav entries a reader cannot tell apart, because they share both a label and a
+ * position in the sidebar.
+ *
+ * The sidebar nests by directory and labels each entry with `navTitle`, so a label is only
+ * useful if it is unique among the entries it appears next to: its siblings, and the
+ * parent it is indented under. nuxt/lib/docs-nav.mjs cannot catch this, because as far as
+ * it is concerned the titles it was handed are the titles.
+ *
+ * The migration hit this by giving every nested directory index the nav key of its
+ * *parent* Eleventy nav entry instead of its own, so getting-started/{editor,library,
+ * programming}/README.md all came out as "Getting started" nested underneath a "Getting
+ * started" that was already there.
+ */
+export function findDuplicateSiblingNavTitles (guidesDir) {
+ const labelled = readGuidePages(guidesDir)
+ .filter(({ frontmatter }) => typeof frontmatter.navTitle === 'string')
+ .map(({ relPath, frontmatter }) => ({
+ relPath,
+ node: navNodeFor(relPath),
+ navTitle: frontmatter.navTitle,
+ }))
+
+ const fileFor = new Map(labelled.map(p => [p.node, p.relPath]))
+ const labelFor = new Map(labelled.map(p => [p.node, p.navTitle]))
+ const groups = new Map()
+
+ for (const { relPath, node, navTitle } of labelled) {
+ const parent = node.split('/').slice(0, -1).join('/')
+ const key = `${parent} ${navTitle}`
+
+ if (!groups.has(key)) groups.set(key, { under: parent || '.', navTitle, files: new Set() })
+ groups.get(key).files.add(relPath)
+
+ // A parent indented directly above its children is as ambiguous as two siblings,
+ // and it is the shape the screenshot on #5750 showed.
+ if (labelFor.get(parent) === navTitle) groups.get(key).files.add(fileFor.get(parent))
+ }
+
+ return [...groups.values()]
+ .filter(group => group.files.size > 1)
+ .map(({ under, navTitle, files }) => ({ under, navTitle, files: [...files].sort() }))
+}
+
+/**
+ * Pages whose full title can never reach a browser tab or a search result.
+ *
+ * nuxt/pages/docs/[...slug].vue titles a page `metaTitle || navTitle || title`. `navTitle`
+ * is deliberately short, because it has to fit the sidebar column, so a page that gives
+ * `title` the whole phrase and `navTitle` an abbreviation of it needs `metaTitle` to say
+ * which one Google gets. Without it the abbreviation wins and the phrase is only ever
+ * visible as the on-page heading.
+ *
+ * This is how 121 moved library pages lost their titles: "Using MySQL with Node-RED (2026
+ * Updated)" became "MySQL", with nothing anywhere reporting a problem.
+ */
+export function findPagesWithUnreachableTitle (guidesDir) {
+ return readGuidePages(guidesDir)
+ .filter(({ frontmatter: fm }) =>
+ typeof fm.title === 'string' &&
+ typeof fm.navTitle === 'string' &&
+ fm.navTitle !== fm.title &&
+ typeof fm.metaTitle !== 'string')
+ .map(({ relPath, frontmatter }) => ({
+ file: relPath,
+ title: frontmatter.title,
+ navTitle: frontmatter.navTitle,
+ }))
+}
diff --git a/nuxt/lib/guides-frontmatter.test.mjs b/nuxt/lib/guides-frontmatter.test.mjs
new file mode 100644
index 0000000000..0ae0ca2b5a
--- /dev/null
+++ b/nuxt/lib/guides-frontmatter.test.mjs
@@ -0,0 +1,127 @@
+import { test } from 'node:test'
+import assert from 'node:assert/strict'
+import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
+import { tmpdir } from 'node:os'
+import { dirname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+import { findDuplicateSiblingNavTitles, findPagesWithUnreachableTitle } from './guides-frontmatter.mjs'
+import { GUIDES_SOURCE } from './guides-sync.mjs'
+import { isDirectory } from './meta-title-length.mjs'
+
+const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '../..')
+const guidesDir = join(repoRoot, GUIDES_SOURCE)
+
+function scratch (files) {
+ const root = mkdtempSync(join(tmpdir(), 'guides-frontmatter-'))
+ for (const [relPath, content] of Object.entries(files)) {
+ const path = join(root, relPath)
+ mkdirSync(dirname(path), { recursive: true })
+ writeFileSync(path, content, 'utf8')
+ }
+ return root
+}
+
+function page (fm) {
+ const lines = Object.entries(fm).map(([k, v]) => `${k}: "${v}"`)
+ return `---\n${lines.join('\n')}\n---\n\n# Heading\n`
+}
+
+// Guards the real-tree tests below against a silent no-op if the source layout moves.
+test('the guides tree exists where these tests expect it', () => {
+ assert.ok(isDirectory(guidesDir), `expected ${guidesDir} to exist`)
+})
+
+test('every sidebar entry is distinguishable from the ones beside it', () => {
+ const duplicates = findDuplicateSiblingNavTitles(guidesDir)
+ assert.deepEqual(duplicates, [], duplicates
+ .map(d => `under ${d.under}/: ${d.files.length} nav entries all labelled "${d.navTitle}" (${d.files.join(', ')})`)
+ .join('\n'))
+})
+
+test('every page whose title is longer than its nav label says which one Google gets', () => {
+ const unreachable = findPagesWithUnreachableTitle(guidesDir)
+ assert.deepEqual(unreachable, [], unreachable
+ .map(p => `${p.file}: title "${p.title}" can never render, navTitle "${p.navTitle}" wins; set metaTitle`)
+ .join('\n'))
+})
+
+test('a directory index competes with its parent\'s children, not with its own', () => {
+ // The exact shape the migration got wrong: three nested indexes carrying the nav label
+ // of the section they sit under, which is already used by that section's own index.
+ const root = scratch({
+ 'node-red/README.md': page({ navTitle: 'Using Node-RED' }),
+ 'node-red/getting-started/README.md': page({ navTitle: 'Getting started' }),
+ 'node-red/getting-started/editor/README.md': page({ navTitle: 'Getting started' }),
+ 'node-red/getting-started/library/README.md': page({ navTitle: 'Getting started' }),
+ })
+
+ try {
+ const [group, ...rest] = findDuplicateSiblingNavTitles(root)
+ assert.deepEqual(rest, [])
+ assert.equal(group.navTitle, 'Getting started')
+ // The section's own index is named alongside its children: the reader sees one
+ // "Getting started" indented under another, which is what the reviewer screenshotted.
+ assert.deepEqual(group.files, [
+ 'node-red/getting-started/README.md',
+ 'node-red/getting-started/editor/README.md',
+ 'node-red/getting-started/library/README.md',
+ ].sort())
+ } finally {
+ rmSync(root, { recursive: true, force: true })
+ }
+})
+
+test('a directory index is recognised under either name, so the rename cannot blind this', () => {
+ // #5752 renames every guide README.md to index.md. Keying only off README.md put each
+ // nested index in a bucket of its own, and this check went green with the duplicate
+ // labels still in place.
+ for (const indexName of ['README.md', 'index.md']) {
+ const root = scratch({
+ [`node-red/getting-started/${indexName}`]: page({ navTitle: 'Getting started' }),
+ [`node-red/getting-started/editor/${indexName}`]: page({ navTitle: 'Getting started' }),
+ })
+
+ try {
+ const [group, ...rest] = findDuplicateSiblingNavTitles(root)
+ assert.deepEqual(rest, [], `${indexName}: expected exactly one clash`)
+ assert.deepEqual(group.files, [
+ `node-red/getting-started/${indexName}`,
+ `node-red/getting-started/editor/${indexName}`,
+ ].sort())
+ } finally {
+ rmSync(root, { recursive: true, force: true })
+ }
+ }
+})
+
+test('the same nav label in two different sections is fine', () => {
+ const root = scratch({
+ 'application-guide/worked-examples/README.md': page({ navTitle: 'Worked examples' }),
+ 'node-red-guide/worked-examples/README.md': page({ navTitle: 'Worked examples' }),
+ })
+
+ try {
+ assert.deepEqual(findDuplicateSiblingNavTitles(root), [])
+ } finally {
+ rmSync(root, { recursive: true, force: true })
+ }
+})
+
+test('a short nav label beside a long title needs a metaTitle, an identical one does not', () => {
+ const root = scratch({
+ 'node-red/database/mysql.md': page({ title: 'Using MySQL with Node-RED (2026 Updated)', navTitle: 'MySQL' }),
+ 'node-red/database/redis.md': page({ title: 'Using Redis with Node-RED', navTitle: 'Redis', metaTitle: 'Using Redis with Node-RED' }),
+ 'application-guide/data-plane.md': page({ title: 'Data plane', navTitle: 'Data plane' }),
+ })
+
+ try {
+ assert.deepEqual(findPagesWithUnreachableTitle(root), [{
+ file: 'node-red/database/mysql.md',
+ title: 'Using MySQL with Node-RED (2026 Updated)',
+ navTitle: 'MySQL',
+ }])
+ } finally {
+ rmSync(root, { recursive: true, force: true })
+ }
+})
diff --git a/nuxt/pages/docs/[...slug].vue b/nuxt/pages/docs/[...slug].vue
index a7bb180beb..e6063ba183 100644
--- a/nuxt/pages/docs/[...slug].vue
+++ b/nuxt/pages/docs/[...slug].vue
@@ -27,7 +27,15 @@ if (page.value.layout === 'redirect' && page.value.redirect?.to) {
throw navigateTo(target, { redirectCode: 301, external: isExternal })
}
-const pageTitle = computed(() => page.value?.navTitle || page.value?.title || slugParts.value.at(-1) || 'Documentation')
+// Two different titles: `navTitle` is the sidebar label, deliberately short enough to fit
+// the nav column, while the browser and search-result title wants the whole phrase. Pages
+// migrated from Eleventy carry that phrase in `metaTitle`, the same field src/blog and
+// src/changelog use, so it stays first here. Without it every moved library page's
+// collapsed to its nav label: "Using MySQL with Node-RED (2026 Updated)" became "MySQL".
+// No page from FlowFuse/flowfuse sets `metaTitle`, so their titles are unchanged.
+const pageTitle = computed(() =>
+ page.value?.metaTitle || page.value?.navTitle || page.value?.title || slugParts.value.at(-1) || 'Documentation'
+)
// Empty on most docs pages: only the ones a catalog feature names as its docsLink get badges.
const plans = useDocsPlans(contentPath)
From 88b3b982cfe3f0522632651b960af888ae287235 Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Tue, 8 Sep 2026 11:13:32 +0200
Subject: [PATCH 09/17] docs: put the core nodes back under their palette
categories
The review asked for the Common, Function, Network, Sequence, Parsers and Storage
grouping back. It went missing resolving a URL collision in the previous round: the
Function node and the Function category both wanted
/docs/node-red/core-nodes/function/, @nuxt/content kept whichever it indexed last with
no build error, and dropping the category pages was the cheap way out. It also dropped
the level of the sidebar that made the node pages navigable.
The categories become a directory each instead, which resolves the collision the other
way round: the category owns core-nodes/function/ and the node is
core-nodes/function/function/, so the two cannot contend for one path. The sidebar
nests the way the editor's palette does, because docs-nav builds the tree from paths
and has no nav-only grouping below the top-level navGroup. navOrder restarts per
category, since a node's siblings are now its own category's nodes.
The old flat URLs still reach a node in one hop, now to the nested path. The bare
category paths, which were pagination-only templates under Eleventy and were left to
404, have a real page to land on and are mapped too, except
/node-red/core-nodes/function/, which was the Function node's own URL and keeps
pointing at the node. Every cross-reference to a node page moves with it, and the
section index links each category heading as well as each node.
---
.../getting-started/node-red-messages.md | 2 +-
.../programming/debugging-flows.md | 4 +-
.../getting-started/programming/if-else.md | 2 +-
.../getting-started/programming/loop.md | 2 +-
.../node-red/integration-technologies/rest.md | 2 +-
.../node-red/protocol/websocket.md | 2 +-
nuxt/lib/core-nodes-sync.mjs | 59 ++++++++++--
nuxt/lib/core-nodes-sync.test.mjs | 47 ++++++----
nuxt/redirects-node-red.ts | 92 ++++++++++---------
nuxt/redirects.ts | 2 +-
src/_includes/core-nodes/delay-use-case.md | 2 +-
src/_includes/core-nodes/html-use-case.md | 2 +-
.../core-nodes/http-request-use-case.md | 2 +-
.../core-nodes/mqtt-broker-config-use-case.md | 2 +-
src/_includes/core-nodes/tcp-in-use-case.md | 2 +-
src/_includes/core-nodes/tpc-out-use-case.md | 4 +-
src/_includes/core-nodes/udp-out-use-case.md | 2 +-
...de-ethernet_ip-to-s7-protocol-converter.md | 2 +-
src/blog/2023/07/community-news-07.md | 4 +-
.../exploring-node-red-dashboard-2-widgets.md | 2 +-
.../07/calling-python-script-from-node-red.md | 2 +-
.../2024/11/building-uns-with-flowfuse.md | 4 +-
.../2024/12/publishing-modbus-data-to-uns.md | 2 +-
src/blog/2025/06/data-acquisition-for-mes.md | 4 +-
.../aperia-technologies-plc-replacement.md | 2 +-
src/webinars/2026/industrial-iot-solutions.md | 4 +-
...the-agile-factory-on-the-connected-edge.md | 2 +-
27 files changed, 160 insertions(+), 98 deletions(-)
diff --git a/nuxt/content-guides/node-red/getting-started/node-red-messages.md b/nuxt/content-guides/node-red/getting-started/node-red-messages.md
index 9ba15cdd68..1bc7377930 100644
--- a/nuxt/content-guides/node-red/getting-started/node-red-messages.md
+++ b/nuxt/content-guides/node-red/getting-started/node-red-messages.md
@@ -244,7 +244,7 @@ Steps:
3. Set the action to "Always convert to JavaScript Object" and click Done.
4. Connect the JSON node between the source node (sending the JSON string) and the destination node (requiring the parsed object).
-The JSON node automatically converts incoming JSON strings into JavaScript objects. For more details, see the [JSON node documentation](/docs/node-red/core-nodes/json/).
+The JSON node automatically converts incoming JSON strings into JavaScript objects. For more details, see the [JSON node documentation](/docs/node-red/core-nodes/parsers/json/).
## Common Mistakes to Avoid
diff --git a/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md b/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
index 6259a0185f..fa70f4aeaa 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/debugging-flows.md
@@ -10,7 +10,7 @@ meta:
# How to Debug Node-RED Flows Using Debugger
-When it comes to debugging application flows in Node-RED, the tool most Node-RED developers often reach for is the [Debug](/docs/node-red/core-nodes/debug/) node. It provides a simple way to output message payloads or other data to the debug sidebar, helping you gain insights into how your flow is working. But what if you needed more control and visibility over the flow’s execution? What if you wanted to step through each node in detail, inspect variables, or pause the flow at specific points to understand what’s happening?
+When it comes to debugging application flows in Node-RED, the tool most Node-RED developers often reach for is the [Debug](/docs/node-red/core-nodes/common/debug/) node. It provides a simple way to output message payloads or other data to the debug sidebar, helping you gain insights into how your flow is working. But what if you needed more control and visibility over the flow’s execution? What if you wanted to step through each node in detail, inspect variables, or pause the flow at specific points to understand what’s happening?
In these cases, using the **Node-RED Debugger** becomes invaluable. The debugger allows you to trace the execution of your flows interactively, set breakpoints, and gain deeper insights beyond what the Debug node offers. This Documentation will show you how to effectively use the Node-RED Debugger to pinpoint issues and fine-tune your applications.
@@ -105,7 +105,7 @@ Manually stepping through the flow is useful for understanding how the flow oper
## Adding Breakpoints for Debugging Flows
-Now that we've pinpointed the problem to be somewhere between the[Switch](/docs/node-red/core-nodes/switch/) node and the [Join](/docs/node-red/core-nodes/join/) node, it’s time to leverage breakpoints for a more efficient debugging experience. These breakpoints allow you to pause the flow automatically allowing you to inspect messages and context without having to step through each node manually. This is especially useful for larger or more intricate flows.
+Now that we've pinpointed the problem to be somewhere between the[Switch](/docs/node-red/core-nodes/function/switch/) node and the [Join](/docs/node-red/core-nodes/sequence/join/) node, it’s time to leverage breakpoints for a more efficient debugging experience. These breakpoints allow you to pause the flow automatically allowing you to inspect messages and context without having to step through each node manually. This is especially useful for larger or more intricate flows.
First, let’s discuss where exactly we should add breakpoints. Our previous debugging shows that all 11 messages are correctly reaching the input of the Switch node. However, we need to check how many messages pass through the Switch node's condition and whether they contain the required part object for the Join node to create a single value (array).
diff --git a/nuxt/content-guides/node-red/getting-started/programming/if-else.md b/nuxt/content-guides/node-red/getting-started/programming/if-else.md
index d60341c28b..bf6c456083 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/if-else.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/if-else.md
@@ -36,7 +36,7 @@ To implement If-Else logic in Node-RED, you can use the Switch node, which align
### Using Switch Node
-The [Switch](/docs/node-red/core-nodes/switch/) node in Node-RED is used for routing messages based on specific conditions, offering a straightforward, low-code approach to implementing conditional logic in your flows. The Switch node allows you to set up rules using a visual interface, making it ideal for users who prefer a more intuitive method for handling conditions. However, it’s important to note that the Switch node represents a different, independent concept known as the "[switch statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch)." While it serves a similar purpose to If-Else logic by building conditional flows, it operates under its own programming paradigm.
+The [Switch](/docs/node-red/core-nodes/function/switch/) node in Node-RED is used for routing messages based on specific conditions, offering a straightforward, low-code approach to implementing conditional logic in your flows. The Switch node allows you to set up rules using a visual interface, making it ideal for users who prefer a more intuitive method for handling conditions. However, it’s important to note that the Switch node represents a different, independent concept known as the "[switch statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch)." While it serves a similar purpose to If-Else logic by building conditional flows, it operates under its own programming paradigm.
To demonstrate the Switch node, we'll set up a flow to make decisions based on the temperature value. We will route messages through different outputs based on temperature thresholds.
diff --git a/nuxt/content-guides/node-red/getting-started/programming/loop.md b/nuxt/content-guides/node-red/getting-started/programming/loop.md
index 65574b1501..ad6e11dcb3 100644
--- a/nuxt/content-guides/node-red/getting-started/programming/loop.md
+++ b/nuxt/content-guides/node-red/getting-started/programming/loop.md
@@ -104,7 +104,7 @@ graph TD
#### For...of / ForEach Loop
-In traditional programming, `for...of` and `forEach` loops are commonly used to iterate through arrays or object properties, allowing for individual element processing. Since Node-RED doesn’t include these specific constructs, you can replicate their functionality by using a combination of nodes, particularly the **[Split](/docs/node-red/core-nodes/split/)** and **[Join](/docs/node-red/core-nodes/join/)** nodes.
+In traditional programming, `for...of` and `forEach` loops are commonly used to iterate through arrays or object properties, allowing for individual element processing. Since Node-RED doesn’t include these specific constructs, you can replicate their functionality by using a combination of nodes, particularly the **[Split](/docs/node-red/core-nodes/sequence/split/)** and **[Join](/docs/node-red/core-nodes/sequence/join/)** nodes.
Here’s how you can replicate this functionality in Node-RED:
diff --git a/nuxt/content-guides/node-red/integration-technologies/rest.md b/nuxt/content-guides/node-red/integration-technologies/rest.md
index 43d988a41e..36d795a1de 100644
--- a/nuxt/content-guides/node-red/integration-technologies/rest.md
+++ b/nuxt/content-guides/node-red/integration-technologies/rest.md
@@ -180,7 +180,7 @@ Connect the change node output to a new chart node, and voila:
While we created a chart and it has some data, there's one more thing to explain.
How can the data be kept up-to-date? It's straight forward to have the `Inject`
-node [run every night](/docs/node-red/core-nodes/inject/),
+node [run every night](/docs/node-red/core-nodes/common/inject/),
but the chart would now have multiple data points
for the same day. This paints multiple lines on top of each other. While that works,
the hover of the chart will display the duplication and it's wastefull.
diff --git a/nuxt/content-guides/node-red/protocol/websocket.md b/nuxt/content-guides/node-red/protocol/websocket.md
index f6c5ddf114..5830bbb5c9 100644
--- a/nuxt/content-guides/node-red/protocol/websocket.md
+++ b/nuxt/content-guides/node-red/protocol/websocket.md
@@ -107,4 +107,4 @@ _Testing Websocket Client with Websocket Server_
Now, to test the client, you can send messages from the server and see the debug window for that message in the client instance. Similarly, you can send messages from the client Instance to the server and observe the responses in the debug window of server instance.
-For more information on the advaced websocket node configuration refer to the [Websocket Node Documentation](/docs/node-red/core-nodes/websocket/)
+For more information on the advaced websocket node configuration refer to the [Websocket Node Documentation](/docs/node-red/core-nodes/network/websocket/)
diff --git a/nuxt/lib/core-nodes-sync.mjs b/nuxt/lib/core-nodes-sync.mjs
index 078d4d4b43..82fe821602 100644
--- a/nuxt/lib/core-nodes-sync.mjs
+++ b/nuxt/lib/core-nodes-sync.mjs
@@ -137,6 +137,42 @@ export async function fetchCoreNodeHelp ({ coreNodes, fetchImpl = fetch, retries
return help
}
+/**
+ * One palette category: the sidebar group its nodes sit in, and a page listing them.
+ *
+ * The categories are a level of the tree rather than headings on one page because
+ * nuxt/lib/docs-nav.mjs builds the sidebar from paths alone - there is no nav-only
+ * grouping field below the top-level navGroup - and the editor's own palette is grouped
+ * this way, so the sidebar should be too.
+ *
+ * Nesting is also what keeps the Function node and the Function category apart. Flat, both
+ * wanted /docs/node-red/core-nodes/function/ and whichever @nuxt/content resolved last
+ * won, silently. As a directory the category is that path and the node is
+ * core-nodes/function/function/, which is unambiguous.
+ */
+export function renderCoreNodeCategoryPage (category, meta, nodes) {
+ const list = nodes
+ .map(n => `- [${n.name}](/docs/node-red/core-nodes/${category}/${n.slug}/)${n.description ? `: ${n.description}` : ''}`)
+ .join('\n')
+
+ return `---
+title: "Node-RED ${meta.title.toLowerCase()} nodes"
+metaTitle: "Node-RED ${meta.title} Nodes"
+navTitle: "${meta.title}"
+navOrder: ${meta.order}
+meta:
+ description: "The ${meta.title} nodes in Node-RED's default palette, and what each one is for."
+---
+
+# ${meta.title}
+
+The **${meta.title}** section of Node-RED's default palette. Each page opens with why you
+would reach for that node, then mirrors the node's built-in help.
+
+${list}
+`
+}
+
/** One page: the FlowFuse use-case intro, then the mirrored upstream help. */
export function renderCoreNodePage (node, { useCase, help, navOrder }) {
const fm = [
@@ -184,9 +220,9 @@ export function renderCategorySections (nodes) {
.map(([category, meta]) => {
const list = nodes
.filter(n => n.category === category)
- .map(n => `- [${n.name}](/docs/node-red/core-nodes/${n.slug}/)${n.description ? `: ${n.description}` : ''}`)
+ .map(n => `- [${n.name}](/docs/node-red/core-nodes/${category}/${n.slug}/)${n.description ? `: ${n.description}` : ''}`)
.join('\n')
- return `## ${meta.title}\n\n${list}`
+ return `## [${meta.title}](/docs/node-red/core-nodes/${category}/)\n\n${list}`
})
.join('\n\n')
}
@@ -233,28 +269,35 @@ meta:
# Core nodes
-Every node in Node-RED's default palette, grouped the way the editor groups them. Each
-page opens with why you would reach for that node, then mirrors the node's built-in help.
+Every node in Node-RED's default palette, grouped into the same sections the editor's
+palette uses. Each page opens with why you would reach for that node, then mirrors the
+node's built-in help.
${renderCategorySections(nodes)}
`, 'utf8')
- let count = 1
- for (const [category] of Object.entries(CATEGORIES)) {
+ for (const [category, meta] of Object.entries(CATEGORIES)) {
const inCategory = nodes.filter(n => n.category === category)
if (!inCategory.length) continue
+ const categoryDir = join(outDir, category)
+ mkdirSync(categoryDir, { recursive: true })
+ writeFileSync(join(categoryDir, 'index.md'), renderCoreNodeCategoryPage(category, meta, inCategory), 'utf8')
+
+ // Restarts per category: navOrder ranks a node among its siblings, which are now
+ // the nodes in its own category rather than all of them.
+ let navOrder = 1
for (const node of inCategory) {
const useCasePath = join(repoRoot, 'src/_includes/core-nodes', `${node.slug}-use-case.md`)
const useCase = existsSync(useCasePath)
? processLibraryMarkdown(readFileSync(useCasePath, 'utf8'), { title: node.name })
: ''
- const dest = join(outDir, `${node.slug}.md`)
+ const dest = join(categoryDir, `${node.slug}.md`)
mkdirSync(dirname(dest), { recursive: true })
writeFileSync(dest, renderCoreNodePage(node, {
useCase,
help: help[`${node.category}/${node.slug}`],
- navOrder: count++,
+ navOrder: navOrder++,
}), 'utf8')
}
}
diff --git a/nuxt/lib/core-nodes-sync.test.mjs b/nuxt/lib/core-nodes-sync.test.mjs
index a16cfb03c9..f030a093a0 100644
--- a/nuxt/lib/core-nodes-sync.test.mjs
+++ b/nuxt/lib/core-nodes-sync.test.mjs
@@ -136,7 +136,7 @@ test('a page leads with the FlowFuse use case and puts the mirrored help under i
})
test("a node page's browser title is the one the Eleventy page it replaces had", () => {
- // /node-red/core-nodes/mqtt-in/ 301s onto /docs/node-red/core-nodes/mqtt-in/, so the
+ // /node-red/core-nodes/mqtt-in/ 301s onto /docs/node-red/core-nodes/network/mqtt-in/, so the
// title has to survive the move or the redirect hands Google a different page. The
// docs page prefers metaTitle over navTitle for exactly this; without it the title
// would be the bare node name.
@@ -162,12 +162,12 @@ test('a sync missing any node help fails loudly and names the node', () => {
)
})
-test('a node whose name matches its category does not collide with a category page', () => {
- // The Function node lives in the `function` category. A category page at
- // core-nodes/function/ and the node page at core-nodes/function.md both resolve to
- // /docs/node-red/core-nodes/function/, and @nuxt/content picks whichever it indexed
- // last. The legacy redirect for that URL is the NODE, so no category pages are
- // emitted at all.
+test('a node whose name matches its category does not collide with its category page', () => {
+ // The Function node lives in the `function` category, and both want to be called
+ // "function". Flat, the category page at core-nodes/function/ and the node page at
+ // core-nodes/function.md resolved to the same URL and @nuxt/content kept whichever it
+ // indexed last, with no build error. Nesting separates them: the category owns the
+ // directory, the node is a file inside it.
const root = mkdtempSync(join(tmpdir(), 'core-nodes-collide-'))
const catalogue = { function: [{ xpath: 'function', name: 'Function', file: '10-function' }] }
@@ -179,10 +179,13 @@ test('a node whose name matches its category does not collide with a category pa
logger: { info () {} },
})
- const entries = readdirSync(join(root, 'nuxt/content/docs/node-red/core-nodes')).sort()
+ const dir = join(root, 'nuxt/content/docs/node-red/core-nodes')
+ assert.deepEqual(readdirSync(dir).sort(), ['function', 'index.md'])
+ assert.deepEqual(readdirSync(join(dir, 'function')).sort(), ['function.md', 'index.md'])
- assert.deepEqual(entries, ['function.md', 'index.md'])
- assert.ok(!entries.includes('function'), 'no category directory may shadow the node page')
+ // /docs/node-red/core-nodes/function/ is the category, and the node is one level down.
+ assert.match(readFileSync(join(dir, 'function/index.md'), 'utf8'), /navTitle: "Function"/)
+ assert.match(readFileSync(join(dir, 'function/function.md'), 'utf8'), /navTitle: "Function"/)
})
test('a full sync writes a page per node plus the section index', () => {
@@ -198,19 +201,27 @@ test('a full sync writes a page per node plus the section index', () => {
})
const dir = join(root, 'nuxt/content/docs/node-red/core-nodes')
- const entries = readdirSync(dir).sort()
assert.equal(count, 2)
// index.md, not README.md: this writes straight into the content tree rather than
// through guides-sync, which is what renames README on the way in. README.md here
// prerenders as /docs/node-red/core-nodes/README/ and 404s the build.
//
- // And no per-category directories: one node page per node, flat, plus the section
- // index. See the collision note on renderCategorySections.
- assert.deepEqual(entries, ['index.md', 'inject.md', 'mqtt-in.md'])
+ // A directory per palette category, each with its own index, so the sidebar nests the
+ // way the editor's palette does. Categories with no nodes in the catalogue are skipped.
+ assert.deepEqual(readdirSync(dir).sort(), ['common', 'index.md', 'network'])
+ assert.deepEqual(readdirSync(join(dir, 'common')).sort(), ['index.md', 'inject.md'])
+ assert.deepEqual(readdirSync(join(dir, 'network')).sort(), ['index.md', 'mqtt-in.md'])
+
assert.match(readFileSync(join(dir, 'index.md'), 'utf8'), /navTitle: "Core nodes"/)
- // The section index carries the per-category grouping the category pages used to.
- const index = readFileSync(join(dir, 'index.md'), 'utf8')
- assert.match(index, /## Common/)
- assert.match(index, /\/docs\/node-red\/core-nodes\/inject\//)
+ assert.match(readFileSync(join(dir, 'common/index.md'), 'utf8'), /navTitle: "Common"/)
+
+ // Both the section index and the category page link the node at its nested URL.
+ for (const page of ['index.md', 'common/index.md']) {
+ assert.match(readFileSync(join(dir, page), 'utf8'), /\/docs\/node-red\/core-nodes\/common\/inject\//)
+ }
+
+ // navOrder ranks a node among its own category's nodes, so each category restarts at 1.
+ assert.match(readFileSync(join(dir, 'common/inject.md'), 'utf8'), /navOrder: 1/)
+ assert.match(readFileSync(join(dir, 'network/mqtt-in.md'), 'utf8'), /navOrder: 1/)
})
diff --git a/nuxt/redirects-node-red.ts b/nuxt/redirects-node-red.ts
index b125a1fe01..e02f8d2fd9 100644
--- a/nuxt/redirects-node-red.ts
+++ b/nuxt/redirects-node-red.ts
@@ -7,52 +7,60 @@
// 404, which is what a path-preserving splat would do for every typo and stale deep link.
//
// The core-node entries are derived from src/_data/coreNodes.json, since those pages were
-// paginated out of that catalogue and never existed as files. Only real node slugs are
-// listed: a bare category path like /node-red/core-nodes/common/ was never a page either,
-// because the .njk templates were pagination-only. /node-red/core-nodes/function/ IS
-// listed, because that was the Function node's own URL.
+// paginated out of that catalogue and never existed as files. They gain a category segment
+// on the way: the palette categories are a level of the sidebar again, so the Inject node
+// is /docs/node-red/core-nodes/common/inject/. A bare category path like
+// /node-red/core-nodes/common/ was never a page under Eleventy - the .njk templates were
+// pagination-only - but it now has a real page to land on, so it is mapped rather than
+// left to 404. /node-red/core-nodes/function/ is the exception: that was the Function
+// *node's* URL, so it keeps pointing at the node, which nesting puts one level down.
import type { NitroRouteRules } from 'nitropack'
const moved: Record = {
'/node-red/core-nodes/': '/docs/node-red/core-nodes/',
- '/node-red/core-nodes/batch/': '/docs/node-red/core-nodes/batch/',
- '/node-red/core-nodes/catch/': '/docs/node-red/core-nodes/catch/',
- '/node-red/core-nodes/change/': '/docs/node-red/core-nodes/change/',
- '/node-red/core-nodes/comment/': '/docs/node-red/core-nodes/comment/',
- '/node-red/core-nodes/complete/': '/docs/node-red/core-nodes/complete/',
- '/node-red/core-nodes/csv/': '/docs/node-red/core-nodes/csv/',
- '/node-red/core-nodes/debug/': '/docs/node-red/core-nodes/debug/',
- '/node-red/core-nodes/delay/': '/docs/node-red/core-nodes/delay/',
- '/node-red/core-nodes/exec/': '/docs/node-red/core-nodes/exec/',
- '/node-red/core-nodes/filter/': '/docs/node-red/core-nodes/filter/',
- '/node-red/core-nodes/function/': '/docs/node-red/core-nodes/function/',
- '/node-red/core-nodes/html/': '/docs/node-red/core-nodes/html/',
- '/node-red/core-nodes/http-in/': '/docs/node-red/core-nodes/http-in/',
- '/node-red/core-nodes/http-proxy/': '/docs/node-red/core-nodes/http-proxy/',
- '/node-red/core-nodes/http-request/': '/docs/node-red/core-nodes/http-request/',
- '/node-red/core-nodes/inject/': '/docs/node-red/core-nodes/inject/',
- '/node-red/core-nodes/join/': '/docs/node-red/core-nodes/join/',
- '/node-red/core-nodes/json/': '/docs/node-red/core-nodes/json/',
- '/node-red/core-nodes/link/': '/docs/node-red/core-nodes/link/',
- '/node-red/core-nodes/mqtt-in/': '/docs/node-red/core-nodes/mqtt-in/',
- '/node-red/core-nodes/mqtt-out/': '/docs/node-red/core-nodes/mqtt-out/',
- '/node-red/core-nodes/range/': '/docs/node-red/core-nodes/range/',
- '/node-red/core-nodes/read-file/': '/docs/node-red/core-nodes/read-file/',
- '/node-red/core-nodes/sort/': '/docs/node-red/core-nodes/sort/',
- '/node-red/core-nodes/split/': '/docs/node-red/core-nodes/split/',
- '/node-red/core-nodes/status/': '/docs/node-red/core-nodes/status/',
- '/node-red/core-nodes/switch/': '/docs/node-red/core-nodes/switch/',
- '/node-red/core-nodes/tcp-in/': '/docs/node-red/core-nodes/tcp-in/',
- '/node-red/core-nodes/template/': '/docs/node-red/core-nodes/template/',
- '/node-red/core-nodes/tls/': '/docs/node-red/core-nodes/tls/',
- '/node-red/core-nodes/trigger/': '/docs/node-red/core-nodes/trigger/',
- '/node-red/core-nodes/udp-in/': '/docs/node-red/core-nodes/udp-in/',
- '/node-red/core-nodes/udp-out/': '/docs/node-red/core-nodes/udp-out/',
- '/node-red/core-nodes/unknown/': '/docs/node-red/core-nodes/unknown/',
- '/node-red/core-nodes/websocket/': '/docs/node-red/core-nodes/websocket/',
- '/node-red/core-nodes/write-file/': '/docs/node-red/core-nodes/write-file/',
- '/node-red/core-nodes/xml/': '/docs/node-red/core-nodes/xml/',
- '/node-red/core-nodes/yaml/': '/docs/node-red/core-nodes/yaml/',
+ '/node-red/core-nodes/common/': '/docs/node-red/core-nodes/common/',
+ '/node-red/core-nodes/network/': '/docs/node-red/core-nodes/network/',
+ '/node-red/core-nodes/parsers/': '/docs/node-red/core-nodes/parsers/',
+ '/node-red/core-nodes/sequence/': '/docs/node-red/core-nodes/sequence/',
+ '/node-red/core-nodes/storage/': '/docs/node-red/core-nodes/storage/',
+ '/node-red/core-nodes/batch/': '/docs/node-red/core-nodes/sequence/batch/',
+ '/node-red/core-nodes/catch/': '/docs/node-red/core-nodes/common/catch/',
+ '/node-red/core-nodes/change/': '/docs/node-red/core-nodes/function/change/',
+ '/node-red/core-nodes/comment/': '/docs/node-red/core-nodes/common/comment/',
+ '/node-red/core-nodes/complete/': '/docs/node-red/core-nodes/common/complete/',
+ '/node-red/core-nodes/csv/': '/docs/node-red/core-nodes/parsers/csv/',
+ '/node-red/core-nodes/debug/': '/docs/node-red/core-nodes/common/debug/',
+ '/node-red/core-nodes/delay/': '/docs/node-red/core-nodes/function/delay/',
+ '/node-red/core-nodes/exec/': '/docs/node-red/core-nodes/function/exec/',
+ '/node-red/core-nodes/filter/': '/docs/node-red/core-nodes/function/filter/',
+ '/node-red/core-nodes/function/': '/docs/node-red/core-nodes/function/function/',
+ '/node-red/core-nodes/html/': '/docs/node-red/core-nodes/parsers/html/',
+ '/node-red/core-nodes/http-in/': '/docs/node-red/core-nodes/network/http-in/',
+ '/node-red/core-nodes/http-proxy/': '/docs/node-red/core-nodes/network/http-proxy/',
+ '/node-red/core-nodes/http-request/': '/docs/node-red/core-nodes/network/http-request/',
+ '/node-red/core-nodes/inject/': '/docs/node-red/core-nodes/common/inject/',
+ '/node-red/core-nodes/join/': '/docs/node-red/core-nodes/sequence/join/',
+ '/node-red/core-nodes/json/': '/docs/node-red/core-nodes/parsers/json/',
+ '/node-red/core-nodes/link/': '/docs/node-red/core-nodes/common/link/',
+ '/node-red/core-nodes/mqtt-in/': '/docs/node-red/core-nodes/network/mqtt-in/',
+ '/node-red/core-nodes/mqtt-out/': '/docs/node-red/core-nodes/network/mqtt-out/',
+ '/node-red/core-nodes/range/': '/docs/node-red/core-nodes/function/range/',
+ '/node-red/core-nodes/read-file/': '/docs/node-red/core-nodes/storage/read-file/',
+ '/node-red/core-nodes/sort/': '/docs/node-red/core-nodes/sequence/sort/',
+ '/node-red/core-nodes/split/': '/docs/node-red/core-nodes/sequence/split/',
+ '/node-red/core-nodes/status/': '/docs/node-red/core-nodes/common/status/',
+ '/node-red/core-nodes/switch/': '/docs/node-red/core-nodes/function/switch/',
+ '/node-red/core-nodes/tcp-in/': '/docs/node-red/core-nodes/network/tcp-in/',
+ '/node-red/core-nodes/template/': '/docs/node-red/core-nodes/function/template/',
+ '/node-red/core-nodes/tls/': '/docs/node-red/core-nodes/network/tls/',
+ '/node-red/core-nodes/trigger/': '/docs/node-red/core-nodes/function/trigger/',
+ '/node-red/core-nodes/udp-in/': '/docs/node-red/core-nodes/network/udp-in/',
+ '/node-red/core-nodes/udp-out/': '/docs/node-red/core-nodes/network/udp-out/',
+ '/node-red/core-nodes/unknown/': '/docs/node-red/core-nodes/common/unknown/',
+ '/node-red/core-nodes/websocket/': '/docs/node-red/core-nodes/network/websocket/',
+ '/node-red/core-nodes/write-file/': '/docs/node-red/core-nodes/storage/write-file/',
+ '/node-red/core-nodes/xml/': '/docs/node-red/core-nodes/parsers/xml/',
+ '/node-red/core-nodes/yaml/': '/docs/node-red/core-nodes/parsers/yaml/',
'/node-red/database/': '/docs/node-red/database/',
'/node-red/database/dynamodb/': '/docs/node-red/database/dynamodb/',
'/node-red/database/firebase/': '/docs/node-red/database/firebase/',
diff --git a/nuxt/redirects.ts b/nuxt/redirects.ts
index 9e5ad3701f..8c9455fff5 100644
--- a/nuxt/redirects.ts
+++ b/nuxt/redirects.ts
@@ -49,7 +49,7 @@ export const redirects: Record = {
'/landing/technology-migration-1/': { redirect: { to: '/vs/kepware/', statusCode: 301 } },
'/landing/technology-migration-2/': { redirect: { to: '/vs/kepware/', statusCode: 301 } },
'/blog/2025/10/the-ai-orchestation-hype/': { redirect: { to: '/blog/2025/10/the-ai-orchestration-hype/', statusCode: 301 } },
- '/node-red/core-nodes/mqtt/': { redirect: { to: '/docs/node-red/core-nodes/mqtt-in/', statusCode: 301 } },
+ '/node-red/core-nodes/mqtt/': { redirect: { to: '/docs/node-red/core-nodes/network/mqtt-in/', statusCode: 301 } },
'/blueprints/manufacturing/manufacturing-support-request/': { redirect: { to: '/blueprints/manufacturing/andon-system/', statusCode: 301 } },
'/blueprints/other/remote-buffering/': { redirect: { to: '/blueprints/getting-started/store-and-forward/', statusCode: 301 } },
'/vs/tulip/': { redirect: { to: '/landing/tulip/', statusCode: 301 } },
diff --git a/src/_includes/core-nodes/delay-use-case.md b/src/_includes/core-nodes/delay-use-case.md
index 24f0260f25..82d28ae91c 100644
--- a/src/_includes/core-nodes/delay-use-case.md
+++ b/src/_includes/core-nodes/delay-use-case.md
@@ -32,7 +32,7 @@ There's situations however where the queue needs to be cleared. This is done by
When a `reset` property is set, the queue will be empty.
This is useful for when the lack of an event might need to send a notification. Schedule the notification
-to be sent but allow a positive event to cancel the notification. In the example below; say you want to turn off the lights if no movement was detected for a period of time, you set the delay node to that time. When there's motion detected, you than send a `msg.reset` message to the delay node to cancel them turning of the light. This depends on an inject node set to [send a message on an interval](/docs/node-red/core-nodes/inject/#run-a-flow-daily-at-midnight) to turn the lights off.
+to be sent but allow a positive event to cancel the notification. In the example below; say you want to turn off the lights if no movement was detected for a period of time, you set the delay node to that time. When there's motion detected, you than send a `msg.reset` message to the delay node to cancel them turning of the light. This depends on an inject node set to [send a message on an interval](/docs/node-red/core-nodes/common/inject/#run-a-flow-daily-at-midnight) to turn the lights off.
{% renderFlow %}
[{"id":"06b93157ff07c9b1","type":"delay","z":"e512003df3c971c7","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":460,"y":100,"wires":[["f345c902d09aaf76"]]},{"id":"1965e58562943d69","type":"inject","z":"e512003df3c971c7","name":"Motion detected","props":[{"p":"reset","v":"true","vt":"bool"}],"repeat":"5","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":250,"y":160,"wires":[["06b93157ff07c9b1"]]},{"id":"f345c902d09aaf76","type":"debug","z":"e512003df3c971c7","name":"Never receive a message","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":670,"y":100,"wires":[]},{"id":"834f0812a01be7a4","type":"inject","z":"e512003df3c971c7","name":"Turn off lights","props":[{"p":"payload"}],"repeat":"10","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":100,"wires":[["06b93157ff07c9b1"]]}]
diff --git a/src/_includes/core-nodes/html-use-case.md b/src/_includes/core-nodes/html-use-case.md
index 423bce325b..5397fe72c2 100644
--- a/src/_includes/core-nodes/html-use-case.md
+++ b/src/_includes/core-nodes/html-use-case.md
@@ -2,7 +2,7 @@ Extracts elements from an HTML document.
## Where and why do we use the HTML node?
-The HTML node parses HTML documents and extracts specific elements using CSS selectors. This is essential when you need to scrape data from web pages, extract specific content from HTML responses, or process HTML documents to retrieve structured information. Unlike the [template node](/docs/node-red/core-nodes/template/) which generates HTML, this node is purely for parsing and extraction.
+The HTML node parses HTML documents and extracts specific elements using CSS selectors. This is essential when you need to scrape data from web pages, extract specific content from HTML responses, or process HTML documents to retrieve structured information. Unlike the [template node](/docs/node-red/core-nodes/function/template/) which generates HTML, this node is purely for parsing and extraction.
## How it works
diff --git a/src/_includes/core-nodes/http-request-use-case.md b/src/_includes/core-nodes/http-request-use-case.md
index 23dc272303..79cc53467f 100644
--- a/src/_includes/core-nodes/http-request-use-case.md
+++ b/src/_includes/core-nodes/http-request-use-case.md
@@ -24,7 +24,7 @@ Below, you'll find a range of settings to tailor HTTP requests to fit the needs
- **bearer:** Uses Bearer token authentication where a bearer token, typically a JSON Web Token (JWT), is sent in the Authorization header.
- **Token:** Provide the bearer token if bearer authentication is selected.
- **Enable Connection Keep-Alive:** Enabling this option will allow Maintain persistent connections for efficiency.
-- **Use Proxy:** Allows to Route requests through a proxy server if necessary, for more information on the configuration of [HTTP Proxy](/docs/node-red/core-nodes/http-proxy/) config node
+- **Use Proxy:** Allows to Route requests through a proxy server if necessary, for more information on the configuration of [HTTP Proxy](/docs/node-red/core-nodes/network/http-proxy/) config node
- **Only send non-2xx responses to Catch node:** Enabling this option will send only non-success responses to the Catch node.
- **Disable Strict HTTP Parsing:** Enabling this option relaxes how Node-RED interprets HTTP responses. It's handy when dealing with responses that don't perfectly match the standard HTTP format.
- **Return:** Allows to Choose the format for response data conversion
diff --git a/src/_includes/core-nodes/mqtt-broker-config-use-case.md b/src/_includes/core-nodes/mqtt-broker-config-use-case.md
index c32b49b9a4..8c0bba72d9 100644
--- a/src/_includes/core-nodes/mqtt-broker-config-use-case.md
+++ b/src/_includes/core-nodes/mqtt-broker-config-use-case.md
@@ -13,7 +13,7 @@ The MQTT-Broker config node in Node-RED is used to define the connection setting
- **MQTT V3.1.1 (legacy)**
- **MQTT V3.1.1**
- **MQTT V5**
-- **Use TLS**: Enabling this option node will use TLS, for more information TLS refer to [TLS node docs](/docs/node-red/core-nodes/tls/)
+- **Use TLS**: Enabling this option node will use TLS, for more information TLS refer to [TLS node docs](/docs/node-red/core-nodes/network/tls/)
- **Client ID**: The client identifier to use when connecting to the broker. If left blank, a random ID will be generated.
- **Keep Alive**: This setting defines the interval (in seconds) at which the MQTT client sends a "keep alive" message to the broker to maintain the connection. It ensures that the connection remains active even if there is no other data being transmitted. If you set this to 0, the client will not send keep-alive messages.
- **Session**:
diff --git a/src/_includes/core-nodes/tcp-in-use-case.md b/src/_includes/core-nodes/tcp-in-use-case.md
index c1bb66ff62..b62dedc27c 100644
--- a/src/_includes/core-nodes/tcp-in-use-case.md
+++ b/src/_includes/core-nodes/tcp-in-use-case.md
@@ -13,7 +13,7 @@ TCP (Transmission Control Protocol) is one of the core protocols of the Internet
- **Connect to:** Enables the TCP node to act as a client, establishing connections to remote servers.
- **Hostname:** Specifies the hostname or IP address of the remote server when using the "Connect to" type.
- **Port:** Specifies the TCP port number to listen on (when using "Listen on" type) or to connect to (when using "Connect to" type).
-- **Enable secure (SSL/TLS) connection:** Enabling this option activates SSL/TLS for secure communication. In Node-RED we have TLS config node which allows to activate TLS secure communication, refer to [TLS config node](/docs/node-red/core-nodes/tls/) for details on configurations.
+- **Enable secure (SSL/TLS) connection:** Enabling this option activates SSL/TLS for secure communication. In Node-RED we have TLS config node which allows to activate TLS secure communication, refer to [TLS config node](/docs/node-red/core-nodes/network/tls/) for details on configurations.
- **Output:**
- **Streams of:** Selecting this option will outputs the data stream received from the TCP connection as a continuous stream of messages.
- **Single:** Selecting this option will outputs a single message containing the data received from the TCP connection.
diff --git a/src/_includes/core-nodes/tpc-out-use-case.md b/src/_includes/core-nodes/tpc-out-use-case.md
index 43c7498e62..64b28e7fa0 100644
--- a/src/_includes/core-nodes/tpc-out-use-case.md
+++ b/src/_includes/core-nodes/tpc-out-use-case.md
@@ -2,7 +2,7 @@
TCP-Out nodes in Node-RED facilitate communication over TCP/IP networks by providing options to connect to remote TCP ports, accept incoming connections, or reply to messages received from a TCP In node.
-If you dont know what is TCP, refer to [What is TCP](/docs/node-red/core-nodes/tcp-in/#what-is-udp/).
+If you dont know what is TCP, refer to [What is TCP](/docs/node-red/core-nodes/network/tcp-in/#what-is-udp/).
## Configuring TCP-Out Node
@@ -16,7 +16,7 @@ To configure a TCP-Out node in Node-RED, follow these steps:
2. **Port**: Specify the port number for the TCP connection.
-3. **Enable Secure (SSL/TLS) Connection**: Activate SSL/TLS for secure communication. If enabled, Node-RED provides a TLS config node for configuring TLS settings. Refer to the [TLS config node](/docs/node-red/core-nodes/tls/) documentation for details on configurations.
+3. **Enable Secure (SSL/TLS) Connection**: Activate SSL/TLS for secure communication. If enabled, Node-RED provides a TLS config node for configuring TLS settings. Refer to the [TLS config node](/docs/node-red/core-nodes/network/tls/) documentation for details on configurations.
4. **Close Connection After Each Message is Sent**: Choose whether to close the connection after sending each message.
diff --git a/src/_includes/core-nodes/udp-out-use-case.md b/src/_includes/core-nodes/udp-out-use-case.md
index 6e6791f932..7893b21a3c 100644
--- a/src/_includes/core-nodes/udp-out-use-case.md
+++ b/src/_includes/core-nodes/udp-out-use-case.md
@@ -2,7 +2,7 @@
The udp-out node in Node-RED is a node used for sending UDP messages to a specified network destination. When you add a udp-out node to your Node-RED flow, you configure it with the IP address and port of the destination device or service. Then, any messages received by the udp-out node are sent as UDP packets to that destination. This node is particularly useful for applications where real-time communication or lightweight message transmission is required.
-For more information on UDP refer to [What is UDP](/docs/node-red/core-nodes/udp-in/#what-is-udp%3F)
+For more information on UDP refer to [What is UDP](/docs/node-red/core-nodes/network/udp-in/#what-is-udp%3F)
## Configuring UDP-Out Node
diff --git a/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md b/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md
index 0988dbcf31..45ad0ca59f 100644
--- a/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md
+++ b/src/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter.md
@@ -197,7 +197,7 @@ Depending on how noisy the REAL data is, which is common with unfiltered 4-20mA

-In the example above, we arbitrarily applied a 3% [deadband](/docs/node-red/core-nodes/filter/)
+In the example above, we arbitrarily applied a 3% [deadband](/docs/node-red/core-nodes/function/filter/)
to the `Robot_Position` value, which means that the value must change by greater than or equal to 3% compared to the last input value, or else the data will be discarded before being sent to the stacklight PLC.
You can adjust the deadband to find the right balance for your particular application.
diff --git a/src/blog/2023/07/community-news-07.md b/src/blog/2023/07/community-news-07.md
index 9778bdbab5..df7c08971f 100644
--- a/src/blog/2023/07/community-news-07.md
+++ b/src/blog/2023/07/community-news-07.md
@@ -38,8 +38,8 @@ Our next webinar will be focused on the device management capabilities in the Fl
- [MQTT and its Role in IoT and Industrial IoT](/docs/node-red/protocol/mqtt/) - A practical explainer on the role of MQTT in IoT use cases and how to connect with an MQTT broker in Node-RED.
- Two new Node-RED Nodes Explained articles
- - [Nodes explained: Split](/docs/node-red/core-nodes/split/)
- - [Nodes explained: Filter](/docs/node-red/core-nodes/filter/)
+ - [Nodes explained: Split](/docs/node-red/core-nodes/sequence/split/)
+ - [Nodes explained: Filter](/docs/node-red/core-nodes/function/filter/)
## From the Community
diff --git a/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md b/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md
index 20982f38a7..ec7e69d7a2 100644
--- a/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md
+++ b/src/blog/2024/05/exploring-node-red-dashboard-2-widgets.md
@@ -148,7 +148,7 @@ _Screenshot displaying the change node retrieving expense data from global conte
{data-zoomable}
_Screenshot displaying the change node converting amount to number_
-5. Drag a **Join** node onto the canvas, select mode as **reduced expression**, and set the **Reduce exp** to `$A + payload.amount`. Set Initial value to `0`, and **Fix-up exp** to `$A`. Give this **join** node the name "Calculate total". This function operates similarly to using the javascript reduce method on an array to calculate the sum of its values. `$A` stores the accumulated value, and with every incoming message payload, it adds the `payload.amount` value to it, for more details on this refer to the [core node docs on join node](/docs/node-red/core-nodes/join/).
+5. Drag a **Join** node onto the canvas, select mode as **reduced expression**, and set the **Reduce exp** to `$A + payload.amount`. Set Initial value to `0`, and **Fix-up exp** to `$A`. Give this **join** node the name "Calculate total". This function operates similarly to using the javascript reduce method on an array to calculate the sum of its values. `$A` stores the accumulated value, and with every incoming message payload, it adds the `payload.amount` value to it, for more details on this refer to the [core node docs on join node](/docs/node-red/core-nodes/sequence/join/).
{data-zoomable}
_Screenshot displaying the join node calculating the total income and expense data_
diff --git a/src/blog/2024/07/calling-python-script-from-node-red.md b/src/blog/2024/07/calling-python-script-from-node-red.md
index a1e5e97c05..540bbb2b2e 100644
--- a/src/blog/2024/07/calling-python-script-from-node-red.md
+++ b/src/blog/2024/07/calling-python-script-from-node-red.md
@@ -103,7 +103,7 @@ Let's now see how to call a Python script from Node-RED. First, we'll create a b
```
::
-Now, let's execute this Python script from Node-RED. To do that, we will use Node-RED's [Exec](/docs/node-red/core-nodes/exec/) node, which allows running commands on your system.
+Now, let's execute this Python script from Node-RED. To do that, we will use Node-RED's [Exec](/docs/node-red/core-nodes/function/exec/) node, which allows running commands on your system.
1. Drag an Inject node onto the canvas.
2. Drag an Exec node onto the canvas and Configure the command to `python ./example.py -u`. The -u flag prevents potential output buffering issues when executing Python scripts via exec.
diff --git a/src/blog/2024/11/building-uns-with-flowfuse.md b/src/blog/2024/11/building-uns-with-flowfuse.md
index 6fb4c51e4c..54d5278414 100644
--- a/src/blog/2024/11/building-uns-with-flowfuse.md
+++ b/src/blog/2024/11/building-uns-with-flowfuse.md
@@ -80,7 +80,7 @@ While this is more readable, it can still be challenging to monitor changes in v
{data-zoomable}
_Change node: Calculating Magnitude_
-After calculating the Magnitude using a [Change node](/docs/node-red/core-nodes/change/), the data might look like this:
+After calculating the Magnitude using a [Change node](/docs/node-red/core-nodes/function/change/), the data might look like this:
```json
{
@@ -160,7 +160,7 @@ While Sparkplug B provides a standardized topic model, its hierarchy may not alw
### Step 5: Sending Collected metrics to UNS
-With your topic naming convention chosen, it’s time to send the data to the UNS. In Node-RED, we will use the [MQTT Out](/docs/node-red/core-nodes/mqtt-in/) node to send the transformed data to the broker.
+With your topic naming convention chosen, it’s time to send the data to the UNS. In Node-RED, we will use the [MQTT Out](/docs/node-red/core-nodes/network/mqtt-in/) node to send the transformed data to the broker.
1. Drag an MQTT out node into your flow.
2. Configure the node to connect to the FlowFuse MQTT Broker using the client credentials generated earlier.
diff --git a/src/blog/2024/12/publishing-modbus-data-to-uns.md b/src/blog/2024/12/publishing-modbus-data-to-uns.md
index a8bf6aefc8..c794c8e14a 100644
--- a/src/blog/2024/12/publishing-modbus-data-to-uns.md
+++ b/src/blog/2024/12/publishing-modbus-data-to-uns.md
@@ -161,7 +161,7 @@ We must convert these raw register values into human-readable formats for cloud
To determine how to process raw Modbus data, such as dividing by a specific value, concatenating, or applying other transformation formulas, refer to the manual of the sensor you use for specific instructions.
-In Node-RED, you can use various nodes for transformation. You can choose the [Function node](/docs/node-red/core-nodes/function/) for advanced processing, the [Change node](/docs/node-red/core-nodes/change/) for simpler operations, or the [Template node](/docs/node-red/core-nodes/template/) for defining schemas. For more complex data parsing scenarios, such as handling multiple data types (floats, 32-bit integers, strings), dealing with big-endian/little-endian conversions, or performing byte swapping, consider using the `node-red-contrib-buffer-parser` node. This specialized node simplifies parsing Modbus buffers into various data formats without writing custom code. Learn more in our guide on [using Buffer Parser for industrial data](/blog/2025/12/node-red-buffer-parser-industrial-data/). In this article, I will demonstrate a low-code approach using the Change node to process the data cleanly.
+In Node-RED, you can use various nodes for transformation. You can choose the [Function node](/docs/node-red/core-nodes/function/) for advanced processing, the [Change node](/docs/node-red/core-nodes/function/change/) for simpler operations, or the [Template node](/docs/node-red/core-nodes/function/template/) for defining schemas. For more complex data parsing scenarios, such as handling multiple data types (floats, 32-bit integers, strings), dealing with big-endian/little-endian conversions, or performing byte swapping, consider using the `node-red-contrib-buffer-parser` node. This specialized node simplifies parsing Modbus buffers into various data formats without writing custom code. Learn more in our guide on [using Buffer Parser for industrial data](/blog/2025/12/node-red-buffer-parser-industrial-data/). In this article, I will demonstrate a low-code approach using the Change node to process the data cleanly.
Additionally, for better organization and accessibility, I will send each metric separately and include additional metadata such as the `timestamp` and `unit`.
diff --git a/src/blog/2025/06/data-acquisition-for-mes.md b/src/blog/2025/06/data-acquisition-for-mes.md
index d65c2ba4e6..07b70b7c0e 100644
--- a/src/blog/2025/06/data-acquisition-for-mes.md
+++ b/src/blog/2025/06/data-acquisition-for-mes.md
@@ -64,12 +64,12 @@ Following are some of the most commonly used protocol nodes:
- **Modbus:**
- **OPC UA:**
- **OPC DA:**
-- **MQTT:** [/docs/node-red/core-nodes/mqtt-in/](/docs/node-red/core-nodes/mqtt-in/)
+- **MQTT:** [/docs/node-red/core-nodes/network/mqtt-in/](/docs/node-red/core-nodes/network/mqtt-in/)
- **Ethernet/IP:**
- **Siemens S7:**
- **MITSUBISHI MC:**
- **OMRON FINS:**
-- **HTTP:** [/docs/node-red/core-nodes/http-in/](/docs/node-red/core-nodes/http-in/)
+- **HTTP:** [/docs/node-red/core-nodes/network/http-in/](/docs/node-red/core-nodes/network/http-in/)
- **LwM2M:**
- **AMQP:** [/docs/node-red/protocol/amqp/](/docs/node-red/protocol/amqp/)
- **Serialport:**
diff --git a/src/customer-stories/aperia-technologies-plc-replacement.md b/src/customer-stories/aperia-technologies-plc-replacement.md
index ea78449f3d..e3e27c01b9 100644
--- a/src/customer-stories/aperia-technologies-plc-replacement.md
+++ b/src/customer-stories/aperia-technologies-plc-replacement.md
@@ -54,7 +54,7 @@ This manual overhead was limiting Aperia's ability to scale manufacturing operat
## The FlowFuse Solution
-Aperia's answer was to remove the proprietary PLC from the center of the architecture entirely. Instead, smart fixtures run Node-RED on an edge Windows instance, managed by FlowFuse. At the hardware layer, Node-RED talks directly to sensors, actuators, scanners, and motors using standard industrial protocols: [Modbus TCP](/docs/node-red/protocol/modbus/)/[RTU](/blog/2025/09/using-modbus-with-flowfuse/), [RS232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), [raw TCP](/docs/node-red/core-nodes/tcp-in/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/). At the software layer, it triggers label printing, logs serialized telemetry to [MongoDB](/docs/node-red/database/mongodb/), syncs maintenance events to Limble, and pulls data from NetSuite ERP. [FlowFuse functions as the orchestration layer](/use-cases/it-ot-middleware/) that ties hardware protocols and enterprise APIs into one canvas, through two strategic implementations:
+Aperia's answer was to remove the proprietary PLC from the center of the architecture entirely. Instead, smart fixtures run Node-RED on an edge Windows instance, managed by FlowFuse. At the hardware layer, Node-RED talks directly to sensors, actuators, scanners, and motors using standard industrial protocols: [Modbus TCP](/docs/node-red/protocol/modbus/)/[RTU](/blog/2025/09/using-modbus-with-flowfuse/), [RS232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), [raw TCP](/docs/node-red/core-nodes/network/tcp-in/), and [EtherNet/IP](/blog/2025/10/using-ethernet-ip-with-flowfuse/). At the software layer, it triggers label printing, logs serialized telemetry to [MongoDB](/docs/node-red/database/mongodb/), syncs maintenance events to Limble, and pulls data from NetSuite ERP. [FlowFuse functions as the orchestration layer](/use-cases/it-ot-middleware/) that ties hardware protocols and enterprise APIs into one canvas, through two strategic implementations:
### Turning Idle Test Equipment into R&D Capacity
diff --git a/src/webinars/2026/industrial-iot-solutions.md b/src/webinars/2026/industrial-iot-solutions.md
index 952ac49da0..ea5875fd05 100644
--- a/src/webinars/2026/industrial-iot-solutions.md
+++ b/src/webinars/2026/industrial-iot-solutions.md
@@ -10,7 +10,7 @@ meta:
Industrial IoT (IIoT) is the use of connected sensors and devices to collect data from industrial operations and feed it into software for monitoring and real-time action. FlowFuse is the platform that runs on top of that data: the flows that connect it, the dashboards that display it, and the governance that keeps it under control.
- question: "What's the difference between FlowFuse Hub, Edge, and Fleet?"
answer: >
- Hub integrates enterprise systems and APIs for IT teams. Edge connects PLCs and machines across sites for OT teams. Fleet manages Node-RED across thousands of distributed devices. All three run on the same platform; the difference is which deployment shape each one is built for.
+ Hub integrates enterprise systems and APIs for IT teams. Edge connects PLCs and machines across sites for OT teams. Fleet manages Node-RED across thousands of distributed devices. All three run on the same platform; the difference is which deployment shape each one is built for.
- question: "How do I know which industrial IoT solution is right for my setup?"
answer: >
It comes down to where your applications live: a data center (Hub), a plant floor (Edge), or thousands of field devices (Fleet). We'll walk through all three live in the webinar and help map your specific setup to one of them. Bring your architecture and we'll sort it live in the Q&A.
@@ -44,7 +44,7 @@ Join Kristopher Sandoval (Product Marketing), Jamie Strusz (Engineering), Drew G
## Which industrial IoT solution fits my setup?
-- FlowFuse Hub integrates [enterprise systems](/landing/enterprise-integration/) and [APIs](/docs/node-red/core-nodes/http-request/), FlowFuse Edge connects [PLCs](/landing/plc/) and machines across sites, FlowFuse Fleet manages [Node-RED](/node-red/) across thousands of devices. We'll map your setup to one of the three, live.
+- FlowFuse Hub integrates [enterprise systems](/landing/enterprise-integration/) and [APIs](/docs/node-red/core-nodes/network/http-request/), FlowFuse Edge connects [PLCs](/landing/plc/) and machines across sites, FlowFuse Fleet manages [Node-RED](/node-red/) across thousands of devices. We'll map your setup to one of the three, live.
- Build faster with Industrial AI: [FlowFuse Expert](/docs/user/expert/) opens its [MCP server](/blog/2025/10/building-mcp-server-using-flowfuse/) to 3rd party agents, such as Cursor, Claude Code, Gemini CLI, and even in-house agents. Any MCP-speaking agent works. Point your AI agent at your FlowFuse instance and it builds, debugs, and fixes flows directly.
- See what's running, everywhere: [role-based access control](/docs/user/role-based-access-control/), [audit logging](/docs/user/logs/#audit-log), and [one place to manage every instance and device](/blog/2024/10/managing-node-red-instances-in-centralize-platfrom/), no matter which product you run.
- Read a [dashboard](/platform/dashboard/) at a glance: new [built-in themes](https://dashboard.flowfuse.com/nodes/config/ui-theme.html) with WCAG AA contrast, [charts](https://dashboard.flowfuse.com/nodes/widgets/ui-chart) that render instead of leaving empty boxes, and readable dates instead of raw timestamps.
diff --git a/src/webinars/2026/the-agile-factory-on-the-connected-edge.md b/src/webinars/2026/the-agile-factory-on-the-connected-edge.md
index a5b4360931..f93dd1dd80 100644
--- a/src/webinars/2026/the-agile-factory-on-the-connected-edge.md
+++ b/src/webinars/2026/the-agile-factory-on-the-connected-edge.md
@@ -25,7 +25,7 @@ In this session, Rhythm Agarwal (Senior Manufacturing Test Engineer at Aperia) a
## How Aperia Replaced Traditional PLCs with Edge Applications
- **Extends the shop floor, doesn't replace it.** Smart fixtures run on FlowFuse instead of a PLC, so existing equipment keeps working, just connected.
-- **Speaks every protocol.** Sensors, scanners, and actuators connect over [MODBUS-TCP](/docs/node-red/protocol/modbus/), [RS-232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), and [raw TCP](/docs/node-red/core-nodes/tcp-in/), all feeding into one data flow.
+- **Speaks every protocol.** Sensors, scanners, and actuators connect over [MODBUS-TCP](/docs/node-red/protocol/modbus/), [RS-232](/blog/2025/07/connect-legacy-equipment-serial-flowfuse/), and [raw TCP](/docs/node-red/core-nodes/network/tcp-in/), all feeding into one data flow.
- **Logs data the moment it happens.** Every station writes to a shared [MongoDB](/docs/node-red/database/mongodb/) backend in real time, not on a delay.
- **One fix, applied everywhere.** Code changes roll out to entire device groups from a single pipeline, with full version control, not machine by machine.
From 11cde53c73320c5f691196d45d01dd42761d1f84 Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Tue, 8 Sep 2026 11:16:36 +0200
Subject: [PATCH 10/17] docs: stop a use-case include adding a second H1 to a
core node page
The MQTT In and MQTT Out pages rendered the node name as an H1 twice in a row. Their
use-case includes open with `# {{ meta.title }}`, and the page is assembled here with
its own `# `, so resolving that interpolation put two identical headings
next to each other.
Eleventy had the same duplicate heading, but empty: `meta.title` was undefined on a
paginated core-node page, so it rendered as a bare `
` and nobody saw
it. Making the title resolve is what made it visible.
The include's headings now fold under the one the generator emits. A heading that just
repeats the node name is dropped, since it says nothing the H1 above it does not, and
any other H1 becomes an H2, which is what it should have been inside an include.
Fenced blocks are untouched, so a `#` comment in an example stays a comment. Three
other includes carried a second H1 of their own and are folded the same way.
The check runs over the committed includes rather than a fixture, since the defect was
in the content and only two files had it.
---
nuxt/lib/core-nodes-sync.mjs | 34 +++++++++++++++++-
nuxt/lib/core-nodes-sync.test.mjs | 57 ++++++++++++++++++++++++++++++-
2 files changed, 89 insertions(+), 2 deletions(-)
diff --git a/nuxt/lib/core-nodes-sync.mjs b/nuxt/lib/core-nodes-sync.mjs
index 82fe821602..95f7c39bdf 100644
--- a/nuxt/lib/core-nodes-sync.mjs
+++ b/nuxt/lib/core-nodes-sync.mjs
@@ -137,6 +137,36 @@ export async function fetchCoreNodeHelp ({ coreNodes, fetchImpl = fetch, retries
return help
}
+/**
+ * Fold a use-case include's own H1s under the H1 this generator emits.
+ *
+ * The page is assembled here, so it owns the `# ` heading; a use-case file that
+ * also opens with one leaves the page with two. `mqtt-in-use-case.md` and
+ * `mqtt-out-use-case.md` start with `# {{ meta.title }}`, which the library transforms
+ * resolve to the node's own name, so those two rendered the node name twice in a row.
+ *
+ * On Eleventy the same duplicate H1 was there but empty (`meta.title` was undefined on a
+ * paginated core-node page), so it was invisible and stayed. Resolving the interpolation
+ * made it visible, which is how the review caught it.
+ *
+ * A heading that just repeats the node name is dropped rather than demoted, since it says
+ * nothing the H1 above it has not. Any other H1 becomes an H2, which is what it should
+ * have been inside an include: a section of the page, not the page's title. Fenced blocks
+ * are left byte for byte, so a `#` comment in an example is not a heading.
+ */
+export function foldUseCaseHeadings (content, nodeName) {
+ const normaliseHeading = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim()
+ const isNodeName = (text) => normaliseHeading(text) === normaliseHeading(nodeName)
+
+ return content.split(/(^```[\s\S]*?^```)/gm).map((block, index) => {
+ if (index % 2 === 1) return block
+
+ return block
+ .replace(/^# +(.+?)[ \t]*$\n?(?:[ \t]*\n)*/gm, (_match, text) =>
+ isNodeName(text) ? '' : `## ${text}\n\n`)
+ }).join('')
+}
+
/**
* One palette category: the sidebar group its nodes sit in, and a page listing them.
*
@@ -290,7 +320,9 @@ ${renderCategorySections(nodes)}
for (const node of inCategory) {
const useCasePath = join(repoRoot, 'src/_includes/core-nodes', `${node.slug}-use-case.md`)
const useCase = existsSync(useCasePath)
- ? processLibraryMarkdown(readFileSync(useCasePath, 'utf8'), { title: node.name })
+ ? foldUseCaseHeadings(
+ processLibraryMarkdown(readFileSync(useCasePath, 'utf8'), { title: node.name }),
+ node.name)
: ''
const dest = join(categoryDir, `${node.slug}.md`)
mkdirSync(dirname(dest), { recursive: true })
diff --git a/nuxt/lib/core-nodes-sync.test.mjs b/nuxt/lib/core-nodes-sync.test.mjs
index f030a093a0..b8910c5965 100644
--- a/nuxt/lib/core-nodes-sync.test.mjs
+++ b/nuxt/lib/core-nodes-sync.test.mjs
@@ -1,17 +1,22 @@
import assert from 'node:assert/strict'
import { mkdtempSync, readFileSync, readdirSync } from 'node:fs'
import { tmpdir } from 'node:os'
-import { join } from 'node:path'
+import { dirname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
import test from 'node:test'
import {
extractHelp,
fetchCoreNodeHelp,
+ foldUseCaseHeadings,
listNodes,
renderCoreNodePage,
slugFor,
syncCoreNodes,
} from './core-nodes-sync.mjs'
+import { processLibraryMarkdown } from './library-markdown.mjs'
+
+const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '../..')
const CATALOGUE = {
common: [{ xpath: 'inject', name: 'Inject', file: '20-inject', description: 'Injects a message', keywords: 'inject' }],
@@ -149,6 +154,56 @@ test("a node page's browser title is the one the Eleventy page it replaces had",
assert.match(out, /navTitle: "MQTT In"/)
})
+test('a use-case include cannot add a second H1 to the page', () => {
+ // mqtt-out-use-case.md opens with `# {{ meta.title }}`, which resolves to the node's
+ // own name, so the page rendered "MQTT Out" as an H1 twice in a row. Eleventy had the
+ // same duplicate but empty, which is why it survived this long.
+ assert.equal(
+ foldUseCaseHeadings('# MQTT Out\n\nReach for this to publish.\n', 'MQTT Out'),
+ 'Reach for this to publish.\n'
+ )
+
+ // An H1 that says something else is a section of the page, not its title.
+ assert.equal(
+ foldUseCaseHeadings('# What is TCP-Out Nodes in Node-RED ?\n\nbody\n', 'TCP Out'),
+ '## What is TCP-Out Nodes in Node-RED ?\n\nbody\n'
+ )
+
+ // A `#` inside a fence is a comment in an example, not a heading.
+ assert.equal(
+ foldUseCaseHeadings('```bash\n# install it\n```\n', 'Exec'),
+ '```bash\n# install it\n```\n'
+ )
+})
+
+test('every real use-case include leaves the assembled page with exactly one H1', () => {
+ // Over the committed includes rather than a fixture: the duplication was in the
+ // content, not the code, and only two of the files had it.
+ const useCaseDir = join(repoRoot, 'src/_includes/core-nodes')
+ const offenders = []
+
+ for (const file of readdirSync(useCaseDir).filter(f => f.endsWith('-use-case.md'))) {
+ const name = file.replace(/-use-case\.md$/, '')
+ const page = renderCoreNodePage(
+ { name, description: 'd' },
+ {
+ useCase: foldUseCaseHeadings(
+ processLibraryMarkdown(readFileSync(join(useCaseDir, file), 'utf8'), { title: name }),
+ name),
+ help: '
help
',
+ navOrder: 1,
+ }
+ )
+ // Outside fenced blocks, so a `#` comment in an example does not count.
+ const h1s = page
+ .split(/^```[\s\S]*?^```/gm)
+ .flatMap(part => part.match(/^# .+$/gm) || [])
+ if (h1s.length !== 1) offenders.push(`${file}: ${h1s.length} H1s (${h1s.join(' | ')})`)
+ }
+
+ assert.deepEqual(offenders, [], offenders.join('\n'))
+})
+
test('a sync missing any node help fails loudly and names the node', () => {
assert.throws(
() => syncCoreNodes({
From 1b95eac4ce2a99fac34f5c302bf7baec5a496b36 Mon Sep 17 00:00:00 2001
From: Dimitrie Hoekstra
Date: Tue, 8 Sep 2026 11:28:21 +0200
Subject: [PATCH 11/17] docs: fix the links the category nesting broke, and the
comments it invalidated
The link checker caught two classes the nesting broke, both of which depended on
how deep a core-node page sits.
Relative asset references. The use-case includes live in src/_includes/core-nodes/
but their images live with the library pages, so they point at `./images/x.png`.
That resolves against the built page's own directory, which only worked while the
pages sat directly under core-nodes/, beside images/. Adding the category level sent
every one of them to core-nodes//images/. The asset path is written out
now, so a page's depth stops mattering, which is what the includes already did for
their