# Enabling Soft-Launched Features for Specific Teams During a soft launch campaign, new features are gated behind PostHog feature flags and enabled on a per-team basis. This guide describes the process for enabling a feature flag for a specific team on FlowFuse Cloud. ## Process ### 1. Change Request A change request is created in the **CloudProject** repository. The request must include: - The **internal team ID** of the team that needs the feature enabled - The **feature** that needs enabling ### 2. Identify the Feature Flag Key The developer or admin handling the request should know which PostHog feature flag key gates the requested feature. If unsure, ask in the **Slack engineering channel**. ### 3. Update the Feature Flag in PostHog 1. Log into **PostHog** on the **production project** 2. In the left-hand menu, navigate to **Features > Feature Flags** 3. Find the relevant feature flag key in the list and open it for editing ### 4. Add a Release Condition Under **Release conditions**, add a new condition set for the team: 1. Click to add a new condition set 2. Give the condition set a **description** — use the team or customer name for readability (see [Organizing Condition Sets](https://flowfuse.com/#organizing-condition-sets) below) 3. Add a filter: **`team-id`** — **equals** — **``** 4. Set the **rollout percentage** to **100%** 5. **Save** the feature flag ### 5. Verify and Close - Verify that the feature flag change is reflected on **production/cloud** — confirm the team now has access to the feature - Once verified, close the change request in the CloudProject repository ## Organizing Condition Sets When adding release conditions, prefer **one condition set per customer**. If a customer has multiple teams, group them under the same condition set. This approach keeps things readable because each condition set can have a description identifying the customer, avoiding the need to track and correlate raw team IDs. If the feature flag already has existing condition sets, follow the pattern that was established when the flag was first created. Use your judgement on the best approach — the goal is to keep the list of conditions manageable and easy to audit. # Kubernetes Hardening This guide walks you through the best ways to secure a Kubernetes cluster running FlowFuse. Locking down your cluster shrinks your attack surface and keeps things contained if a single component gets compromised. These tips work best when implemented together, so roll out as many of them as your environment allows. ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} These are general hardening recommendations. Adapt them to your organisation's security policies and your cluster's specific configuration. ::: :: ## Network Policies By default, Kubernetes allows unrestricted network traffic between all pods, across all namespaces. Any pod can reach any other pod on any port. This flat network model means that a single compromised pod, for example a vulnerable instance, can be used to reach and attack every other workload in the cluster. Network Policies let you enforce the principle of least privilege at the network layer: a pod should only be able to talk to the workloads it genuinely needs. Restricting traffic contains lateral movement, so a breach in one component cannot trivially spread to others. ::div{.ff-callout.ff-callout--warning} Warning :::div{.ff-callout__content} **Do not treat the policies below as a copy-and-paste solution.** They are illustrative examples, tied to the assumptions of the environment they were written for - namespace names, the ingress controller, the CNI, service ports, which components are deployed, and where operators live. Applied blindly they will either break platform traffic or leave gaps you believe are closed. Network Policies are one of the easiest things in Kubernetes to get subtly wrong: a rule that *looks* correct can silently drop traffic (wrong port direction, Service vs pod port, a missing return path) or silently allow it (an unenforced CNI, an overly broad selector). Implement them deliberately, with a working understanding of how traffic actually flows in your cluster - pod-to-pod, cross-namespace, ingress, egress, and DNS. Roll out one policy at a time, start in a non-production environment, verify each addition against real traffic (and check the affected pods' logs and Service endpoints), and confirm your CNI actually enforces policies before relying on them for security. ::: :: ### FlowFuse and Network Policies FlowFuse runs the platform (namespace of your choice, selected during Helm chart installation) and the hosted Node-RED instances (configured with the `forge.projectNamespace` Helm chart value) in separate namespaces. If you enforce Network Policies, you must explicitly allow the traffic FlowFuse needs - otherwise the instances cannot reach the platform. See [I use Kubernetes Network Policies, how can I configure them?](https://flowfuse.com/docs/install/kubernetes#i-use-kubernetes-network-policies-how-can-i-configure-them) for the required policy. ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} The following examples assume the default namespaces `flowfuse`, as a core application namespace, and `projects` for Hosted Instances namespace. If you have configured different namespaces, replace them accordingly. ::: :: The two namespaces have very different trust levels, so they are hardened differently: - **`flowfuse`** runs trusted first-party components (core app, MQTT broker, private registry, local database). Here we restrict **inbound** traffic only — deny all ingress, then allow the connections the platform needs. Egress is left open so the platform can reach external services (licensing, npm registry, SMTP, etc.) without maintaining a brittle allow-list. - **`projects`** runs Node-RED instances executing **user-supplied flows** - untrusted code. Here we lock down **both ingress and egress** to contain a malicious or compromised flow: it should reach only the platform services it legitimately needs, and nothing else. ### Core platform namespace (`flowfuse`) **1. Deny all inbound traffic.** Egress is intentionally not restricted here. ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress namespace: flowfuse spec: podSelector: {} policyTypes: - Ingress ``` **2. Allow inbound from the ingress controller** so users can reach the application (namespace usually `traefik`): ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-from-traefik namespace: flowfuse spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: traefik ``` **3. Allow inbound from the Hosted Instances** so Node-RED instances can reach the MQTT broker, core app and the private registry: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-from-projects namespace: flowfuse spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: projects ``` **4. Allow traffic between the platform's own components** in this namespace - the core app connecting to the database, broker and registry, plus broker clustering. As these are all trusted first-party components, we allow intra-namespace traffic: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-intra-namespace namespace: flowfuse spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: flowfuse ``` ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} The Helm chart also ships a `flowforge-database-policy` that permits the core app → database connection when using the embedded database. The `allow-intra-namespace` rule above is a superset of it; keep both if you prefer defence in depth. ::: :: **5. Allow inbound from the EMQX operator** - The MQTT broker cluster is managed by the EMQX operator, which usually runs in its own namespace (`emqx-operator` by default) and polls the broker's management API (port `18083`) to set a pod *readiness gate*. If this is blocked, the check times out, the readiness gate never turns true, the broker pods are marked `NotReady`, their Service endpoints go empty, and every broker client fails to connect with a `503` error response. This rule is required for the operator to manage the broker cluster correctly: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-from-emqx-operator namespace: flowfuse spec: podSelector: matchLabels: apps.emqx.io/instance: emqx policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: emqx-operator ports: - protocol: TCP port: 18083 ``` ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} The same pattern applies to any other operator, admission webhook, or metrics controller that must reach pods in this namespace: allow ingress from its namespace, or its readiness/reconcile checks will silently break your Services. If a Service unexpectedly loses its endpoints after applying policies, check the managing controller's logs for connection timeouts. ::: :: ### Hosted Instances namespace (`projects`) This namespace runs untrusted user flows, so we deny **both** directions by default and add back only what an instance legitimately needs. **1. Deny all inbound and outbound traffic:** ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: projects spec: podSelector: {} policyTypes: - Ingress - Egress ``` **Instance isolation comes for free here.** With no rule permitting `projects` → `projects` traffic, Node-RED instances cannot reach another in either direction. A separate "deny pod-to-pod" policy is not needed since a compromised instance already cannot talk to its neighbours. **2. Allow DNS resolution** (cluster DNS, usually in `kube-system`): ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-dns namespace: projects spec: podSelector: {} policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - protocol: UDP port: 53 - protocol: TCP port: 53 ``` **3. Allow traffic with the ingress controller**: - **Ingress:** users reach the Node-RED editor and dashboards - **Egress:** allow a Hosted Instance to reach the core app and private npm registry through the ingress controller ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-traefik namespace: projects spec: podSelector: {} policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: traefik egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: traefik ``` **4. Allow inbound from the platform** so the core app can manage and health-check instances: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-from-flowfuse namespace: projects spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: flowfuse ``` **5. Allow outbound to the platform services** - the MQTT broker, core app and private npm registry. Instances **should not** connect to the database directly, so it is deliberately not allowed: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-egress-to-flowfuse namespace: projects spec: podSelector: {} policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: flowfuse ports: - protocol: TCP port: 1883 # MQTT broker - protocol: TCP port: 1884 # MQTT over WebSocket - protocol: TCP port: 3000 # core app pod port - protocol: TCP port: 4873 # private npm registry ``` **6. (Optional) Allow outbound to the public internet.** Thanks to the `default-deny-all` policy, instances **do not have** access to the Internet. If your flows need to call external APIs, add the rule below. It allows outbound to the internet while excluding private ranges: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-egress-external namespace: projects spec: podSelector: {} policyTypes: - Egress egress: - to: - ipBlock: cidr: 0.0.0.0/0 except: - 10.0.0.0/8 # RFC1918 private range - 172.16.0.0/12 # RFC1918 private range - 192.168.0.0/16 # RFC1918 private range - 169.254.0.0/16 # link-local: blocks the cloud metadata endpoint (169.254.169.254) and node-local DNS - 100.64.0.0/10 # CGNAT range used internally by some managed clusters (GKE, EKS) ports: - protocol: TCP port: 443 - protocol: TCP port: 80 ``` The `except` list is what makes this rule safe: it blocks instances from reaching internal networks even while internet access is open. Note the `169.254.0.0/16` entry in particular as it blocks the cloud metadata endpoint (`169.254.169.254`), which would otherwise let a compromised flow retrieve the node's cloud IAM credentials. `100.64.0.0/10` covers the CGNAT range that some managed Kubernetes providers use internally. Include it if your cluster does. Note that excluding `169.254.0.0/16` also blocks node-local DNS cache, which runs on a link-local address. If your cluster uses it, add an egress rule allowing UDP/TCP `53` to that address (commonly `169.254.20.10`) so DNS keeps resolving. ## TLS for Ingress Without TLS, all traffic between users and the platform, including login credentials, session cookies, API tokens and flow data, travels in plaintext. Anyone able to observe the network path (a compromised router, a shared Wi-Fi network, a malicious intermediary) can read or tamper with it. Enabling TLS encrypts this traffic and lets clients verify they are talking to the genuine platform, protecting against eavesdropping and man-in-the-middle attacks. Serving the platform over HTTPS is a baseline requirement for any production deployment. FlowFuse supports TLS termination either at a cloud Load Balancer or at the Kubernetes Ingress Controller (via [Cert-Manager](https://cert-manager.io/docs/){rel=""nofollow""}). Full configuration steps are in the installation guide: - [I would like to secure the platform with HTTPS, how can I do that?](https://flowfuse.com/docs/install/kubernetes#i-would-like-to-secure-the-platform-with-https-how-can-i-do-that) ## Database Hardening FlowFuse stores all its data in a PostgreSQL database. The database is a critical component of the platform, and if it is compromised, an attacker can read or modify all data. The following recommendations reduce the risk of compromise and limit the impact if it does happen. ### Use a dedicated, least-privilege database user The FlowFuse application should connect using a dedicated database user that owns only its own database — never the PostgreSQL superuser (`postgres`). If the application's credentials are leaked, a scoped user limits the blast radius to the FlowFuse database rather than the entire database server. Create a dedicated user and database, for example: ```sql CREATE USER flowfuse WITH PASSWORD 'a-strong-generated-password'; CREATE DATABASE flowforge OWNER flowfuse; ``` Then configure FlowFuse to connect with these credentials. See [How to use external database server?](https://flowfuse.com/docs/install/kubernetes/#how-to-use-external-database-server%3F){rel=""nofollow""} for how to configure the connection in `customization.yml`. Additional recommendations: - **Use a strong, randomly generated password** - **Require TLS for database connections** so credentials and data are encrypted in transit between the platform and the database - **Restrict network access** to the database so only the FlowFuse platform can reach it (see [Network Policies](https://flowfuse.com/#network-policies), or your cloud provider's firewall / security group rules for managed databases) ### Backups Regular backups protect against data loss from accidental deletion, corruption, or a failed upgrade. A hardened deployment is not complete without a tested backup strategy. - **External / managed database:** use your database provider's backup and point-in-time-recovery features - **Embedded database:** if you use the Helm chart's internal PostgreSQL (`forge.localPostgresql: true`), you can schedule backups with a Kubernetes CronJob running `pg_dump`. A ready-to-use `CronJob` + `PersistentVolumeClaim` example is provided in the installation guide: [How to backup embedded database?](https://flowfuse.com/docs/install/kubernetes/#how-to-backup-embedded-database%3F){rel=""nofollow""} ::div{.ff-callout.ff-callout--warning} Warning :::div{.ff-callout__content} **Test your restores.** A backup is only useful if it can be restored. Periodically verify that you can restore from a backup into a clean database. ::: :: ## RBAC (Role-Based Access Control) Kubernetes RBAC controls *who* (users, groups, service accounts) can perform *what* actions (verbs like `get`, `list`, `create`, `delete`) on *which* resources. RBAC is the primary mechanism for enforcing least privilege inside the cluster. The core objects are: - **Role / ClusterRole** - a set of permissions. A `Role` is namespace-scoped; a `ClusterRole` applies cluster-wide. - **RoleBinding / ClusterRoleBinding** - grants a Role or ClusterRole to a subject (user, group or service account). ### Least-privilege principles - **Grant the minimum.** Give each user and service account only the permissions they actually need, scoped to the narrowest namespace and resource set that works. Avoid broad wildcards (`verbs: ["*"]`, `resources: ["*"]`) - **Never grant `cluster-admin` casually.** Reserve it for a small number of trusted administrators, day-to-day operations rarely need it - **Prefer namespaced `Role`s over `ClusterRole`s** unless a permission genuinely must span the whole cluster - **Audit regularly.** Review bindings periodically and remove access that is no longer needed. Command like `kubectl auth can-i --list` may help you inspect effective permissions ### Example: a read-only namespaced Role ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: flowfuse name: pod-reader rules: - apiGroups: [""] resources: ["pods", "pods/log"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: flowfuse subjects: - kind: User name: user@example.com apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io ``` This grants the user `user@example.com` read-only access to pods and their logs in the `flowfuse` namespace - and nothing else. ## Other High-Level Best Practices The following practices further reduce the attack surface of your cluster: - **Keep Kubernetes and node images patched.** Run a supported Kubernetes version and apply security updates to the control plane, nodes, and container images promptly - **Manage secrets properly.** Store credentials in Kubernetes Secrets (ideally with encryption at rest enabled, or an external secrets manager such as HashiCorp Vault or a cloud KMS). Never commit secrets to version control - **Apply Pod Security Standards.** Enforce the `restricted` [Pod Security Standard](https://kubernetes.io/docs/concepts/security/pod-security-standards/){rel=""nofollow""} where possible: run containers as non-root, drop unnecessary Linux capabilities, use a read-only root filesystem, and disallow privilege escalation - **Set resource requests and limits.** CPU and memory limits prevent a single workload — such as a runaway flow — from starving others and provide a defence against resource-exhaustion denial-of-service - **Enable audit logging.** Kubernetes audit logs record who did what and when, which is essential for detecting and investigating incidents - **Limit access to the cluster API and nodes.** Restrict the API server to trusted networks, avoid exposing the Kubelet, and disable SSH access to nodes where you can - **Scan images for vulnerabilities.** Use image scanning in your CI pipeline and admission control to block images with known critical vulnerabilities - **Use namespaces for isolation.** Separating workloads into namespaces makes RBAC and Network Policies easier to reason about and enforce # Administering FlowFuse ## Getting started - [Understanding the FlowFuse Architecture](https://flowfuse.com/docs/contribute/architecture) - [Install/Upgrade](https://flowfuse.com/docs/install) - requirements, deployment models, installation methods and upgrading - [`flowforge.yml` configuration](https://flowfuse.com/docs/install/configuration) - base platform configuration, done before you run. - [First Run Setup](https://flowfuse.com/docs/install/first-run) - create your admin user - [FlowFuse Concepts](https://flowfuse.com/docs/user/concepts) - [Usage Telemetry](https://flowfuse.com/docs/admin/telemetry) - [Single-Sign On](https://flowfuse.com/docs/admin/sso/) - [Licensing](https://flowfuse.com/pricing/){rel=""nofollow""} - [User management](https://flowfuse.com/docs/admin/user-management) - [Platform Monitoring](https://flowfuse.com/docs/admin/monitoring) - [Soft Launch Enablement](https://flowfuse.com/docs/admin/feature-flags) ## Administering FlowFuse ### Accessing the Admin Settings The Admin Settings can be accessed from the main menu: ![](https://flowfuse.com/docs/admin/images/admin-menu-option.png){width="300"} ### Admin Settings The Admin Settings view lets you manage the platform and its users. The following settings are available: - **Allow new users to register on the login screen** (default: `false`) :br With this option enabled, the platform login page allows visitors to register with the platform. :br This option is only available if email sending has been enabled. - **Create a personal team for users when they register** (default: `false`) :br With this option enabled, the platform will automatically create a Team for the user. This allows them to start creating Node-RED instances straight away. :br By default, this doesn't happen, which means the user must either manually create the Team (if that option is enabled), or be invited to an existing Team. :br When enabled, a choice of what type of team should be created for the user is shown. - **Allow users to reset their password on the login screen** (default: \`false) :br With this option enabled, a 'forgot your password' link is shown on the login screen, and provides a workflow where a user can reset their password via a link emailed to them. :br This option is only available if email sending has been enabled. - **Allow users to create teams** (default: `false`) :br This option allows users to create new Teams on the platform. By default, it is not enabled which means all Teams must be created by an Admin. - **Allow users to invite external users to teams** (default: `false`) :br This option allows users to invite people to join a Team who are not currently registered users of the platform. It sends an email with an invitation to sign-up to the platform and join the Team. :br By default, this is not enabled - users must be added by an Admin. :br This option is only available if email sending has been enabled. ### Managing Users The Users page of Admin Settings can be used to manage the user on the platform. It can be used to: - Add new users to the platform. :br With the 0.1.0 release, the admin sets the new user's password and it is left to the admin to share the login details with the user outside of the platform. - Edit a user's details. :br This includes making them an admin - giving them full access to the platform. It also provides a list of all pending user invitations, showing who invited whom to which team. ### Managing Teams With the 0.1.0 release, the Teams page just lists the teams on the platform. Further team management options will come in later releases. ### Managing Team Types The Team Types page can be used to manage the Team Types on the platform. They determine what features of the platform are available to teams of a given type, including what Instance Types are available and any limits that should be applied. #### Team Type Features - **Team Library**:br Enables Team Level sharing of flow fragments and examples. More details [here](https://flowfuse.com/docs/user/shared-library) - **Project Nodes**:br Enables the FlowFuse Project Nodes which allows Instances within a Team to pass messages between them. More details [here](https://flowfuse.com/docs/user/projectnodes) - **Custom NPM Catalogs**:br Allows .npmrc entries to be passed to Hosted and Remote Instances and also custom Node-RED Catalog URLs to be injected. This allows Node-RED nodes to be loaded from 3rd party registries (e.g. npm proxy instances). More details [here](https://flowfuse.com/docs/user/instance-settings#palette) - **NPM Packages**:br Allows a Team to upload and then access their own private Node-RED nodes (or other NodeJS module packages). This can include Subflows packaged using the FlowFuse Subflow Packaging Sidebar (Requires Team Library Feature to be enabled). More details [here](https://flowfuse.com/docs/user/custom-npm-packages) - **Certified Nodes**:br Allows the Team access to [FlowFuse Certified nodes](https://flowfuse.com/certified-nodes/){rel=""nofollow""} (Requires security token from FlowFuse, only available to Enterprise license holders) - **FlowFuse Exclusive Nodes**:br Allows the Team access to [FlowFuse Exclusive nodes](https://flowfuse.com/node-red/flowfuse/){rel=""nofollow""} (Requires security token from FlowFuse, only available to Enterprise license holders) - **Email Alerts**:br Allows Teams to enable email alerts when an Hosted Instance crashes or is approaching the CPU/Memory limits set for it's Stack (requires email to be enabled). More details [here](https://flowfuse.com/docs/user/instance-settings#alerts) - **Protected Instances**:br Allows Hosted Instances to be marked as Protected. This disables Editor access and requires all flows to be updated via a Pipeline. More details [here](https://flowfuse.com/docs/user/devops-pipelines#protected-instances) - **Git Integration**:br This allows a GitHub project to be used as the source or output of a Pipeline. More details [here](https://flowfuse.com/docs/user/devops-pipelines#git-repository-stage) - **API/Debug Length Limits**:br Lets a Team change the size of the max payload a Hosted Instance can accept and also the size messages sent to the Debug Sidebar will be truncated to. More details [here](https://flowfuse.com/docs/user/instance-settings#editor) - **Static Assets**:br Allows Hosted Instances to share static files stored in the Instance Persistent Storage space via HTTP. More details [here](https://flowfuse.com/docs/user/static-asset-service) - **Instance Resources**:br Enables CPU and Memory historical charts for Hosted and Remote Instances. - **Application Level RBAC**:br Allows Team level Roles to be applied at an Application level inside the Team. - **Team-based Endpoint Security**:br Allows HTTP endpoints provided by a Hosted or Remote Instance to require a FlowFuse account and membership to the allow access. Also applies to Node-RED Dashboards hosted on the instances. More details [here](https://flowfuse.com/docs/user/http-access-tokens) - **Device Groups**:br Allows Remote Instances to be placed in groups which can be used as the source or target of a Pipeline stage. More details [here](https://flowfuse.com/docs/user/device-groups) - **Device Auto Snapshot**:br Creates a snapshot on every deploy when the Remote Instance is in Developer Mode. These snapshots keep a rolling set of last 10 deploys. More details [here](https://flowfuse.com/docs/user/snapshots) - **Instance Auto Snapshot**:br Creates a snapshot on every deploy from a Hosted Instance. These snapshots keep a rolling set of the last 10 deploys. More details [here](https://flowfuse.com/docs/user/snapshots) - **Custom Hostnames**:br Allows Hosted instances to be accessed on a second hostname with an arbitrary domain (Kubernetes only, requires specific configuration). More details [here](https://flowfuse.com/docs/user/custom-hostnames) - **High Availability**:br Allows for 2 copies of Node-RED to be running for a single Hosted Node-RED instance to provide Load Balanced higher throughput and fail over protection (Kubernetes only). More details [here](https://flowfuse.com/docs/user/high-availability) - **Bill of Materials / Dependencies**:br Provides a Team level view of what Node-RED nodes are installed and what versions are being used and which have newer versions available. More details [here](https://flowfuse.com/docs/user/bill-of-materials) - **Version History Timeline**:br Enables a graphical representation of Snapshot creation to allow a clearer picture of when they are created. More details [here](https://flowfuse.com/docs/user/snapshots#timeline-view) - **Team Broker**:br Provides a Team scoped MQTT broker and Asynchronous API documentation generation. More details [here](https://flowfuse.com/docs/user/teambroker) - **Tables**:br Provides a Team scoped shared SQL Relational Database (Requires specific configuration). More details [here](https://flowfuse.com/docs/user/ff-tables) - **AI Features**:br Global toggle for all AI functionality within the team. When disabled, all AI features below are unavailable regardless of their individual settings. Requires `ai.enabled: true` in the platform configuration. - **Expert Assistant**:br Enables the FlowFuse Expert chat assistant for the team. Provides AI-powered support for building and debugging Node-RED flows. Enabled by default (opt-out). Requires the AI Features flag to be enabled. More details [here](https://flowfuse.com/docs/user/expert/) - **Expert Insights**:br Enables the Insights mode of FlowFuse Expert, allowing users to query live operational data from Node-RED instances via MCP. Enabled by default (opt-out). Requires the AI Features flag to be enabled. More details [here](https://flowfuse.com/docs/user/expert/chat/) - **Assistant Inline Code Completion**:br Allows LLM assistance when writing Function nodes. Requires the AI Features flag to be enabled and assistant service configuration. More details [here](https://flowfuse.com/docs/user/expert/node-red-embedded-ai/) - **Generated Snapshot Descriptions**:br Enables AI-generated descriptions of Snapshots. Requires the AI Features flag to be enabled and assistant service configuration. More details [here](https://flowfuse.com/docs/user/expert/node-red-embedded-ai/) ### Managing Instance Types The Instance Types page can be used to manage the Instance Types on the platform. When billing is enabled, an instance type can be associated with a particular Stripe Product/Price - allowing each type to have a different monthly price associated with it. The Instance Types page shows what types are currently active, how many stacks each type has been assigned to it, and how many instances have been created of that type. Whenever a new Instance Type is created, it will need to be manually enabled for the individual [Team Types](https://flowfuse.com/#managing-team-types) before they will be available for teams to use. Instance Types also control the default Stack (and in turn, the default Node-RED versions) that Instances will run with if there are multiple available Stacks associated with an Instance Type. ### Managing Stacks > Admin Settings > Stacks The Stacks page can be used to manage the Stacks on the platform. It can be used to create and edit the stacks on the platform. For Deployment specific information about working with stacks, refer to the documentation of your chosen deployment model: - [Local Stacks](https://flowfuse.com/docs/contribute/local/stacks) - [Docker Stacks](https://flowfuse.com/docs/install/docker/stacks) - [Kubernetes Stacks](https://flowfuse.com/docs/install/kubernetes/stacks) #### Create Stack ##### Upgrading Stacks You can create a new version of an existing stack via the drop-down menu in the stack table. This allows the platform to notify users that an update is available for their instance, allowing them to upgrade the stack at their convenience. ![Screenshot of FlowFuse showing where admins can create new stacks](https://flowfuse.com/docs/admin/images/admin-stacks-create-new-version.png){dataZoomable=""}*Screenshot of FlowFuse showing where admins can create new stacks* Node-RED instances that use the old stack will offer the new stack as a one-click upgrade option. ##### Create New Stack Alternatively, click 'Create stack' to create an entirely new stack. ![Screenshot of FlowFuse showing the "Create Stack" dialog](https://flowfuse.com/docs/admin/images/admin-stack-create.png){dataZoomable=""}*Screenshot of FlowFuse showing the "Create Stack" dialog* When prompted for the Node-RED version, the value here depends on the setup you're running: - **Local**: Provide the exact stack name (Node-RED version) that was installed. For example, if you ran the script with `latest` and it resulted in `3.1.9` being installed, you should enter `3.1.9`. This must match the directory name created in your `stacks` directory. If you changed the directory name for any reason, make sure to use that name. - **Docker**: Support the container image name ([docs](https://flowfuse.com/docs/install/docker/stacks)) - **k8s**: Support the container image name ([docs](https://flowfuse.com/docs/install/kubernetes/stacks)) #### Updating Stacks It is *not* possible to edit a stack that is being used by Instances. ### Managing Templates With [templates](https://flowfuse.com/docs/user/concepts/#template) administrators can apply Node-RED configuration options as default. For these options, the administrator can lock the selected value so users cannot change them, or keep them editable by end-users. If you edit a template that is being used by an Application Instance, those changes will get applied the next time the instance is restarted. #### Disallow using nodes On FlowFuse Cloud, but recommended on self-managed installs on Docker and Kubernetes, where certain nodes will not work so are excluded from being used by the template. When adding `31-tcpin.js,32-udp.js,10-file.js,23-watch.js,90-exec.js` to the `Exclude node by filename` section of a template and locking this value users are prevented from using these. # Platform Monitoring FlowFuse provides an API end-point that can be used to monitor statistical information about the platform. The end-point is accessible to any logged-in Admin user at the url (replace `example.com` with the domain of your FlowFuse instance) - `https://example.com/api/v1/admin/stats` By default, it returns a JSON object containing key statistics such as the number of users, instances, and other information. If the `accept` header of the http request includes `application/openmetrics-text` then the response is formatted as OpenMetrics text. This can be directly consumed by tools such as Prometheus. ## Enabling token-based access In Admin Settings there is an option to allow token-based access to platform statistics. When enabled, the platform will generate an access token that can be used to access the end-point without having a full Admin login. This is useful when configuring tools such as Prometheus to monitor the platform. 1. Under Admin Settings -> General, check the 'allow token-based access' option 2. A dialog is shown containing the token. This is the *only* time the token will be shown - make sure you record its value. The token should be provided as a bearer token in any http request to the end-point; ```bash TOKEN=your_generated_token curl -H 'Authorization: Bearer $TOKEN' https://example.com/api/v1/admin/stats ``` ## Configure Prometheus to scrape the Platform statistics endpoint To configure Prometheus to scrape the FlowFuse Platform statistics endpoint, you need to add a new scrape job to your Prometheus configuration file (usually `prometheus.yml`). Since the statistics endpoint is secured, you will need to provide the access token in the scrape configuration (see the previous paragraph on enabling token-based access). Once you have the token, you can add a new scrape job to your Prometheus configuration (replace `` and `` with yor FlowFuse Platform-sepcific values): ```yaml scrape_configs: - job_name: flowfuse_platform_stats metrics_path: /api/v1/admin/stats scheme: https authorization: type: Bearer credentials: "" scrape_protocols: ["OpenMetricsText1.0.0"] static_configs: - targets: [""] ``` # Observability Observability is the ability to understand the internal state and behaviour of a system by analysing its outputs, without needing to know its internal workings. For a self-hosted FlowFuse platform running on Kubernetes, this means having a holistic view of the platform and the cluster it runs on - their health, performance, and any potential issues - so you can detect and diagnose problems before they affect your users. This page describes the kinds of tools you can use and what each is for, so you can plan an observability stack that fits your environment. It does not prescribe a specific deployment; the tools named below are common open-source choices, but the concepts apply equally to managed or commercial alternatives. ## The three pillars Observability is usually described in terms of three complementary signals: - **Metrics** - numeric measurements sampled over time (request rates, memory usage, pod counts). Good for dashboards, trends, and alerting thresholds. - **Logs** - timestamped records of discrete events emitted by the platform and by the workloads running on the cluster. Good for understanding *why* something happened. - **Dashboards & visualization** - a unified place to explore metrics and logs together, spot correlations, and share views across a team. A complete setup collects all three and ties them together, so an alert on a metric can be investigated against the corresponding logs. ## Tools ### Prometheus [Prometheus](https://prometheus.io/){rel=""nofollow""} collects and stores time-series metrics by scraping HTTP endpoints at regular intervals. For a self-hosted FlowFuse platform there are two useful sources of metrics: - **Kubernetes cluster metrics** - cluster-level metrics such as CPU and memory usage, pod status, and node health, typically gathered via [kube-state-metrics](https://github.com/kubernetes/kube-state-metrics){rel=""nofollow""} and [node-exporter](https://github.com/prometheus/node_exporter){rel=""nofollow""}. - **Hosted Instances metrics** - every Hosted Instance is a pod running on the Kubernetes cluster. It produces metrics that are exposed by cAdvisor at the `/metrics` endpoint, which Prometheus can scrape. This includes CPU, memory and networking usage. - **Platform metrics** - FlowFuse core deployment exposes a `/metrics` endpoint that Prometheus can scrape. This includes resources performance statistics, request rates, and error counts, etc. ### Loki [Loki](https://grafana.com/oss/loki/){rel=""nofollow""} is a log aggregation system designed to pair with Prometheus. It collects, stores, and lets you query logs from the platform and from the pods running on your cluster, so you can analyse logs alongside metrics using the same labels. ### Grafana [Grafana](https://grafana.com/oss/grafana/){rel=""nofollow""} is an open-source platform for building and sharing dashboards. It provides a single interface over your observability data: - **Data source integration** - connect Prometheus, Loki, and other sources to visualize metrics and logs in one place. - **Customizable dashboards** - build tailored views for platform health, resource usage, and workload behaviour. - **Alerting** - define alerting rules against your metrics and logs to be notified of problems proactively. ## Quick start The fastest way to get started is the **LGTM stack** - Loki (logs), Grafana (dashboards), Tempo (traces), and Mimir/Prometheus (metrics) - is to either use Grafana Cloud or deploy it self-hosted: - **Grafana Cloud (hosted)** - sign up for [Grafana Cloud](https://grafana.com/products/cloud/){rel=""nofollow""} and install the [Kubernetes Monitoring](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/){rel=""nofollow""} integration. It deploys collectors (Grafana Alloy) into your cluster that ship metrics and logs to Grafana's managed backend. Point one scrape target at the FlowFuse Platform metrics endpoint and you have platform plus cluster visibility with minimal setup. - **Self-hosted LGTM** - run the stack inside your own cluster using the [`kube-prometheus-stack`](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack){rel=""nofollow""} (Prometheus + Grafana + Alertmanager) and [`loki`](https://github.com/grafana/loki/tree/main/production/helm/loki){rel=""nofollow""} Helm charts. You keep full control of your data and retention, at the cost of maintaining the stack yourself. Start with Grafana Cloud if you want results quickly and are comfortable sending telemetry off-cluster; choose self-hosted LGTM if data residency or cost at scale matters. The concepts in the rest of this page apply to either. ### Initial Grafana dashboards Rather than building Grafana dashboards from scratch, import a few proven community dashboards to get cluster visibility on day one. In Grafana, use **Dashboards → New → Import** and enter the dashboard ID: - **[Node Exporter Full](https://grafana.com/grafana/dashboards/1860){rel=""nofollow""}** (ID `1860`) - per-node CPU, memory, disk, and network from node-exporter. - **[Kubernetes / Views / Global](https://grafana.com/grafana/dashboards/15757){rel=""nofollow""}** (ID `15757`) - cluster-wide health and resource usage across nodes, namespaces, and workloads. - **[Kubernetes / Views / Pods](https://grafana.com/grafana/dashboards/15760){rel=""nofollow""}** (ID `15760`) - per-pod CPU, memory, and network, useful for inspecting individual Hosted Instances. > If you deployed the `kube-prometheus-stack` Helm chart, a full set of `Kubernetes / Compute Resources / *` dashboards is installed automatically - check the dashboard list before importing duplicates. Once the cluster basics are in place, build a FlowFuse-specific dashboard from the platform metrics described under [Prometheus](https://flowfuse.com/#prometheus) above. ## Next steps - [Platform Monitoring](https://flowfuse.com/docs/admin/monitoring) - details of the FlowFuse statistics endpoint used by Prometheus. - [Installing FlowFuse on Kubernetes](https://flowfuse.com/docs/install/kubernetes/) - deploying and configuring the platform on a cluster. # Configuring Single Sign-On *This feature is only available on self-hosted Enterprise licensed instances of FlowFuse.* FlowFuse allows users to sign in through their SAML identity provider, such as Google Workspace, or using LDAP against a directory service provider. The platform can be configured with multiple SSO configurations and uses the user's email domain to identify which provider should be used. Since FlowFuse 2.7, the SSO configuration can be configured to automatically registers when they first sign in. Otherwise, a user will have to first register on the platform, providing a temporary password in order to create an account. They will then be able to log in via their SSO provider. By default, admin users can still log in with their original FlowFuse username and password, which ensures they are not locked out if there is a problem with the SSO configuration. ## SAML SSO SAML based SSO allows the FlowFuse platform to authenticate users against their identity provider such as Google Workspace. Once enabled for a particular email domain, regular users on that domain will be directed to the Identity Provider in order to log in. They will no longer be able to log in with their local password, nor will they be able to change their email address in User Settings. - [Configuring SAML SSO](https://flowfuse.com/docs/admin/sso/saml) ## LDAP SSO LDAP based SSO allows the FlowFuse platform to authenticate users against a directory service provider, such as OpenLDAP. When logging in, the users credentials are passed to the service provider to verify. - [Configuring LDAP SSO](https://flowfuse.com/docs/admin/sso/ldap) # Configuring LDAP based Single Sign-On *This feature is only available on self-hosted Enterprise licensed instances of FlowFuse.* The SSO Configurations are managed by the platform Administrator under the `Admin Settings > Settings > SSO` section. The user must already exist on the FlowFuse platform before they can sign in via SSO. ### Create a SSO Configuration 1. Click 'Create SSO Configuration' to create a new config :br![](https://flowfuse.com/docs/admin/sso/images/create-sso-config-ldap.png) 2. Give the configuration a name to help identify it, and provide the email domain name this configuration should apply to. Ensure the LDAP option is selected - this cannot be changed after the configuration is created. 3. Click 'Create configuration' :br At this point, the configuration has been created and metadata generated for the configuration, but it is not active. ### Configuration LDAP The following fields are provided for configuring LDAP. You will need to refer to your LDAP service provider details for the correct values to enter: ![](https://flowfuse.com/docs/admin/sso/images/edit-sso-config-ldap.png) - `Server` - the address of the LDAP server, including port number. - `Username` - the bind DN to use to connect to the server. The user must have permission to lookup users in the directory. - `Password` - the password to connect to the server with. - `Base DN` - the base object under which user searches are performed. - `User Search Filter` - the filter used to search for a user. See below for more details. - `Enable TLS`- whether to use TLS for the LDAP connection - `Verify Server Certificate` - when TLS is enabled, whether to perform strict certification validation. You can save the configuration at any time by clicking the `Update configuration` button. The configuration will only be enabled when you tick the `active` checkbox and save the changes. ### Session Length Overrides Each SSO configuration can override the platform default Max Session life an Max Session Idle time. These are Controlled by the "Custom Session Expiry (hours)" and "Custom Session Idle Time (hours)" respectively. ![Settings for Custom Session lifetime](https://flowfuse.com/docs/admin/sso/images/edit-sso-custom-session.png)*Settings for Custom Session lifetime* #### User Search Filter The search filter is used when checking if a user exists within the directory, using the standard LDAP query notation. The default search filter is `(uid=${username})`. The platform will replace `${username}` and `${email}` with the user's details when they attempt to login. ## Creating new users With FlowFuse 2.7, the SSO Configuration now includes an option to automatically register users who sign in via the configuration. This option is not enabled by default, but can be enabled but selecting the `Allow Provisioning of New Users on first login` option in the SOO configuration. When creating the user, the platform will use information provided by the LDAP provider to create the username. The user will be directed to their settings page where they can modify their user details to their preferred values. ## Managing Team Membership with LDAP Groups LDAP implementations can also be used to group users To enable this option, select the `Manage roles using group assertions` in the SSO configuration. The following configuration options should then be set: - `Group DN` - this is the base DN to be used to search for group membership. - `Team Scope`- this determines what teams can be managed using this configuration. There are two options: - `Apply to all teams` - this will allow the SAML groups to manage all teams on the platform. This is suitable for a self-hosted installation of FlowFuse with a single SSO configuration for all users on the platform. - `Apply to selected teams` - this will restrict what teams can be managed to the provided list. This is suitable for shared-tenancy platforms with multiple SSO configurations for different groups of users, such as FlowFuse Cloud. When this option is selected, an additional option is available - `Allow users to be in other teams`. This will allow users who sign-in via this SSO configuration to be members of teams not in the list above. Their membership of those teams will not be managed by the SSO groups. If that option is disabled, then the user will be removed from any teams not in the list above. ### LDAP Groups configuration A user's team membership is managed by what groups they are in. When the user logs in, the LDAP provider will be queried for a list of groups they are a member of. This can be either as a `member` or `uniqueMember` of a `groupOfNames` or `groupOfUniqueNames` respectively. The group name is used to identify a team, using its slug property, and the user's role in the team. The name must take the form `ff--`. For example, the group `ff-development-owner` will container the owners of the team `development`. The valid roles for a user in a team are: - `owner` - `member` - `viewer` - `dashboard` *Note*: this uses the team slug property to identify the team. This has been chosen to simplify managing the groups in the LDAP Provider - rather than using the team's id. However, a team's slug can be changed by a team owner. Doing so will break the link between the group and the team membership - so should only be done with care. An optional prefix and suffix can be include in the group name to support LDAP providers that have existing naming policies. The SSO configuration can be configured with the lengths of these values so they will be stripped off before the group name is validated. For example, if an organisation requires all groups to begin with `acme-org-`, a prefix length of `9` can be set and the group `acme-org-ff-development-owner` will be handled as `ff-development-owner`. ### Application Level Groups In addition to being able to add Users to Teams using groups, Role overrides for specific Applications within those groups can also be controlled. The User must have a membership of the Team the Application belongs to for an Application override to take effect. If the User is not a member of that Team, the override is ignored. Groups for Application overrides use a similar pattern to Team Groups and use the same prefix and suffix length modifiers. They take the form `ff-[]-`, where `` can be the Application name or id and `` must be one of the valid roles listed above, but can also be `none` to remove access from an Application within the Team. Any unrecognised role is ignored. For example, given a Team called `development` and an Application called `test`, owner-level access to the Team would be granted by membership of a group named `ff-development-owner`, and an override to `viewer` for the Application `test` would be granted by `ff-development[test]-viewer`. If multiple groups grant different roles for the same Application, the highest role is applied. Application overrides applied this way are managed by the SSO provider: they cannot be edited in the FlowFuse UI, and if the corresponding group is removed the override is cleared the next time the User logs in. ## Managing Admin users The SSO Configuration can be configured to manage the admin users of the platform by enabling the `Manage Admin roles using group assertions` option. Once enabled, the name of a group can be provided that will be used to identify whether a user is an admin or not. \**Note:* the platform will refuse to remove the admin flag from a user if they are the only admin on the platform. It is *strongly* recommended to have an admin user on the system that is not managed via SSO to ensure continued access in case of any issues with the SSO provider. ## Providers The following is the node-exhaustive list of the providers that are known to work with FlowFuse LDAP SSO. - [OpenLDAP](https://www.openldap.org/){rel=""nofollow""} # Configuring SAML based Single Sign-On *This feature is only available on FlowFuse Cloud and self-hosted Enterprise licensed instances of FlowFuse.* The SSO Configurations are managed by the platform Administrator under the `Admin Settings > Settings > SSO` section. To fully configure SAML SSO, you will need to generate a configuration in FlowFuse, provide some of the generated values to your Identity Provider, and copy back some values they provide. ## Configuring SSO on FlowFuse Cloud Configuring SSO on FlowFuse Cloud requires co-ordinating tasks between the customer and FlowFuse Cloud administrators. **All changes must be made via a [Production Change Request](https://github.com/FlowFuse/CloudProject/issues/new?assignees=&labels=change-request&projects=&template=change-request.yml&title=Change%3A+){rel=""nofollow""} in the CloudProject repository - even if you are actioning it directly.** When a customer requests SSO to be setup for their users, we require the following information: 1. Confirm the customer's entitlement for SSO enablement. It is only available to *Enterprise* tier customers. 2. The email domain that will be covered by the configuration. Note that each SSO configuration can only be applied to a single domain. If a customer has multiple domains, each one will require its own SSO configuration. [Issue #5011](https://github.com/FlowFuse/flowfuse/issues/5011){rel=""nofollow""} has been raised to make this more flexible in the future. 3. Whether it is SAML or LDAP based SSO 4. What Identify Provider they are using for their SSO. Once this information has been provided, create a Change Request issue in the CloudProject repository recording this information. We can then create a draft SSO configuration in the Admin/Settings/SSO section. The configuration should not be marked as active yet. From the draft configuration, the values of `ACS URL` and `Entity ID / Issuer` can be given to the customer. These values will be required by their Identify Provider. Refer to the provider-specific documentation for how those values get applied. In return, the customer then needs to provide: 1. `Identity Provider Single Sign-On URL` 2. `Identity Provider Issuer ID / URL` 3. `X.509 Certificate Public Key` Again, refer to the provider-specific documentation for where to find these values as each provider has its own terminology. These values should be applied to the draft SSO configuration in FlowFuse Cloud. The configuration can then be marked as active. It is recommended to do this final step whilst on a call with the customer so they can test the setup in real time. ### Common Issues Aside from navigating the mismatched terminology between services providers, the most common issue we hit is where a login attempt fails and 'Invalid Document Signature' is shown in FlowFuse logs. This is because we expect both the SAML Assertions and Responses to be signed by the public certificate. The default configuration for many providers is to only sign the assertions - check the provider-specific documentation for the appropriate option to enable to address this. ## Configuring SSO The following instructions give more details information on how to setup SSO. ### Create a SSO Configuration 1. Click 'Create SSO Configuration' to create a new config :br![](https://flowfuse.com/docs/admin/sso/images/create-sso-config.png) 2. Give the configuration a name to help identify it, and provide the email domain name this configuration should apply to. Ensure the SAML option is selected - this cannot be changed after the configuration is created. 3. Click 'Create configuration' :br At this point, the configuration has been created and metadata generated for the configuration, but it is not active. :br![](https://flowfuse.com/docs/admin/sso/images/edit-sso-config.png) 4. Copy the `ACS URL` and `Entity ID / Issuer` values as you will need to configure your Identity Provider with these values. You can save the configuration at any time by clicking the `Update configuration` button. The configuration will only be enabled when you tick the `active` checkbox and save the changes. ### Configure your Identify Provider Every Identity Provider uses slightly different terminology and varies what information they require and what they provide. This can make it a tricky task to complete. We provide specific guides for the providers we have verified below. If you have a working configuration for a provider not listed here, please do share the details so we can add them to the list. The general points are: 1. Your Identity Provider will supply you with some of the following values that should be entered into your FlowFuse SAML SSO Configuration: - `Single Sign-On URL` - also referred to as 'SAML Endpoint', 'Login URL' or 'IdP SSO URL'. - `Issuer ID / URL` - `X.509 Certification Public Key` - the public key of a certificate used to sign SAML requests. 2. Configure the `NameID` SAML option to be `EmailAddress` and have it return the email of the user logging in. This is how FlowFuse will verify they are a known user on the platform. ### Session Length Overrides Each SSO configuration can override the platform default Max Session life an Max Session Idle time. These are Controlled by the "Custom Session Expiry (hours)" and "Custom Session Idle Time (hours)" respectively. ![Settings for Custom Session lifetime](https://flowfuse.com/docs/admin/sso/images/edit-sso-custom-session.png)*Settings for Custom Session lifetime* ### Enable your SAML SSO Configuration Once you have setup both sides of the configuration you can enable it for use by ticking the `active` checkbox and clicking `Update configuration`. ## Creating new users With FlowFuse 2.7, the SSO Configuration now includes an option to automatically register users who sign in via the configuration. This option is not enabled by default, but can be enabled but selecting the `Allow Provisioning of New Users on first login` option in the SOO configuration. When creating the user, the platform will use information provided by the SAML provider to create the username. The user will be directed to their settings page where they can modify their user details to their preferred values. \## Managing Team Membership with SAML Groups Some SAML providers allow user group information to be shared as part of the sign-in process. When properly configured, this can be used to manage what FlowFuse teams a user has access to. To enable this option, select the `Manage roles using group assertions` in the SSO configuration. The following configuration options should then be set: - `Group Assertion Name` - this is used to identify the group membership information in the response sent by the Identity Provider. It defaults to `ff-roles` but can be customised if the Identify Provider requires it. - `Team Scope`- this determines what teams can be managed using this configuration. There are two options: - `Apply to all teams` - this will allow the SAML groups to manage all teams on the platform. This is suitable for a self-hosted installation of FlowFuse with a single SSO configuration for all users on the platform. - `Apply to selected teams` - this will restrict what teams can be managed to the provided list. This is suitable for shared-tenancy platforms with multiple SSO configurations for different groups of users, such as FlowFuse Cloud. When this option is selected, an additional option is available - `Allow users to be in other teams`. This will allow users who sign-in via this SSO configuration to be members of teams not in the list above. Their membership of those teams will not be managed by the SSO groups. If that option is disabled, then the user will be removed from any teams not in the list above. ### SAML Groups configuration A user's team membership is managed by what groups they are in. When the user logs in, the SAML provider must be configured to provide a list of groups they are a member of as a SAML assertion. The group name is used to identify a team, using its slug property, and the user's role in the team. The name must take the form `ff--`. For example, the group `ff-development-owner` will container the owners of the team `development`. The valid roles for a user in a team are: - `owner` - `member` - `viewer` - `dashboard` *Note*: this uses the team slug property to identify the team. This has been chosen to simplify managing the groups in the SAML Provider - rather than using the team's id. However, a team's slug can be changed by a team owner. Doing so will break the link between the group and the team membership - so should only be done with care. An optional prefix and suffix can be include in the group name to support SAML providers that have existing naming policies. The SSO configuration can be configured with the lengths of these values so they will be stripped off before the group name is validated. For example, if an organisation requires all groups to begin with `acme-org-`, a prefix length of `9` can be set and the group `acme-org-ff-development-owner` will be handled as `ff-development-owner`. ### Application Level Groups In addition to being able to add Users to Teams using groups, Role overrides for specific Applications within those groups can also be controlled. The User must have a membership of the Team the Application belongs to for an Application override to take effect. If the User is not a member of that Team, the override is ignored. Groups for Application overrides use a similar pattern to Team Groups and use the same prefix and suffix length modifiers. They take the form `ff-[]-`, where `` can be the Application name or id and `` must be one of the valid roles listed above, but can also be `none` to remove access from an Application within the Team. Any unrecognised role is ignored. For example, given a Team called `development` and an Application called `test`, owner-level access to the Team would be granted by membership of a group named `ff-development-owner`, and an override to `viewer` for the Application `test` would be granted by `ff-development[test]-viewer`. If multiple groups grant different roles for the same Application, the highest role is applied. Application overrides applied this way are managed by the SSO provider: they cannot be edited in the FlowFuse UI, and if the corresponding group is removed the override is cleared the next time the User logs in. ## Managing Admin users The SSO Configuration can be configured to manage the admin users of the platform by enabling the `Manage Admin roles using group assertions` option. Once enabled, the name of a group can be provided that will be used to identify whether a user is an admin or not. \**Note:* the platform will refuse to remove the admin flag from a user if they are the only admin on the platform. It is *strongly* recommended to have an admin user on the system that is not managed via SSO to ensure continued access in case of any issues with the SSO provider. ## Direct SSO Login For Self Hosted users there is an option in the Admin Settings to enable buttons on the login page for each active SAML SSO provider. These buttons will redirect to the SSO provider rather than requiring users to enter and email address in the username field to select the correct provider. ## Forcing All Users to Use SSO For self-hosted installations that need to ensure no user can bypass SSO, there is an option in **Admin Settings > Settings > SSO > Force all logins for non-admin users via a single SAML SSO provider**. ![SSO settings page showing the option to force all non-admin users to log in via a single SAML SSO provider](https://flowfuse.com/docs/admin/sso/images/force-sso.png)*SSO settings page showing the option to force all non-admin users to log in via a single SAML SSO provider* When this option is enabled: - All users are redirected to the configured SSO provider at login, regardless of their email domain - The email and password login form is no longer presented as a fallback option - Admin users can bypass SSO by accessing `/admin` routes This is intended for organisations running a single identity provider across the entire platform, where per-domain SSO configuration is not sufficient to cover all users. ## Providers The following is a non-exhaustive list of the providers that are known to work with FlowFuse SAML SSO. - [Microsoft Entra](https://flowfuse.com/#microsoft-entra) - [Google Workspace](https://flowfuse.com/#google-workspace) - [OneLogin](https://flowfuse.com/#onelogin) - [Okta](https://flowfuse.com/#okta) - [Keycloak](https://flowfuse.com/#keycloak) ### Microsoft Entra Microsoft provide a guide for creating a custom SAML Application [here](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal){rel=""nofollow""}. The following tables map the Entra terminology to the FlowFuse settings. | FlowFuse Setting | Entra Setting | | -------------------------------------- | -------------------------------------------- | | `ACS URL` | `Reply URL (Assertion Consumer Service URL)` | | `Identity Provider Issuer ID / URL` | `Microsoft Entra Identifier` | | `Identity Provider Single Sign-On URL` | `Login URL` | | `X.509 Certificate Public Key` | `Certificate (Base64)` | Follow these steps to properly configure SAML SSO for Microsoft Entra: 1. In FlowFuse: 1. Create a draft SSO configuration in FlowFuse with the appropriate email domain 2. In Entra: 1. Create a SAML application - use the guide linked above for more information 2. Copy the following values from the FlowFuse SSO configuration into the corresponding Entra configuration: 1. Set `Reply URL` to the value of `ACS URL` 2. Set `Identifier (Entity ID)` to the value of `Entity ID/Issuer` 3. Within the `SAML Signing Certificate` configuration, the `Signing Option` must be set to `Sign SAML response and assertion`. 4. The `Unique User Identifier (Name ID)` claim must be configured to return the value of the `user.mail` source attribute. 5. Download the `Federation Metadata XML` file from the `SAML Certificates` section of the Entra application. 3. In FlowFuse: 1. From the metadata XML file, copy the follow properties into the FlowFuse SSO configuration: 1. Set `Identity Provider Single Sign-On URL` to the value of the `Location` attribute of the `` tag. This should look like `https://login.microsoftonline.com//saml2`. 2. Set `Identity Provider Issuer ID / URL` to the value of the `entityID` attribute of the `` tag. This should look like `https://sts.windows.net//` 3. Set `X.509 Certificate Public Key` to the value of the `` tag. This does *not* need to have the `-----BEGIN CERTIFICATE-----/-----END CERTIFICATE-----` wrapper. #### Group Membership Configuration By default, when enabled, Entra will share group assertions under the name `http://schemas.microsoft.com/ws/2008/06/identity/claims/groups` and provides the groups as a list of object ids. Either the `Group Assertion Name` should be set to this name, or Entra configured to use a custom assertion name that matches the FlowFuse SSO Configuration value. Entra must also be configured to return group names rather than object ids. ### Google Workspace Google provide a guide for creating a custom SAML Application [here](https://support.google.com/a/answer/6087519?hl=en){rel=""nofollow""}. Google Workspace only supports HTTPS-based SSO URLs. You cannot use it when developing locally using `http://localhost:3000`. The following table maps the Google Workspace terminology to the FlowFuse settings. | FlowFuse Setting | Google Workspace Setting | | -------------------------------------- | ------------------------ | | `Identity Provider Single Sign-On URL` | `SSO URL` | | `Identity Provider Issuer ID / URL` | `Entity ID` | | `X.509 Certificate Public Key` | `Certificate` | Within the `Service provider details` configuration, the `Signed response` option must be enabled. ### OneLogin Follow [this guide](https://onelogin.service-now.com/support?id=kb_article&sys_id=93f95543db109700d5505eea4b96198f){rel=""nofollow""} to create a `OneLogin SAML Test Connector`. | FlowFuse Setting | OneLogin Setting | | -------------------------------------- | -------------------------- | | `Identity Provider Single Sign-On URL` | `SAML 2.0 Endpoint (HTTP)` | | `Identity Provider Issuer ID / URL` | `Issuer URL` | | `X.509 Certificate Public Key` | `X.509 Certificate` | ### Okta Within your Okta Admin dashboard, browse the App Integration catalog and add a new instance of the `SAML Service Provider` integration. On the Sign-On Options section, ensure SAML 2.0 is selected. Below that section you will see a notice saying: > SAML 2.0 in not configured until you complete the setup instructions. Click the 'View setup instructions' button to open the page in a new window. Follow the instructions on that - copying the `Identity Provider Issuer`, `Identity Provider HTTP POST URL` and `Identity Provider Certificate` values into the FlowFuse SSO configuration. Back on the Okta SAML Application configuration page, under the `Advanced Sign-on Settings` section enter the `Assertion Consumer Service URL` and `Service Provider Entity Id` from the FlowFuse SSO configuration. Under `Credential Details` section, change the Application username format to `Email`. #### Group Membership Configuration To configure Okta to return Group assertions, edit the Settings of the SAML Service Provider's SAML 2.0 configuration. Expand the 'Attributes' section and add a `Group Attribute Statement`. The name must match the `Group Assertion Name` in the FlowFuse SSO configuration (default: `ff-roles`). You can optionally add a filter of `ff-` so that it only returns groups used by FlowFuse. ### Keycloak Within your Keycloak Admin Console, create a new Client with the following settings: Under the General Settings: - Set `Client type` to `SAML` - Set the `Client ID` to the `Entity ID / Issuer` value from the FlowFuse SSO configuration. Under the Login Settings: - Set `Valid redirect URIs` to the `ACS URL` value from the FlowFuse SSO configuration. Once created and you are shown the full client configuration, make the following additional changes: - Set `Name ID format` to `email` - Under the 'Keys' tab, turn off `Client signature required` Save the changes. Next, select the 'Download adapter config' option under the 'Action' dropdown menu. Select the 'Mod Auth Mellon files' format and click Download. This will download a zip file. Extract the zip and open the `idp-metadata.xml` file in a text editor. The final task is to copy some of the contents of the XML file into the FlowFuse SSO configuration. - Copy the value of the `entityID` attribute into the `Identity Provider Issuer ID / URL` property - Find one of the `md:SingleSignOnService` tags and copy the value of its `Location` attribute into the `Identity Provider Single Sign-On URL` property - Copy the contents of the `ds:X509Certificate` tag into the `X.509 Certificate Public Key` property #### Group Membership Configuration In Keycloak and the Realm setup with FlowFuse as a client: - Create a new "Client Scope" - Give it a name and ensure the "Protocol" is `SAML` - After saving the scope, select the "Mappers" tab - "Add mapper" and pick "By configuration" - Select "Group list" from the options - Give it a name and set "Group attribute name" to `ff-roles` (this must match the value configured in FlowFuse, default 'ff-roles') - Enable 'Single Group Attribute' - Ensure that "Full group path" is unchecked - Save and return to the "Clients" list and select your FlowFuse Client created earlier - Under "Client scopes", use the "Add client scope" button to add the new scope # Usage Telemetry The platform shares anonymous usage information with us at FlowFuse. This helps us understand how the platform is being used, what areas need improvement and how we should prioritise future work. Ultimately, it helps us produce a better platform for all its users. We do not collect: - Any personally identifiable information. We do store a secure hash of the sending IP address - but the plain value is never stored - Any specific details of the flows running on the platform. ## Core Telemetry ### Configuring Telemetry By default, usage telemetry is enabled on the platform. The administrator can opt-out of sharing information as part of the initial setup, or through the Admin Settings section of the platform UI. It is also possible to disable in the `flowforge.yml` configuration file. This overrides whatever option is set in the Admin Settings UI. **IMPORTANT: Licensed installations cannot disable telemetry** ```yaml telemetry: enabled: false ``` ### Collected Data The following pieces of information are included in the telemetry sent back to us: ```json { "instanceId": "5db51f99-c6fb-4340-9c19-78adce58cc1b", "os": { "type": "Darwin", "release": "20.5.0", "arch": "x64" }, "env": { "nodejs": "v16.19.1", "flowforge": "1.0.0" }, "platform": { "counts": { "users": 6, "teams": 5, "projects": 4, "devices": 9, "projectSnapshots": 16, "projectTemplates": 7, "projectStacks": 2, "libraryEntries": 0, "sharedLibraryEntries": 2 }, "config": { "driver": "localfs", "broker": { "enabled": true }, "fileStore": { "enabled": true }, "email": { "enabled": false } }, "license": { "id": "4c105579-782b-4d53-af62-cf7fa69f6b43", "type": "DEV" } } } ``` | Property | Description | | ----------------------------------- | -------------------------------------------------------------------------------------------- | | `instanceId` | A unique identifier for the FlowFuse instance. | | `os` | Information about the operating system | | `env` | Node.js and FlowFuse versions | | `platform.counts` | A snapshot of the number of users, teams, projects (instances), etc, in use on the platform. | | `platform.config.driver` | Which backend driver is being used | | `platform.config.broker.enabled` | A flag indicating whether the the internal comms broker is enabled | | `platform.config.fileStore.enabled` | A flag indicating whether the file store is enabled | | `platform.config.email.enabled` | A flag indicating whether email is enabled | | `platform.license.id` | The ID of the license loaded | | `platform.license.type` | The type of the license | When the data is collected, we also store the timestamp the data was received and a **hash** of the sending IP address - we do not store the plain value. ### Schedule For the core tracking, the platform will send the telemetry data: - 30 seconds after the platform starts up (but only if the platform has already been initialised) - Once every 24 hours at a time randomly picked when the platform starts The data is sent via an HTTP Post to `https://ping.flowforge.com`. ## Frontend Telemetry The FlowFuse UI can be configured to track usage to help understand how users are navigating the pages. It supports integration with two different services: - [PostHog](https://posthog.com/){rel=""nofollow""} *(recommended)*: You will require your own API key to pass into the `yml`, which will begin the logging of user interactions. - [Sentry](https://sentry.io/){rel=""nofollow""} *(recommended)*: You will need to specify your Sentry DSN for the frontend and back-end - [Plausible](https://plausible.io/){rel=""nofollow""}: *(deprecated since 0.9 and will be removed in the future)*: You can setup your own account, and pass the relevant domain to the `yml` in the telemetry configuration. As this option is deprecated, details of how to configure are no longer provided. ### Configuring Telemetry | Option | Description | | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `telemetry.enabled` | Enables the anonymous usage telemetry of the platform. Default: `true` | | `telemetry.backend.sentry.dsn` | The API key provided to you from your own sentry account. Default: `null` | | `telemetry.frontend.posthog.apikey` | The API key provided to you from your own PostHog account. Default: `null` | | `telemetry.frontend.posthog.capture_pageview` | FlowFuse is designed as to provide custom posthog `$pageview` events that provide more detail on navigation than the default, and suit a single page application better. As such, we recommend setting this to false in order to prevent duplicate `pageleave`/`pageview` events firing. Default: `true` | | `telemetry.frontend.sentry.dsn` | The API key provided to you from your own sentry account. Default: `null` | | `telemetry.frontend.sentry.production_mode` | Should this instance be treated as production (lower session count recorded). Default: `false` | ```yaml telemetry: enabled: true frontend: posthog: apikey: capture_pageview: false sentry: dsn: production_mode: true backend: sentry: dsn: prometheus: enabled: true ``` Sentry reads the environment variable `SENTRY_ENV`, falling back to `NODE_ENV` to set the environment for both frontend and backend. #### Telemetry During Build Configure .env with the auth token, org, and project name for the frontend project. ```yaml # Used for BUILD time sentry reporting SENTRY_AUTH_TOKEN= SENTRY_ORG= SENTRY_PROJECT= ``` # User management ## User registration Depending on where FlowFuse is installed, users should or should not be allowed to sign up for the service. User registration can be configured in the admin panel. Go to "Admin Settings" > "Settings" and select or deselect "Allow new users to register on the login screen". ## Creating new users To add new users to the platform go to "Admin Settings" > "Users" and click the "New User" button. Fill out the form, and provide the new user with their password. To require users to change their password the next time they log in, use the "Expire Password" option on the "Edit User" dialog. ## Deleting a user Users can only be removed if they are not the sole owners of any teams. As such they must either delete their teams first or ensure their teams have alternative owners. They can either do this themselves or an Admin do it for them. They can then be removed via the "Edit User" dialog in the Admin view. # FlowFuse Platform API The platform provides a REST API that makes it possible to create integrations and custom workflows. The API comes with an OpenAPI 3.0 Specification that can be viewed [here](https://app.flowfuse.com/api/){rel=""nofollow""}, or on any FlowFuse instance on the path `/api/`. ### Accessing the API To make use of the API you will need a valid Access Token. Tokens can be generated for a user under the Security section of the User Settings page. ![Tokens Settings Page](https://flowfuse.com/docs/api/images/tokens.png) Tokens can be set to have a limited life or unlimited and can be revoked by deleting the token from the list. Tokens with an expiry date will be deleted once they reach that date. Be aware that the token value will only be displayed once, at creation time, there is no way to recover the token after this point. Currently, all routes require a valid token to be included in the request. The tokens are passed using the `Authorization` header as a `Bearer` token. For example, the following will get a list of the token owner's teams: ```text curl -H "Authorization: Bearer ffpat_d4vZlLhCN8muyFUi6UsquLj47H2aTDkDpvxBUf5Ea" \ https://app.flowforge.com/api/v1/user/teams ``` When sending data to the API, requests must set the `Content-Type` header to `application/json`. For example, the following will update the name of the team with an id of `mNYLkklLAG`: ```text curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ffpat_d4vZlLhCN8muyFUi6UsquLj47H2aTDkDpvxBUf5Ea" \ -d '{"name": "My Development Team"}' \ https://app.flowforge.com/api/v1/teams/mNYLkklLAG ``` # Billing ## Payment Methods We will accept payments via credit or debit card only using Stripe as our payment provider. All payments are processed in US Dollars. ## Team Billing Each team has its own billing subscription that includes charges for the Node-RED instances and Devices owned by the team. ## Billing Cycle If you've signed up to FlowFuse Cloud without a yearly contract agreed upon with our sales team, your FlowFuse teams are billed monthly on the anniversary of the team creation. You will receive one bill for each team. Hosted and Remote Instances which are added beyond the included amount are pro-rated charges on the current billing cycle and invoiced at the end of the cycle. ## Removing Instances When an instance is deleted your account will receive pro-rated credit for the time remaining in the billing cycle. ## Suspended Instances Suspended Node-RED instances have no running editor, nor a runtime. You are not charged for suspended instances. When an instance is suspended your account will receive pro-rated credit for the time remaining in the billing cycle. When an instance is restarted it will be charged for the remaining time in the billing cycle. ## Managing Billing Details Click on "Billing" followed by "Stripe Customer Portal" to get a summary of the team's current subscription. You'll be redirected to a Stripe customer portal where you can update customer details as: The credit card on file and the billing information. ## Failed Payments If your payment fails for any reason you will receive a notification to the billing email address, you may need to login and update the card on file. Stripe will retry the payment several times over a number of days. If the card repeatedly fails your Node-RED instances will be suspended and a banner will be displayed at the top of the page for all users. An admin will need to update your card details to be able to create new instances or restart them. ## Cancelling your subscription To cancel your subscription you can either delete or suspend your team; both options are available under the Team Settings page. Deleting the team will remove all of the team's instances and devices - they cannot be recovered after being deleted. Suspending the team will stop all of your team's instances and devices and cancel your subscription so no further charges are made. You will not be able to do anything more with the team whilst it is suspended. You can unsuspend the team in the future by setting up a new payment subscription. When deleting the team, if you have outstanding credit you can request a refund via a [support ticket](https://flowfuse.com/support/), include the Team ID in your email. # FlowFuse Cloud FlowFuse Cloud is a hosted service allowing users to sign-up and start creating Node-RED instances without having to install and manage their own instance of FlowFuse. The [Concepts](https://flowfuse.com/docs/user/concepts) remain the same, but we run the platform for you. ## 30-day Free Trial When users sign-up to FlowFuse Cloud they get a 30-day free trial of the platform. This is a great way to start using FlowFuse and discover a lot of the value it provides. Users can end their trial by heading to the Billing page of their team and setting up their payment information. This includes the option to pick which plan you want to upgrade the team to. Otherwise, at the end of the 30-day trial period, any instances created in the team will be suspended. This means they will no longer be running and the editor will not be accessible. Users will need to add their Billing details at which point they will be able to restart their suspended Node-RED instances. We will email users about their trial when it is nearing the end to ensure they know what is happening. ## Billing Customers are billed at the team level for each Node-RED instance they create. This is a recurring monthly charge. See the [Billing](https://flowfuse.com/docs/cloud/billing) page for more detailed answers about billing. ## Support Premium customers can get support by [filing a ticket](https://flowfuse.com/support). We offer support for the FlowFuse application and your account, any issues relating to Node-RED such as your flows or a 3rd party node should be raised in the [community forum](https://community.FlowFuse.com){rel=""nofollow""}. ### Requesting a new verification email When a user signs up for FlowFuse Cloud an email will be sent to verify it. If this email doesn't get delivered one can be resend by signing in to FlowFuse and click the button to resend it. ## Team Types FlowFuse Cloud offers a Trial and an Enterprise Team Type, aimed at different sorts of users ### Enterprise Enterprise includes: - HA for Instances - SSO - Better Support SLA - MQTT Broker Includes 10 instances and 20 MQTT Clients in the base price ### Changing Team Type You can change Team Type by selecting the "Team Settings" option from the left hand menu, then clicking on the "Change Team Type" button ![Change Team Type](https://flowfuse.com/docs/cloud/images/change-team-type.png) From here you will be presented with a choice of Team Types. You will not be able to downgrade to a lower Team Type if you already have more resources than allowed at that level. Please Suspend or Delete any no longer required Instances or Devices. ![Available Team Types](https://flowfuse.com/docs/cloud/images/availble-team-types.png) ## Node-RED on FlowFuse Cloud FlowFuse currently offers Node-RED 4.x, 3.x and 2.x to customers. When creating a new instance a [stack](https://flowfuse.com/docs/user/concepts#stack) is chosen, which later can be [upgraded to a later version](https://flowfuse.com/docs/user/changestack). Each Node-RED can install custom modules as advertised in the [Flow Library](https://flows.nodered.org){rel=""nofollow""}. Note that some modules have dependencies on system libraries or other components that are not available within the FlowFuse container images we use. Those modules cannot be used within FlowFuse Cloud. If you have a particular requirement, please do [contact us](https://flowfuse.com/contact-us/){rel=""nofollow""} so we can discuss what options may be available. ## Cloud Instance Sizes The different sizes of Cloud Instances relate to how much memory and CPU is made available to them. Memory tends to be the main limiting factor for a Node-RED instance. The following table shows what is currently allocated by instance size. | Size | Memory (RAM) | | ------ | ------------ | | Small | 256MB | | Medium | 768MB | | Large | 3840MB | Medium and Large instance types require the Enterprise tier. ## Use of the File System FlowFuse Cloud hosted instances have access to a persistent file-system that will retain the files stored on it across restarts of the instance. A quota limit is applied to how much data can be stored, based on the Team type. Enterprise teams have a file storage quota of 100GB per instance. Files can be manually uploaded to an instance using the [Static Asset Service](https://flowfuse.com/blog/2024/08/flowfuse-2-8-release/#static-assets-service){rel=""nofollow""}. ## Node-RED Context Node-RED Context can be used to store small pieces of application state within the runtime. By default, this is stored in memory only. FlowFuse Cloud provides an optional context store that can be used to persist the data. The amount of data that can be stored in context is determined by the Team type. Enterprise teams have a context store quota of 1GB per instance. ## Network Connections ### HTTP(S) & Websockets Node-RED exposes an HTTPS interface on port 443 with each instance having its own hostname (`example.flowfuse.cloud`). Plain HTTP requests to port 80 will receive a redirect to HTTPS on port 443. You MUST connect using the hostname not the IP address to reach your Node-RED instance. Websocket connections over SSL (`wss:`) are also supported. The payload size per request is limited to 5MB, which is the Node-RED default. When a request exceeds this limit, the whole request is rejected with a `413 Payload Too Large` error. ### TCP and UDP The default TCP and UDP nodes have been removed from the Node-RED palette. This is because it is not possible to route these sorts of connections to the container running Node-RED inside the FlowFuse Cloud platform. ### MQTT MQTT Connections to an external broker using the standard MQTT nodes will work fine as the connection is initiated by Node-RED. FlowFuse provides an MQTT broker for Enterprise Node-RED instances. See the following section. Also the Project Nodes can be used to easily pass messages between Node-RED instances running in the platform. #### Enterprise Team Broker Enterprise teams come with their own MQTT broker. You can provision clients from the broker tab in the left hand menu. Enterprise level Teams can register up to 20 clients as part of their plan. The ability to purchase additional packs of clients will come in a near future release. The broker is available on `broker.flowfuse.cloud` and supports the following connection types: - MQTT on port `1883` - MQTT over TLS on port `8883` - MQTT over secure WebSockets on port `443` When creating clients you can specify a username, but it will prepended to the the Team's id e.g. `alice` will become `alice@32E4NEO5pY`. Clients must also use the username as the MQTT Client ID in order to connect. ![Create Broker Client](https://flowfuse.com/docs/cloud/images/create-broker-client.png) e.g. ```text mosquitto_sub -u "alice@32E4NEO5pY" -i "alice@32E4NEO5pY" -P "password" -h broker.flowfuse.cloud -t "#" ``` Or in Node-RED as follows ![Node-RED MQTT Client Connection](https://flowfuse.com/docs/cloud/images/node-red-mqtt-connection.png) ![Node-RED MQTT Client Security](https://flowfuse.com/docs/cloud/images/node-red-mqtt-security.png) ### IP Addresses Outbound connections from FlowFuse will always come from the IP address `63.33.85.112`. This can make access to a remote database or corporate network possible where those systems are protected by IP address filtering firewalls. All incoming connections MUST use the hostname and not an IP address. ## Data Security FlowFuse Cloud is hosted on Amazon Web Services. The following statements apply to data handling within the platform: - The underlying database use the industry standard AES-256 encryption algorithm to encrypt the stored data. - Persistent storage offered to Node-RED instances uses AES-256 encryption algorithm to encrypt data and metadata at rest. - The network load balancer uses the latest recommended AWS Network Security policy. This enforces TLS1.2 as a minimum. ## Single-Sign On FlowFuse supports configuring both SAML and LDAP based Single Sign-On for particular email domains. This can be configured on request for FlowFuse Cloud by submitting a support request via our [Contact Us](https://flowfuse.com/contact-us/){rel=""nofollow""} page. For SAML providers, you must have the ability to configure a SAML endpoint on your Identity Provider, and have the authority to configure SSO for your email domain. We have currently validated our SSO support with the following Identity Providers: - Microsoft Entra - Google Workspace - OneLogin - Keycloak If you are using a different Identity Provider, please still get in touch, and we can evaluate what will be required to enable it. ## Custom Hostnames FlowFuse Cloud can support custom hostnames for instances in Enterprise teams. This allows you to point your own subdomain, such as `dashboard.example.com` at one of your instances. See [Custom Hostnames](https://flowfuse.com/docs/user/custom-hostnames) for more information. ## Removing your account Before you can delete your account, teams you own must either be deleted or have at least 1 other owner. Once this is done, you can remove your account by going to the "User Settings" page and clicking the "Delete Account" button. See also: [cancelling your subscription](https://flowfuse.com/docs/cloud/billing#cancelling-your-subscription). # Adding Template Settings Within FlowFuse, each Node-RED instance is created from a Template. The Template defines a set of preconfigured options for the instance. This includes runtime settings - values that you would normally expect to set in your Node-RED settings.js file. The Template also defines which of those options can be customised by individual instances. This guide explains how to add a new Node-RED runtime option to the Template object so that it can be customised and passed through to the underlying Node-RED settings.js file. This is reasonably straightforward for simple boolean/string/numeric types. For other types (objects/arrays) it gets more complicated and we don't currently have good examples to follow. For the 'simple' cases, the steps are: 1. Update the Frontend 1. Pick a name for the setting, add it to the list of known settings and any validation logic that is needed 2. Add it to the appropriate Template section 2. Update the runtime 1. Add it to the known list of settings and any additional validation logic 3. Update the Launcher 1. Update the template used to generate settings.js with the new property ## 1. Updating the Frontend There are a set of views in the frontend used to present and edit templates. They get used in two different ways: 1. When creating/editing a template. All options are available and there is a dropdown that lets the user set the policy on the setting (which controls whether an instance is allowed to override the setting) 2. When editing instance settings. All options are shown, but only lets the user modify those that the template policy allows to be changed. This reuse of the views saves a lot of code duplication, at the cost of some complication in implementing it. ### 1.1 - Pick a name for the setting The name should try to match its corresponding property in the Node-RED settings.js file. Some thought should be made as to organisation of the properties. 1. Edit `frontend/src/pages/admin/Template/utils.js` 2. Add the new property name to the `templateFields` and `defaultTemplateValues` objects. Notice the names are flat strings with `_` used as a hierarchy separator... that will make more sense if you're looking at the file. 3. If the value needs any sort of validation, add it to `templateValidators` in the same file. ### 1.2 - Add it to the appropriate Template section Currently there are: - Editor - Palette - Environment - a special case that is unlikely to get other options added These each lives in their own file under `frontend/src/pages/admin/Template/sections/`. Pick an existing setting that most closely matches the setting you want to add (ie checkbox or text input), and copy its entry to the appropriate place. Make sure you update *all* of the references of the copied property name to your new property name. ## 2. Updating the Runtime ### 2.1 - Adding it to the known list 1. Edit `forge/db/controllers/ProjectTemplate.js` to add the new property to the list of known settings. 2. In that same file, update the `validateSettings` function with any additional validation or data cleansing needed. ## 3. Update nr-launcher In the `flowforge-nr-launcher` repo... 1. Edit `lib/runtimeSettings.js` to include the new setting in the generate settings.js file. Note that you must handle the case where the new setting is not present - either by applying a sensible default, or omitting the value. # HTTP API of the FlowFuse platform ## API documentation All public API routes should include a `schema` as part of their definition. This serves a number of purposes: - It will be included in the auto-generated OpenAPI 3.0 spec - Fastify will validate requests include any required properties - Fastify will ensure the response object matches the defined schema Some general guidance: - Ensure the routes have an appropriate tag set - this determines where in the Swagger UI it gets displayed. - Ensure the tag is listed in `forge/routes/api-docs.js` so it appears in the right place - We define view schemas under `forge/db/views/*` alongside the code that generates the view. Keep the naming consistent with other views. - Learn from the existing schemas - be consistent in style. References: - [Fastify Validation and Serialization](https://fastify.dev/docs/latest/Reference/Validation-and-Serialization){rel=""nofollow""} - [OpenAPI 3.0 spec](https://swagger.io/specification/){rel=""nofollow""} ## Object Ids Most database models have a primary key of an auto-incrementing integer stored in the `id` column. These ids are internal properties of the models and should *not* be exposed via the API. This is because they can be guessed and leak information about how many instances of any particular model exist. All model instances have an auto-generated `hashid` property that is an encoded version of the `id` property. This property should be used on the API but aliased as the `id` property. Each database model has a pair of helper functions to encode and decode hashids to/from the true `id` value: ```js const encodedHashid = app.db.models.User.encodeHashid(123); const objectId = app.db.models.User.decodeHashid("edjEbo2K1w") ``` ## API Path design ### Admin routes All admin-only routes exist under: ```txt /api/v1/admin/... ``` ### Logged-in user routes All routes relating to the logged-in user exist under: ```txt /api/v1/user/... ``` ### Object collection routes API routes that are related to objects in collections follow the pattern: ```txt /api/v1///... ``` ## Implementing routes All API routes exist under `forge/routes/api` in the repository, grouped into files based on the entity/functionality the api is related to. All requests will have already been validated to ensure they are being made with a valid session for a logged in user, or an access token. If there is a valid session, `request.session.User` will be the requesting user. ### Opening a route to anonymous users In rare cases, a route needs to be accessible to anonymous users. To by-pass the built-in preHandler, you can set `allowAnonymous` on the routes `config` object: ```js app.get('/', { config: { allowAnonymous: true } }, async (request, reply) => { ... }) ``` ### Team routes Routes under `forge/routes/api/team.js` that relate to a specific team, must use `:teamId` as the placeholder in the route. A request preHandler will use that to ensure the requesting user has permission to access the instance. The following properties will then be available on the `request`: - `request.team` - the team - `request.teamMembership` - the requesting user's role on the team ### Project routes **Note:** In FlowForge 1.5 we started to replace the Project concept with that of Application and Instance. No changes have been made to the underlying APIs - that will be evaluated as part of 1.6. Routes under `forge/routes/api/project.js` that relate to a specific instance, must use `:projectId` as the placeholder in the route. A request preHandler will use that to ensure the requesting user has permission to access the instance. The following properties will then be available on the `request`: - `request.project` - the instance (previously called "project") - `request.teamMembership` - the requesting user's role on the team that owns the instance ### User/role permissions Permissions are defined in `forge/routes/auth/permissions.js`. Each permission specifies the role a user must have to have that permission. If a route requires a particular permission, it can use `app.needsPermission` to generate a preHandler function that will ensure any requesting user has that permission. For example, to add a user to a team, the requesting user needs to have `"team:user:add"`: ```js app.post('/', { preHandler: app.needsPermission("team:user:add") }, async (request, reply) => { // This is defined under forge/routes/api/teamMembers.js - and is mounted // under the path `/api/v1/teams/:teamId/members/` // Due to the team route preHandler, this means `request.teamMembership` // will be defined and will be used by the `needsPermission` handler }); ``` ## Error formats If a route needs to return an error it should respond with a payload in the format: ```js { code: 'error_code', error: 'Human-readable message' } ``` The `code` property should be a well-defined string that can be used to programmatically identify the error without relying on the human-readable message. There is a set of predefined codes that should be used where appropriate: - `unauthorized` - `invalid_request` - `unexpected_error` If the error is related to an invalid option/parameter/object selection, then the code should be: - `invalid_` For example: `invalid_project_name`. ## Pagination & Search All routes that return collections of things must use pagination to allow for future growth, and provide a way to search the collection. ### Pagination We use a cursor-based approach to our pagination. A cursor is a pointer to an entity in the collection and provides the starting point for what should be returned. - End-points accept `cursor` and `limit` parameters - If `cursor` is not provided, it returns from the beginning of the collection. For some end-points, this will mean returning the most recent entries - the `/api/v1/projects/:id/logs` for example. - Each end-point should have a sensible default for the `limit` parameter The response object for paginated end-points should have the format: ```js { "meta": { "next_cursor": "16416724188790000", }, "": [ ], "count": 123 } ``` The `meta` property contains information to help the client navigate the collection. - `next_cursor` - if set, provides the cursor to use to get the next page of results - `previous_cursor` - if set, provides the cursor to use to get the previous page of results. Note that not all end-points need to be navigable in both directions so may never return `previous_cursor`. The `` property should be called the appropriate plural form of what is being returned, such as `projects`. The `count` property is optional and indicates the total number of objects in the collection. End-points *should* include it if the number is known and is of material use. It is *not* returned by end-points used to query logs etc as the total count is a constantly changing value. #### Cursor design A cursor should be able to point directly at an entity in the collection. It could be the entity hashid, or a timestamp if it is a time-series collection such as the logs. The cursor should *not* be the internal `id` of the entity in the collection - as we do not expose those ids on the api, use the `hashid` value as the external `id`. If an end-point supports navigating the collection in reverse (by returning `previous_cursor`), the cursor should be prefixed with `-` to indicate the query should be in the opposite direction to the collection's natural order. ### Search & Filtering Search is done as simple case-insensitive text-based queries against string columns in the database model. This is a crude but effective way to implement search but may need a more comprehensive approach in the future. The search value is provided via the `query` query parameter. ```text /api/v1/example?query=something ``` Filtering can be used to limit the results based on the values of specific columns. The Filter value is provided via additional query parameters. ```text /api/v1/example?eventName=foo ``` ### Implementing pagination & search Two utility functions are provided to help implement pagination and search. #### `app.getPaginationOptions` This returns an object of pagination options for a given request, with any default values automatically applied: ```js const paginationOptions = app.getPaginationOptions(request, {limit: 1000}) // paginationOptions.limit = how many results to return // paginationOptions.cursor = starting cursor // paginationOptions.query = search string to use // paginationOptions.* = any remaining query parameters ``` #### `buildPaginationSearchClause` This takes the pagination options along with model-specific configuration options to build a suitable `where` clause that can be passed to the database model's `getAll` function. ```text const where = buildPaginationSearchClause(params, whereClause, searchColumns, filterMap) ``` - `params` - the pagination object returned by `app.getPaginationOptions` for a given request - `whereClause` - (optional) an object containing any additional query clauses that should be applied - `searchColumns` - (optional) an array of fully-qualified column names that should be searched when `query=` is included in query - `filterMap` - (option) a map of filter name to fully-qualified column name that are valid filter parameters With the filtering option, if a particular filter parameter is specified more than once in the query string, the generated query will apply an Or between the values. For example, lets consider the imaginary `Thing` model has a `name`, `description` and `type`. The following will mean: - any `query=` parameter will be searched for in the `name` and `description` columns - any `type=` parameter will filter on the `type` column. ```js const { buildPaginationSearchClause } = require('../utils') ... getAll: async (pagination = {}, where = {}) => { // Ensure a sensible default limit for this particular type of thing const limit = parseInt(pagination.limit) || 1000 // Decode the cursor from hashid to database id if (pagination.cursor) { pagination.cursor = M.Thing.decodeHashid(pagination.cursor) } // Build the full where query using the buildPaginationSearchClause utility. // We pass in the list of columns that should be searched against where = buildPaginationSearchClause( pagination, where, ['Thing.name', 'Thing.description'], { type: 'Thing.type' } ) // Run the query const { count, rows } = await this.findAndCountAll({ where, order: [['id', 'ASC']], limit }) // Return the results with the additional metadata return { meta: { next_cursor: rows.length === limit ? rows[rows.length - 1].hashid : undefined }, count, things: rows } } ``` # FlowFuse Architecture A FlowFuse install is made up of 2 main components - The Management Application - The Node-RED instances These can be deployed in one of 2 ways - On a single machine :br![LocalFS Architecture](https://flowfuse.com/docs/contribute/images/ff-localfs.png) - Using a Container Orchestration platform (Kubernetes/Docker Compose) :br![Container Architecture](https://flowfuse.com/docs/contribute/images/ff-containers.png) ## FlowFuse Management Application This provides the interface for managing the objects in the platform. It also provides a collection of APIs to support the Node-RED instances once started. A key component is the Container API driver, this is the part that actually creates/destroys Node-RED instances and keeps track of what should be running and restarts if needed. ### Container Drivers Node-RED instances are started by the FlowFuse Management Application via one of the following Container Drivers. Documentation for the Container Driver API will be available in the [API](https://flowfuse.com/docs/api) section. #### Localfs This driver runs Node-RED as separate processes on the same machine as the FlowFuse Management Application. Each instance gets its own `userDir` and a dedicated TCP/IP port to listen to. State is stored in a local SQLite database There is no automatic Ingres automation provided by this driver. #### Kubernetes This driver runs Node-RED in separate containers and each instance is accessed by a dedicated hostname via an HTTP Ingres proxy. The state is stored in a provided PostgreSQL database. Node-RED containers are segregated into their own namespace (currently hardcoded to `flowforge`) The driver uses the [@kubernetes/client-node](https://www.npmjs.com/package/@kubernetes/client-node){rel=""nofollow""} to interact with the cluster. The driver will create the required Service and Ingres Kubernetes resources to expose each instance via whatever Ingress Controller the underlying Kubernetes cluster provides. #### Docker-Compose This driver runs Node-RED in separate containers and each instance is accessed by a dedicated hostname via an HTTP Ingres proxy. The state is stored in a provided PostgreSQL database. The driver uses the [dockerode](https://www.npmjs.com/package/dockerode){rel=""nofollow""} to interact with the cluster. The driver will add the required Environment variables to each Node-RED container to work with the [jwilder/nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy){rel=""nofollow""} NGINX proxy. ## FlowFuse Instances A FlowFuse Node-RED Instance is made up of 2 processes - The FlowFuse Launcher - A Node-RED instance ![Project Architecture](https://flowfuse.com/docs/contribute/images/ff-project-arch.png) ### FlowFuse Launcher This is a small application that handles downloading the Instance specific settings, building a `settings.js` from those settings and then starting the Node-RED instance. The launcher presents an HTTP API (it defaults to the Node-RED port + 1000) that allows the FlowFuse Management Application to start/stop/restart the Node-RED instance as well as query its current state and retrieve the console logs. The launcher can be found [here](https://github.com/FlowFuse/nr-launcher){rel=""nofollow""} Within the launcher are some custom plugins that are loaded by Node-RED: #### nr-storage This plugin is used to save flows, settings, sessions, and library entries back to the FlowFuse Management Application. #### nr-auth This plugin is used to authenticate users trying to access the Node-RED Editor, it refers back to the FlowFuse Management Application to ensure only members of the team that owns the instance can log in. This plugin uses the Node-RED [Authentication API](https://nodered.org/docs/user-guide/runtime/securing-node-red#custom-user-authentication){rel=""nofollow""} #### nr-audit-logger This plugin sends Node-RED Audit events (e.g. user log in and flow deployment events) back to the to the FlowFuse Management Application to allow a reliable audit of what actions have taken place in the instance. This plugin uses the Node-RED [Logging API](https://nodered.org/docs/user-guide/runtime/logging){rel=""nofollow""} ## Component Overview ```mermaid erDiagram USER ||--o{ NGINX : Requests NGINX { Protocol HTTP-TLS Port default-443 } FORGE-APP { Protocol HTTP-TLS Port default-3000 } POSTGRESQL { Protocol tcp-tls Port default-5432 } MOSQUITTO { Protocol HTTP-TLS-WSS-MQTT Port default-1883 Port websocket-1884 } FLOWFORGE-FILE-SERVER { Protocol HTTP-TLS Port default-3001 } NGINX }o--o| NODE-RED : routes NGINX }o--o{ FORGE-APP: routes NGINX ||--|| MOSQUITTO: mqtt-ws FORGE-APP ||--|{ POSTGRESQL: query FORGE-APP ||--|| NODE-RED: "flow update" NODE-RED }o--o{ FLOWFORGE-FILE-SERVER: "Blob store" FLOWFORGE-FILE-SERVER ||--|| FORGE-APP: "Authenticate" NODE-RED { Protocol HTTP-TLS Port default-1880 } NODE-RED }o--|| MOSQUITTO: mqtt FORGE-APP }o--|| MOSQUITTO: mqtt NODE-RED-DEVICES { Port default-1880 Protocol User-Defined } NODE-RED-DEVICES }o--|| NGINX: mqtt-ws USER ||--|| NODE-RED-DEVICES: Requests ``` # Creating Debug Stack Containers Sometimes we want to be able to run some debug code within a stack running in our staging test environment. For example, changes to the `nr-launcher` component or any of the other components that run within the stack. This guide shows a simple way to do that without having to rebuild the container from scratch each time. This will require: 1. Docker 2. A container registry you can push images to. For example, a free DockerHub account where you can push images ### Creating a debug container Pick an existing container image to use as your starting point. For example, `flowfuse/node-red:2.5.0-4.0.x` contains `nr-launcher@2.5.0` and the latest Node-RED 4.x release. **Note**: our staging environments requires `arm64` based containers. The following instructions work for Macs (with M1/M2) - additional steps may be needed for other OS; please contribute them if you know them. Use the following command to open a shell into the container: ```text docker run -it --entrypoint /bin/bash flowfuse/node-red:2.5.0-4.0.x ``` The prompt will then look like this: ```text e8dcd669ea4c:/usr/src/flowforge-nr-launcher$ ``` Take a note of the `e8dcd669ea4c` - this is the id of the container instance you have created. The following directories are probably of interest: - `/usr/src/flowforge-nr-launcher` - contains the `nr-launcher` code and its dependencies - `/usr/src/node-red` - contains the `node-red` code Using `vi`, you can edit the files to make the changes you want and when you're done, exit the shell. Having made the changes, use `docker commit` to create a new container image. The command requires the id of the container you've just edited and the tag for the container. It also needs to restore the `entrypoint` configuration back to the default. ```text docker commit \ --change='ENTRYPOINT ["./node_modules/.bin/flowfuse-node-red", "-p", "2880", "-n", "/usr/src/node-red"]' \ e8dcd669ea4c \ knolleary/ff-debug:debug-1 ``` Finally, you can push the new container to your container registry. ```text docker push knolleary/ff-debug:debug-1 ``` ### Using the container in FlowFuse Debug containers should *only* be used in pre-staging/staging environments. Do *not* add to production. Once pushed, you can create a custom stack in the FlowFuse admin section and give it the location of your container. ### Iterating If you find you want to add some more debug, repeat the process, however use your existing image as the starting point: ```text docker run -it --entrypoint /bin/bash knolleary/ff-debug:debug-1 ``` Be sure to increment the number in the image name (`debug-2`) when you commit and push the new container. ### Tidying up Remember to delete your instances/stacks once you're done, as well as all of the local docker images and containers that have been created along the way. # Database Migrations Any changes made to the database models must include migrations that can modify the database state from one state to another. Whilst we use Sequelize as our ORM layer, we do not use the migration tooling it provides - we have our own. ## Creating migrations ### Filename A migration is provided as JavaScript in the directory `forge/db/migrations`. Its file name must follow the pattern: ```text YYYYMMDD-nn-description.js ``` - `YYYYMMDD` - the date the migration is added - `nn` - a two digit number - `description` - a name for the migration For example `20220204-01-add-billing.js`. This ensures the migrations have a natural order to be applied. The `nn` part of the name allows multiple migrations to be added on the same day but is kept in the right order. ### Structure The migration code should use the following layout: ```js module.exports = { up: async (context) => { // Apply the migration }, down: async (context) => { // Remove the migration } } ``` The `up` function applies to the migration. This can be to create new tables, add columns to existing ones - whatever is needed. The `down` function reverses the migration. It should restore the database back to how it was prior to the migration. The `context` argument is an instance of [Sequelize.QueryInterface](https://sequelize.org/docs/v6/other-topics/query-interface/){rel=""nofollow""} that can be used to perform operations on the database. ## Applying migrations Migrations are applied automatically at the start of the FlowFuse application. Down migrations are not yet supported. ## Considerations when writing migrations Whilst migrations give us the ability to make changes to the database, they must be used with great care. A failing migration will prevent the platform from starting and may require manual intervention. Everything should be done to avoid that from happening. Certain types of migration need particular guidance and care over. ### Adding constraints If a migration is adding a new constraint to an existing table, you need to consider very carefully what impact that could have on an existing system with real data. For example, adding a new 'unique' constraint where you cannot guarantee that constraint hasn't already been broken. What strategy will you use to guard against that or to help recover from it? What additional testing is needed for the migration to verify its behavior in those situations? The preferred method to add a new unique constraint is by adding a new index to the database. This is because SQLite doesn't provide a way to alter columns that doesn't involve dropping the whole table and triggering any cascade triggers. # Working with Feature Flags When adding features to the platform it is sometimes a requirement to be able to restrict the feature to licensed platforms, and furthermore to certain types of team on the platform. Most typically this will be a feature that should only be available to the `Enterprise` tier on FlowFuse Cloud. This is a quick guide for how to add a feature flag - both at the platform-wide level and against individual `TeamTypes`. Feature flag names should use `camelCase`. ### Add a platform-wide feature flag All licensable features should set a platform-wide feature flag to indicate the feature is available. ```text app.config.features.register('featureFlagName', true) ``` [Here](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/forge/ee/lib/ha/index.js#L6){rel=""nofollow""} is an example of how the HA feature sets its platform-wide flag. ### Add a team type feature flag The feature flag is set by an admin user via [`TeamTypeEditDialog.vue`](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/frontend/src/pages/admin/TeamTypes/dialogs/TeamTypeEditDialog.vue){rel=""nofollow""}. 1. Add an entry to the existing list of feature flags [here](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/frontend/src/pages/admin/TeamTypes/dialogs/TeamTypeEditDialog.vue#L73-L85){rel=""nofollow""}. 2. Add a check to ensure the right default value is applied [here](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/frontend/src/pages/admin/TeamTypes/dialogs/TeamTypeEditDialog.vue#L172-L174){rel=""nofollow""}. Any new feature should default to `false` so it can then be selectively enabled. ### Using the feature flags - runtime side Platform-wide feature flags can be checked using: ```text const isFeatureEnabledOnPlatform = app.config.features.enabled('featureFlagName') ``` `TeamType` feature flags can be checked using the `getFeatureProperty` function of the `TeamType` model: ```text // myTeamType is an instance of `TeamType` const isFeatureEnabledForTeamType = myTeamType.getFeatureProperty('featureFlagName', false) ``` The first arg is the name of the feature flag, the second arg is the default value if the feature flag is otherwise unset. As mentioned above, any new feature should default to `false`. [Here](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/forge/ee/routes/sharedLibrary/index.js#L22){rel=""nofollow""} is an example of this in action. ### Using the feature flag - frontend In the frontend, platform-wide feature flags can be checked against the `features` property of the `account` store. TeamType feature flags can be checked against `team.type.properties.features.featureFlagName`. [Here](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/frontend/src/pages/application/DeviceGroups.vue#L135-L146){rel=""nofollow""} is an example of how we combine these two things: ```text computed: { ...mapState('account', ['features']), featureEnabledForTeam () { return !!this.team.type.properties.features?.deviceGroups }, featureEnabledForPlatform () { return this.features.deviceGroups }, featureEnabled () { return this.featureEnabledForTeam && this.featureEnabledForPlatform } }, ``` This allows the UI to distinguish between a feature being unavailable because the platform is not licensed for it, and a feature being unavailable for the current team type. This allows different messages to be displayed with the most appropriate call to action. The `EmptyState` component has support for this - see [here](https://github.com/FlowFuse/flowfuse/blob/0335c9056019ff9987d97f3ad3f18675de1c2422/frontend/src/pages/application/DeviceGroups.vue#L29){rel=""nofollow""} for how that is applied. ### Using the FEATURE\_CONFIGS composable The recommended approach for frontend feature checks is to use the `FEATURE_CONFIGS` array in `frontend/src/composables/FeatureChecks.ts`. Each entry defines a feature with platform and/or team level checks, and the `buildFeatureChecks` function produces computed properties that are available via the `featuresCheck` getter on the `account-settings` store. Features can be **opt-in** (disabled by default, must be explicitly enabled per team type) or **opt-out** (enabled by default, must be explicitly disabled). Opt-out features use `optOut: true` in their config entry. Features can also declare dependencies on other features using `dependsOn`, `dependsOnPlatform`, and `dependsOnTeam`. For example, all AI sub-features depend on the `ai` flag: ```text { output: 'isExpertAssistantFeatureEnabled', platformKey: 'expertAssistant', teamKey: 'expertAssistant', optOut: true, dependsOnPlatform: 'ai', dependsOnTeam: 'ai', dependsOnTeamOptOut: true } ``` See the JSDoc on the `FEATURE_CONFIGS` array in `FeatureChecks.ts` for full documentation of all available options. # Contributing to FlowFuse This guide will help you get setup to contribute to the FlowFuse project. The core of the FlowFuse platform is available under the Apache-2.0 license and we welcome contributions from the community. ### Software Requirements This guide assumes you have a working development environment including: - Node.js 18/20 - Platform build tools - Linux: `apt-get install build-essential` - MacOS: `xcode-select --install` - Windows: installed as part of the official node.js installer - ☑️ Automatically install the necessary tools must be checked - Git ### Project Repositories There are a number of repositories under the [FlowFuse GitHub organisation](https://github.com/FlowFuse){rel=""nofollow""} that make up the platform. | Repository | Description | | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [FlowFuse](https://github.com/FlowFuse/flowfuse){rel=""nofollow""} | This is the core of the platform. | | [installer](https://github.com/FlowFuse/installer){rel=""nofollow""} | The installer for the platform | | [driver-localfs](https://github.com/FlowFuse/driver-localfs){rel=""nofollow""} | The LocalFS driver. This deploys instances to the local system. | | [driver-docker](https://github.com/FlowFuse/driver-docker){rel=""nofollow""} | The Docker driver. This deploys instances as containers in a Docker-managed environment. | | [driver-k8s](https://github.com/FlowFuse/driver-k8s){rel=""nofollow""} | The Kubernetes driver. This deploys instances as containers in a Kubernetes-managed environment. | | [nr-launcher](https://github.com/FlowFuse/nr-launcher){rel=""nofollow""} | The launcher application is used to start and monitor an individual instance of Node-RED in the FlowFuse platform. This includes a number of Node-RED plugins used to integrate with the FlowFuse platform. | ### Setting Up A Development Environment With the project split across multiple repositories, setting up a development environment manually takes quite a lot of steps to ensure everything is checked out and configured properly. To make it easier, you can use the [FlowFuse Development Environment](https://github.com/FlowFuse/dev-env){rel=""nofollow""} project to get set up. The following steps will get your development environment setup in no time: ```bash git clone https://github.com/FlowFuse/dev-env.git cd dev-env npm install npm run init ``` This clones all of the main project repositories, installs their dependencies and builds the repositories that need it. All of the repositories are cloned under the `packages` directory: ```txt dev-env └── packages ├── device-agent ├── docker-compose ├── driver-docker ├── driver-k8s ├── driver-localfs ├── file-server ├── flowfuse ├── helm ├── installer ├── nr-file-nodes ├── nr-launcher └── nr-project-nodes ``` More details on using the FlowForge Development Environment are available in its [documentation](https://github.com/FlowFuse/dev-env){rel=""nofollow""}. ### FlowFuse Code Structure The `FlowFuse/flowfuse` repository is the core of the platform and where you'll likely want to begin. ```txt . ├── bin ├── config - build config files ├── docs ├── etc - FlowFuse platform configuration files ├── forge - Platform core code │ ├── config │ ├── containers │ ├── db │ ├── ee │ ├── lib │ ├── licensing │ ├── monitor │ ├── postoffice │ ├── routes │ └── settings ├── frontend - Frontend code │ ├── dist - build output - created by `npm run build` │ ├── public - static assets │ └── src - vue src │ ├── api │ ├── components │ ├── pages │ ├── routes │ └── store ├── test - tests for FlowFuse └── var - where the database and localfs project directories are created ``` ## Development Setup 1. [Create a Stack](https://flowfuse.com/#create-a-stack) 2. [Running FlowFuse](https://flowfuse.com/#running-flowfuse) 3. [Configuring FlowFuse](https://flowfuse.com/#configuring-flowfuse) 4. [Mocking email](https://flowfuse.com/#mocking-email) 5. [Testing](https://flowfuse.com/#testing) 6. [VSCode Tips](https://flowfuse.com/#vscode-tips) 7. [Team Broker](https://flowfuse.com/docs/contribute/team-broker) ### Create a Stack You will need to setup the version(s) of Node-RED you want to use in your stacks. From the `flowfuse` directory run ```bash npm run install-stack --vers=3.1.9 ``` Where `3.1.9` is the version of Node-RED you want to use in the stack. #### Working with Local Nodes If you want to test local, in-development nodes in FlowFuse, you can create a dedicated stack for this purpose. ```bash npm run install-stack --vers=3.1.9 ``` Navigate to the stacks directory: ```bash cd /var/stacks ``` Rename the directory to something more appropriate: ```bash mv "3.1.9" "3.1.9-local" ``` Install your local repository directly into the stack: ```bash cd 3.1.9-local npm install /path/to/your/nodes-repo ``` With your stack created from the terminal, you can now add it via the FlowFuse Admin UI - see [Managing Stacks](https://flowfuse.com/docs/admin/introduction/#create-new-stack). ### Running FlowFuse A number of `npm` tasks are defined in the `package.json` file of this repository. To get started from the `flowfuse` directory use: ```bash npm run serve ``` This does a couple of things in parallel: - Starts the core FlowFuse application and watches the source code for any changes - triggering a restart if needed. - Builds the frontend application using WebPack and watches for any changes - triggering a rebuild as needed. When running like this, the `NODE_ENV` environment variable gets set to `development`. *Note*: if you have not used the [FlowFuse Development Environment](https://flowfuse.com/#setting-up-a-development-environment), then you will need to run `npm run build` to build the platform before you can use `npm run serve`. ### Configuring FlowFuse When running in development mode, the core app will use `etc/flowforge.yml` for its configuration. As you may want to have a local configuration that you don't want to commit back to git, you can create a file called `etc/flowforge.local.yml` and it will use that instead. That filename is set to be ignored by git so it won't be accidentally committed. ### Mocking email If you are developing locally and need to enable external email sending, you can either: - Setup a local test SMTP server. For example, the Nodemailer project provides a useful app that does the job: {rel=""nofollow""} - Alternatively, set the `email.debug` option to `true` in your configuration file and the app will print all emails to its log. ### Configuring billing If you need to develop features covered by the Billing EE feature, you will need to configure the platform with a set of valid Stripe API keys and an EE license. The *development-only* EE licence is provided in `flowfuse/forge/licensing/index.js`. This licence is not valid for production usage. For FlowForge Inc. employees the configuration is provided in 1Password as 'Stripe Testing Configuration'. ```yaml license: *** billing: stripe: key: *** wh_secret: *** team_price: *** team_product: *** project_price: *** project_product: *** device_price: *** device_product: *** deviceCost: 10 new_customer_free_credit: 1000 teams: starter: price: *** product: *** userCost: 0 ``` You will also need to install the [Stripe CLI](https://stripe.com/docs/cli/){rel=""nofollow""} to handle webhook callbacks properly. Install the CLI following their documentation, then run the following command, with the API key using the value of `billing.stripe.key` from above. ```bash stripe listen --forward-to localhost:3000/ee/billing/callback --api-key *** ``` Note that due to the way Stripe works, you will receive events for *all* activity in the configured Stripe account. That means if someone else is actively developing with billing enabled on the same account, you will see their events arrive. #### Free Trials Free trials are implemented as a Stripe Credit that is applied when a FlowFuse user creates their first team and completes billing sign up. To enable trials, set the `billing.stripe.new_customer_free_credit` value to a credit amount in cents. For a totally free trial, this amount should match the cost of the Stripe product for the project type to be trialed to be trialed. The Stripe webhook forwarder must be running as the credit is handled as part of the webhook handling. ### Testing Our testing philosophy follows the principle of: > Write tests. Not too many. Mostly integration [1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""} We create both unit tests and system level tests. The former is suitable for well-contained components that need to provide a stable api and behavior to the rest of the code base. The latter is for testing the external behavior of the platform as a whole with as little internal mocking as possible. We use code coverage reporting as *one* aspect of assessing our testing coverage. We do not treat 100% coverage as an imperative goal - that can often lead to busy work writing tests that don't provide any real value in understanding the overall quality of the system. Unit tests should provide sufficient coverage to give us confidence that a component's behavior does not unexpectedly change. #### Running tests To run the tests for the project, you can use the following npm tasks: - `npm run test` - runs the whole test suite, covering code linting, unit and systems tests. - `npm run lint` - runs the linting tests - `npm run test:unit` - runs the unit tests - `npm run test:system` - runs the system tests - `npm run test:docs` - checks the validity of links in the documentation ##### Testing against PostgreSQL By default, the tests use an in-memory SQLite database to test against. This is the most self-contained way of testing the platform. But it is also necessary to test against PostgreSQL. To enable the use of PostgreSQL in the tests: 1. Ensure you have an instance of PostgreSQL running locally. For example, via docker: ```bash docker run -it -p 5432:5432 --name ff-postgres -e POSTGRES_PASSWORD=secret postgres:14 ``` 2. Enable PostgreSQL mode by setting the following environment variable: ```bash export FF_TEST_DB_POSTGRES=true ``` :brThe database connection can be set using the following env vars (default values shown) ```bash export FF_TEST_DB_POSTGRES_HOST=localhost export FF_TEST_DB_POSTGRES_PORT=5432 export FF_TEST_DB_POSTGRES_USER=postgres export FF_TEST_DB_POSTGRES_PASSWORD=secret export FF_TEST_DB_POSTGRES_DATABASE=flowforge_test ``` #### Reporting code coverage The `test:*` tasks have corresponding code coverage tasks. These tasks run the tests using `nyc` to generate code coverage information. - `npm run cover` - runs the whole test suite (excluding linting) with code coverage enabled and generates a report (via the `cover:report` task) - `npm run cover:unit` - runs the unit tests with code coverage enabled. It does *not* generate the report. - `npm run cover:system` - runs the system tests with code coverage enabled. It does *not* generate the report. - `npm run cover:report` - generates a report of the code coverage. This is printed to the console and generates a browsable HTML copy under `coverage/index.html` ### VSCode Tips To step debug in VSCode 1. Open `launch.json` config and enter the JavaScript below 2. Choose `Start-Watch` from the "Run and Debug" menu 3. Press ▶️ or :kbd[F5] to start debugging There are 2 other "Run and Debug" entries in the menu... - "Attach by Process ID" - this will allow you to attach to a launched driver - "Debug Current Test" - this will enable you to step debug a test (starts debugging the currently open test file) #### ```json { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "command": "npm run start-watch", "name": "Start-Watch", "request": "launch", "type": "node-terminal", "env": { "NODE_ENV": "development" } }, { "name": "Attach by Process ID", "processId": "${command:PickProcess}", "request": "attach", "skipFiles": [ "/**" ], "type": "node" }, { "type": "node", "request": "launch", "name": "Debug Current Test", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ "--no-warnings", "-u", "bdd",// set to bdd, not tdd "--timeout", "999999", "--colors", "${file}" ], "env": { "NODE_ENV": "development" }, "internalConsoleOptions": "openOnSessionStart" } ] } ``` ## Footnotes 1. {rel=""nofollow""} [↩](https://flowfuse.com/#user-content-fnref-1){.data-footnote-backref ariaLabel="Back to reference 1" dataFootnoteBackref=""} # Local Install This guide is for setting up FlowFuse on a single machine, ideal for smaller deployments, evaluations, or for contributors who want to gain a basic understanding of the FlowFuse platform and its features. **Note: Local installation does not support HTTPS** ## Prerequisites ### Operating System The install script has been tested against the following operating systems: - Raspbian/Raspberry Pi OS versions Buster/Bullseye [1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""} - Debian Buster/Bullseye - Fedora 35 - Ubuntu 20.04 - CentOS 8/RHEL 8/Amazon Linux 2 - MacOS Big Sur & Monterey on Intel & Apple M processors - Windows 10 & 11 ### Node.js FlowFuse requires ***Node.js v20***. #### Linux The install script will check to see if it can find a suitable version of Node.js. If not, it will offer to install it for you. It will also ensure you have the appropriate build tools installed that are often needed by Node.js modules to build native components. #### Windows/MacOS If the install script cannot find a suitable version of Node.js, it will exit. You will need to manually install it before proceeding. Information about how to do this can be found on the Node.js website here: {rel=""nofollow""} You will also need to install the appropriate build tools. - **Windows**: the standard Node.js installer will offer to do that for you. - **MacOS**: you will need the `XCode Command Line Tools`to be installed. This can be done by running the following command: ```bash xcode-select --install ``` ## Installing FlowFuse 1. Create a directory to be the base of your FlowFuse install. For example: `/opt/flowforge` or `c:\flowforge`:br For Linux/MacOS: ```bash sudo mkdir /opt/flowforge sudo chown $USER /opt/flowforge ``` :brFor Windows: ```bash mkdir c:\flowforge ``` 2. Download the latest [Installer zip file](https://github.com/FlowFuse/installer/releases/latest/download/flowforge-installer.zip){rel=""nofollow""} into a temporary location. 3. Unzip the downloaded zip file and copy its contents to the FlowForge directory ### For Linux/MacOS: :br*Assumes `/tmp/` is the directory where you downloaded `flowforge-installer.zip`* ```bash cd /tmp/ unzip flowforge-installer.zip cp -R flowforge-installer/* /opt/flowforge ``` ### For Windows: :br*Assumes `c:\temp` is the directory where you downloaded `flowforge-installer.zip`* ```bash cd c:\temp tar -xf flowforge-installer.zip xcopy /E /I flowforge-installer c:\flowforge ``` 4. Run the installer and follow the prompts :br For Linux/MacOS: ```bash cd /opt/flowforge ./install.sh ``` :brFor Windows: ```bash cd c:\flowforge install.bat ``` ### Installing as a service (optional) On Linux, the installer will ask if you want to run FlowFuse as a service. This will mean it starts automatically whenever you restart your device. If you select this option, it will ask if you want to run the service as the current user, or create a new `flowforge` user. If you choose to create the user, it will also change the ownership of the FlowFuse directory to that user. ## Configuring FlowFuse The default FlowFuse configuration is provided in the file `flowforge.yml` - Linux/MacOS: `/opt/flowforge/etc/flowforge.yml` - Windows: `c:\flowforge\etc\flowforge.yml` The default configuration file already contains everything you need to get started with FlowFuse. It will allow you to access FlowFuse and the Node-RED instances you create, from the same server running the platform. If you want to allow access from other devices on the network, you must edit the configuration file and change the `host` setting to `0.0.0.0` and change `base_url` to contain the IP address of the server. NOTE: We do not support changing the `host` and `base_url` values once you have created an instance. For more information on all of the options available, see the [configuration guide](https://flowfuse.com/docs/install/configuration). ## Running FlowFuse To run it manually, you can use: - Linux/MacOS: ```bash /opt/flowforge/bin/flowforge.sh ``` - Windows: ```bash c:\flowforge\bin\flowforge.bat ``` Or to run as a service: - Linux ```bash service flowforge start ``` ## First Run Setup Once FlowFuse is started, you will be ready to perform the first run setup. Follow [this guide](https://flowfuse.com/docs/install/first-run) to continue. ## Setting up Mosquitto (optional) The platform depends on the [Mosquitto MQTT Broker](https://mosquitto.org/){rel=""nofollow""} to provide real-time messaging between devices and the platform. This is currently an *optional* component - the platform will work without the broker, but some features will not be available (e.g Access to Remote Instance Editor and Remote Instance logs requires the MQTT Broker). We do **not** support sharing a broker with other non-FlowFuse applications. If you already have mosquitto installed and running, you will need to run a second instance dedicated to FlowFuse. You can either follow the manual install steps, which involve building the authentication plugin from scratch, or make use of the [Docker install](https://flowfuse.com/#docker-install). ### Manual install **Note**: if you are running on Windows, you will need to follow the [Docker install](https://flowfuse.com/#docker-install) instructions below due to a limitation of the authentication plugin we use. Follow the appropriate [install instructions](https://mosquitto.org/download/){rel=""nofollow""} for your operating system. Once installed, you can download pre-built binaries for Linux platforms from [here](https://github.com/iegomez/mosquitto-go-auth/releases/latest){rel=""nofollow""} and then jump to step 4 below. On MacOS you will need to build and install the authentication plugin. 1. Clone the plugin repository ```bash git clone https://github.com/iegomez/mosquitto-go-auth.git ``` 2. Follow the instructions on [building the plugin](https://github.com/iegomez/mosquitto-go-auth#building-the-plugin){rel=""nofollow""} 3. This should result in a file called `go-auth.so` being generated 4. Run mosquitto with the configuration file found in the `broker` directory :br You will need to customise the values to match your local configuration: - `auth_plugin` - set to the path of the `go-auth.so` file built in the previous step - `listener 1883/1884` - if you already have mosquitto running locally, you'll need to change these ports to something else. - `auth_opt_http_host` / `auth_opt_http_port` - if you plan to run the platform on a different port, change these settings to match. ```bash mosquitto -c broker/mosquitto.conf ``` ### Docker Install Instead of installing and building mosquitto and the authentication plugin from source, you can use a pre-built docker image that provides everything needed. 1. First pull the latest version of the pre-built container ```bash docker pull iegomez/mosquitto-go-auth ``` 2. A default mosquitto.conf file can be found in the `broker` directory. :br You will need to customise the values to match your local configuration: - `auth_opt_http_host` value to match the IP address of either the docker0 interface or the external IP address of the machine running the FlowFuse platform - `auth_opt_http_port` if you have changed the port the FlowFuse platform is running on - `auth_plugin` should be changed to `auth_plugin /mosquitto/go-auth.so` 3. Start the container with the following command ```bash docker run -d -v /opt/flowforge/broker/mosquitto.conf:/etc/mosquitto/mosquitto.conf -p 1883:1883 -p 1884:1884 --name flowforge-broker iegomez/mosquitto-go-auth ``` :brThis will map the `1883`/`1884` ports to the host machine so they can be accessed outside of the container. If you already have an MQTT broker running on port 1883, then you'll need to modify the `-p` options to use a different set of ports. For example: `-p 9883:1883 -p 9884:1884`. ## File Server By default the FlowFuse File Server component is disabled as it is a licensed feature. If you provide a license you can start the File Server with the following command: ```bash sudo service flowforge-file start ``` You can then uncomment the following section in the `/opt/flowforge/etc/flowforge.yml` file ```yaml ################################################# # File Server config # ################################################# fileStore: url: http://localhost:3001 ``` ## Upgrade If upgrading from 1.x.y to 2.x.y then you may need to upgrade from NodeJS v16 to NodeJS v18. Please ensure you do this before the following steps. To upgrade to the latest release you can follow these steps. Replace `x.y.z` with the version you are upgrading to. 1. Stop FlowFuse `sudo service flowfuse stop` [2](https://flowfuse.com/#user-content-fn-2){#user-content-fnref-2 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""} 2. Change into the `app`directory - `cd /opt/flowforge/app` (Linux/MacOS) - `cd c:\flowforge\app` (Windows) 3. NPM install the desired version - `sudo -u flowforge npm install @flowfuse/flowfuse@x.y.z` (Linux/MacOS) [3](https://flowfuse.com/#user-content-fn-3){#user-content-fnref-3 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""} - `npm install @flowfuse/flowfuse@x.y.z` (Windows) 4. Check the release notes for any additional steps needed to upgrade the particular version 5. Restart FlowFuse `sudo service flowfuse start` [2](https://flowfuse.com/#user-content-fn-2){#user-content-fnref-2-2 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""} If you are running as your normal user you can drop the `sudo -u flowfuse` and just run `npm install @flowfuse/flowfuse@x.y.z` ## Uninstall To uninstall, stop all the running processes and then delete the `/opt/flowfuse` or `c:\flowfuse\` directory. ## Footnotes 1. Arm6 devices, such as the original Raspberry Pi Zero and Zero W are not supported. [↩](https://flowfuse.com/#user-content-fnref-1){.data-footnote-backref ariaLabel="Back to reference 1" dataFootnoteBackref=""} 2. Assumes you are running FlowFuse as a Linux service. [↩](https://flowfuse.com/#user-content-fnref-2){.data-footnote-backref ariaLabel="Back to reference 2" dataFootnoteBackref=""} [↩2](https://flowfuse.com/#user-content-fnref-2-2){.data-footnote-backref ariaLabel="Back to reference 2-2" dataFootnoteBackref=""} 3. Assumes you are running FlowFuse as the `flowfuse` user as created by the installer [↩](https://flowfuse.com/#user-content-fnref-3){.data-footnote-backref ariaLabel="Back to reference 3" dataFootnoteBackref=""} # Local Stacks A Stack defines a set of platform configuration options that will get applied to each Node-RED instance when it is created. For the Local deployment model, this covers two things: - `memory` - the value to apply (in MB) to the Node.js `max-old-space-size` option. This defines the point where Node.js will start freeing unused memory. It is not a hard limit - Node-RED's memory usage will not be capped - but this is useful when running on a memory constrained device such as a Raspberry Pi. Recommended minimum `256`. - `nodered` - the version number of Node-RED to use. This should match the value used in the steps following. The FlowFuse installer will create a default stack using the latest stable release of Node-RED. The stacks are stored under `/opt/flowforge/var/stacks` or `c:\flowforge\var\stacks` on Windows. ### Creating a Stack When a new version of Node-RED is released, it can be added to your FlowFuse platform by creating a new stack. For a local install there are two steps required: 1. Install a new Node-RED version :br In the FlowFuse Home directory, run the provided install script. You must provide the full Node-RED version number, eg `3.0.2`, or use `latest` to install the most recent stable version. :br Linux/Mac: ```bash cd /opt/flowforge ./bin/ff-install-stack.sh 3.0.2 ``` :brWindows ```bash cd c:\flowforge bin\ff-install-stack.bat 3.0.2 ``` 2. Add the Stack into the FlowFuse Admin UI - see [Managing Stacks](https://flowfuse.com/docs/admin/introduction/#managing-stacks). ### Development Only If you are developing FlowFuse having checked it out from GitHub then you can run the following command in the repository root to install a stack: ```bash npm run install-stack --vers=3.0.2 ``` # Team Broker configuration The FlowFuse Team Broker makes use of an EMQX instance. ## Requirement - Docker ## Configuration files Create a directory where the configuration for your broker will live. Create the following three files, with their respective content, in that directory: cluster.hocon ```text authentication = [ { backend = http body { clientId = "${clientid}" password = "${password}" username = "${username}" } connect_timeout = "15s" enable = true enable_pipelining = 100 headers { content-type = "application/json" } mechanism = password_based method = post pool_size = 8 request_timeout = "5s" ssl { ciphers = [] depth = 10 enable = false hibernate_after = "5s" log_level = notice reuse_sessions = true secure_renegotiate = true verify = verify_peer versions = [ "tlsv1.3", "tlsv1.2" ] } url = "http://host.docker.internal:3000/api/comms/v2/auth" }, { backend = built_in_database bootstrap_file = "${EMQX_ETC_DIR}/auth-built-in-db-bootstrap.csv" bootstrap_type = plain enable = true mechanism = password_based password_hash_algorithm {name = plain, salt_position = disable} user_id_type = username } ] authorization { cache { enable = true excludes = [] max_size = 32 ttl = "1m" } deny_action = ignore no_match = allow sources = [ { body { action = "${action}" topic = "${topic}" username = "${username}" } connect_timeout = "15s" enable = true enable_pipelining = 100 headers { content-type = "application/json" } method = post pool_size = 8 request_timeout = "30s" ssl { ciphers = [] depth = 10 enable = false hibernate_after = "5s" log_level = notice reuse_sessions = true secure_renegotiate = true verify = verify_peer versions = [ "tlsv1.3", "tlsv1.2" ] } type = http url = "http://host.docker.internal:3000/api/comms/v2/acls" }, { enable = false path = "data/authz/acl.conf" type = file } ] } listeners { tcp { default { acceptors = 16 access_rules = [ "allow all" ] bind = "0.0.0.0:1883" enable = true enable_authn = true max_conn_rate = infinity max_connections = infinity mountpoint = "${client_attrs.team}" proxy_protocol = false proxy_protocol_timeout = "3s" tcp_options { active_n = 100 backlog = 1024 buffer = "4KB" high_watermark = "1MB" keepalive = none nodelay = true reuseaddr = true send_timeout = "15s" send_timeout_close = true } zone = default } } ws { default { acceptors = 16 access_rules = [ "allow all" ] bind = "0.0.0.0:8083" enable = true enable_authn = true max_conn_rate = infinity max_connections = infinity mountpoint = "${client_attrs.team}" proxy_protocol = false proxy_protocol_timeout = "3s" tcp_options { active_n = 100 backlog = 1024 buffer = "4KB" high_watermark = "1MB" keepalive = none nodelay = true reuseaddr = true send_timeout = "15s" send_timeout_close = true } websocket { allow_origin_absence = true check_origin_enable = false check_origins = "http://localhost:18083, http://127.0.0.1:18083" compress = false deflate_opts { client_context_takeover = takeover client_max_window_bits = 15 mem_level = 8 server_context_takeover = takeover server_max_window_bits = 15 strategy = default } fail_if_no_subprotocol = true idle_timeout = "7200s" max_frame_size = infinity mqtt_path = "/" mqtt_piggyback = multiple proxy_address_header = "x-forwarded-for" proxy_port_header = "x-forwarded-port" supported_subprotocols = "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5" validate_utf8 = true } zone = default } } } dashboard { default_password = topSecret } api_key { bootstrap_file = "/mounted/config/api-keys" } ``` acl.conf ```text {allow, {username, {re, "^dashboard$"}}, subscribe, ["$SYS/#"]}. {allow, {ipaddr, "127.0.0.1"}, all, ["$SYS/#", "#"]}. {deny, all, subscribe, ["$SYS/#"]}. {allow, all}. ``` api-keys ```text flowforge:verySecret:administrator ``` ## Starting The following docker command should be run in the directory the configuration files were stored. ```text docker run -d --rm \ -v $(pwd)/cluster.hocon:/opt/emqx/data/configs/cluster.hocon \ -v $(pwd)/api-keys:/mounted/config/api-keys \ -v $(pwd)/acl.conf:/opt/emqx/data/authz/acl.conf \ --add-host=host.docker.internal:host-gateway \ -p 1883:1883 -p 8083:8083 -p 18083:18083 --name emqx emqx/emqx:5.8.0 ``` ## Configuring FlowFuse Make sure the `broker` section of the `flowfuse.yml` is updated as follows ```text broker: url: mqtt://[::1]:1883 public_url: ws://:8083 teamBroker: enabled: true ``` ## Access to the EQMX Dashboard You can log into the EMQX Dashboard at `http://locahost:18083` Username: `admin` Password: `topSecret` # Enabling the device editor ```mermaid sequenceDiagram User->>FrontEnd: Clicks 'open editor' against device FrontEnd->>+Forge: PUT /api/v1/devices/:id/editor { tunnel: 'enable' } Forge->Forge: Generates Forge--)Device: Publishes command to establish connection with Device--)Forge: WS Connect /api/v1/devices/:id/editor/comms/:token Forge->>-FrontEnd: Returns session identifier FrontEnd->>FrontEnd: Opens /device//editor/ FrontEnd-->+Forge: Sends requests to /device//editor/** Forge--)+Device: Request proxied over WebSocket Device-->>Editor: Performs request on local Node-RED Editor-->>Device: Returns response Device-->>-Forge: Streams response back Forge-->>-FrontEnd: Streams response back User->>FrontEnd: User navigates away FrontEnd-->Forge: Node-RED WebSocket closes Note over Forge: if no active WebSockets for this device Forge--)Device: Close WebSocket ``` # Workflows A collection of sequence diagrams for key parts of the FlowFuse platform - [Login](https://flowfuse.com/docs/contribute/workflows/login) - [User Sign Up](https://flowfuse.com/docs/contribute/workflows/signup) - [Password Reset](https://flowfuse.com/docs/contribute/workflows/password-reset) - [Invite External User](https://flowfuse.com/docs/contribute/workflows/invite-external-user) - [Team Create](https://flowfuse.com/docs/contribute/workflows/team-create) - [Project Create](https://flowfuse.com/docs/contribute/workflows/project-create) - [Project States](https://flowfuse.com/docs/contribute/workflows/project-states) # Invite External User Flow ```mermaid sequenceDiagram autonumber participant UserEmail participant InvitedUser participant TeamOwner participant UI participant Runtime participant DB Note over TeamOwner: TeamOwner wants to invite an external user to a team TeamOwner->>UI: Opens Add Team Members dialog TeamOwner->>UI: Enters User email, clicks okay UI->>+Runtime: POST /api/v1/teams/:teamId/invitations Runtime->>DB: Create Invitation Runtime->>UserEmail: Send email containing link to /account/create?email={email}` Runtime->>DB: Update audit log Runtime-->>-UI: { status: 'okay' } Note over TeamOwner: TeamOwner role complete UserEmail-->>InvitedUser: Email received InvitedUser->>+UI: Opens /account/create?email={email} UI->>UI: Prefills email field of sign-up page InvitedUser->>UI: Enters details on sign-up page InvitedUser->>UI: Clicks Sign-up UI->>+Runtime: POST /account/register Runtime->>Runtime: Checks an invite exists for this email Note over InvitedUser,Runtime: Standard sign-up flow continues ``` - See also [User Sign Up](https://flowfuse.com/docs/contribute/workflows/signup) # User Login Flows This represents the login flow as of FlowFuse 1.2, that incorporates optional SSO ```mermaid sequenceDiagram participant U as User participant B as Browser participant RT as ForgeApp participant DB as Database participant IDP as IdentifyProvider U->>B: Enters username/email on sign-up page U->>B: Clicks login B->>RT: POST /account/login (username=XYZ) RT->DB: Checks username/email against list of SSO registered domains alt Email not SSO enabled RT->>B: 403:{ code: 'password_required' } B->>U: Shows password box U->>B: Enters password U->>B: Clicks login B->>RT: POST /account/login (username/password) RT->>DB: Validates username/password RT->>B: 200:{} end alt Email SSO enabled alt Username provided or Password provided RT->>B: 403:{ code:'sso_required', error:'Please login with your email' } B->>U: Prompts user to enter email not username end alt Email provided RT->>B: 403:{ code:'sso_required', redirect:'/account/login?u=' } B->>RT: GET /account/login?u= RT->>RT: passport.authenticate RT->>IDP: SAML exchange IDP->>IDP: User authentication IDP->>RT: POST RT->>DB: Verify Email against users alt Valid User RT->>DB: create session RT->>B: redirect to / else Unknown User RT->>B: redirect to / end end end ``` # Reset Password Flow ```mermaid sequenceDiagram autonumber participant UserEmail participant User participant UI participant Runtime participant DB User->>UI: Access the login page UI->>UI: Displays 'forgot pw' if `user:reset-password`=true User->>UI: Clicks 'forgot password' User->>UI: Enters their email address, clicks submit UI->>+Runtime: POST /account/forgot_password { email: } Runtime->>DB: Get user DB->>Runtime: User Runtime->>DB: Generate AccessToken { scope: 'password:reset' } Runtime->>UserEmail: Send email containing reset link Runtime-->>-UI: { status: 'okay' } UserEmail-->>User: Email received User->>UI: Opens /account/change-password/{token} User->>UI: Enters new details, clicks submit UI->>+Runtime: POST /account/reset_password/:token { password } Runtime->>DB: Validate {token} is a valid password reset token Runtime->>DB: Get the user associated with token Runtime->>DB: Change users password Runtime->>DB: Delete the token Runtime->>-UI: {status: 'okay' } UI->UI: Prompt user to login ``` # Sequence For Project Creation ```mermaid sequenceDiagram autonumber participant User participant Ui participant Runtime participant ContainerDriver participant DB participant Stripe User->>Ui: Clicks Create Project User->>Ui: Enters Project Name Ui->>Runtime: POST /projects alt billing enabled Runtime->DB: Team has Subscription DB->>Runtime: Subscription alt Valid Subscription Runtime->>Stripe: Add Project to Subscription Runtime->>ContainerDriver: create() Runtime->>ContainerDriver: start() Runtime->>Ui: { status: "okay" } Ui->>Ui: Show Project Overview alt success Stripe->>Runtime: POST /ee/billing/callback else failure Stripe->>Runtime: POST /ee/billing/callback Runtime->>ContainerDriver: stop() Runtime->>ContainerDriver: disable() Runtime->>User: email message end else no Subscription Runtime->>Ui: Failed to create project, no Billing info end else no billing Runtime->>Ui: { status: "okay" } Ui->>Ui: Show Project Overview end ``` # States - **Starting (first time)** The nr-laucher process is starting up. On container based systems this is done by creating a container - **Loading** nr-launcher pulling instance settings - **Installing** Once the nr-launcher process has started and downloaded the Instance settings it will npm install any nodes in the settings -> palette section. - **Starting (second time)** Once any nodes are installed the Node-RED process will be started, this state ends once the process can will respond to HTTP request - **Running** The normal state for an Instance - **Restarting** If the "Restart" action is triggered the nr-launcher will restart just the NR process inside the container. It will pull the latest settings data from the Forge app. A restart will also be triggered if the NR process fails to respond to 3 HTTP health checks in a row. Health checks run ever 7 seconds by default. - **Suspending** driver has been asked to suspend the Instance - **Suspended** When an instance is Suspended the Node-RED process is stopped and the nr-launcher shutdown, the container is then shutdown and removed on container based platforms - **Safe** Node-RED can be started in Safe Mode, this starts the Editor but does not run the flows. This is to allow a user to edit the flow to fix a problem. The flows are started when the flows are deployed. This is triggered if the NR process restarts more than 5 times with a run time of less than 30 seconds between each restart. - **Stopped** If the NR process continues to crash while in Safe Mode then it will be placed in to a Stopped state. The nr-launcher is still running but the NR process is not. # 2.3.0 ```mermaid stateDiagram-v2 direction TB InstanceCreated: Instance Created state "nr-launcher" as nrLauncher { direction TB LoadingSettings NPM NodeRED Safe: Safe Mode Stopped LoadingSettings --> NPM: loading NPM --> NPM: installing NPM --> NodeRED: starting NodeRED --> NodeRED: restarting NodeRED --> NodeRED: running NodeRED --> Safe: safe Safe --> Stopped: crashed } InstanceDeleted: Instance Deleted InstanceSuspended: Instance Suspended [*] --> InstanceCreated InstanceCreated --> nrLauncher : starting nrLauncher --> InstanceDeleted nrLauncher --> InstanceSuspended: suspending InstanceSuspended --> nrLauncher: starting InstanceSuspended --> InstanceSuspended: suspended InstanceDeleted --> [*] ``` # User Sign Up Flow ```mermaid sequenceDiagram autonumber participant UE as UserEmail participant US as User participant UI participant RT as Runtime participant DB US->>UI: Enters details on sign-up page US->>UI: Clicks Sign-up UI->>+RT: POST /account/register RT->>DB: Create User RT->>RT: Generate Email Verification Token par Runtime to UserEmail RT->>UE: Send email containing verification code and Runtime to UI RT-->>-UI: { status: 'okay' } and session created end UI->>UI: Show 'Check your email' page UE-->>US: Email received US->>+UI: Enters verification code US->>UI: Click "Continue" button UI->>+RT: POST /account/verify/token { token } RT->>RT: Checks {token} is for the logged in user RT->>DB: User.email_verified=true loop for each pending invite RT->>DB: Add user to team RT->>DB: Delete invite RT->>DB: Update audit log end RT-->>-US: Redirect '/' ``` # Sequence For Team Creation ```mermaid sequenceDiagram autonumber participant User participant Ui participant Runtime participant ContainerDriver participant DB participant Stripe User->>Ui: Clicks Create Team User->>Ui: Enters Team Name alt billing enabled Ui->>Runtime: POST /api/v1/teams Runtime->>DB: Create Team Runtime->>Stripe: checkout.create.session Stripe->>Runtime: Session ID Runtime->>Ui: { billingURL: "https://stripe..." } Ui->>Stripe: Redirect User->>Stripe: Enters Credit Card info alt complete Stripe->>Ui: Redirect to Ui Ui->>Ui: Show Team Overview Stripe->>Runtime: POST /ee/billing/callback Runtime->>DB: Create Subscription else abort Stripe->>Ui: Message Ui->>Runtime: DELETE /api/v1/teams/{id} end else: no billing Ui->>Runtime: POST /teams Runtime->>DB: Create Team Runtime->>Ui: { status: "okay"} Ui->>Ui: Show Team Overview end ``` # Node-RED Safe Mode When a Node-RED instance is unresponsive, for example due to an infinite loop, it can be put into Safe Mode. 1. Edit the instance's [Environment Variables](https://flowfuse.com/docs/user/envvar) 2. Add a variable called `NODE_RED_ENABLE_SAFE_MODE` to `true`. 3. Save the changes then suspend/restart the instance. When starting up in Safe Mode, Node-RED will provide access to the editor without starting the flows. You can log in to the editor, make any necessary changes and then deploy to restart the flows. Once recovered you should delete the `NODE_RED_ENABLE_SAFE_MODE` environment variable to prevent it entering Safe Mode the next time it is restarted. # Deploying Flows to the Device Agent Before you're able to deploy your flows to your Remote Instance, you will have needed to have completed these steps: 1. [Install the Device Agent on the Device](https://flowfuse.com/docs/device-agent/install/overview) - installs Node-RED and other requirements in order to communicate with FlowFuse. 2. [Register the Remote Instance with FlowFuse](https://flowfuse.com/docs/device-agent/register) - this step will have provided you with a `device.yml` file to move to your Remote Instance. 3. [Run the Device Agent](https://flowfuse.com/docs/device-agent/running) - starts the Device Agent on the Remote Instance. ## Deploying a Node-RED Snapshot to the Remote Instance From a Hosted Instance To deploy a Node-RED Snapshot to the Remote Instance: 1. [Create a snapshot](https://flowfuse.com/docs/user/snapshots#create-a-snapshot) - a point-in-time backup of the Node-RED flows and configuration. 2. [Mark that snapshot](https://flowfuse.com/docs/user/snapshots#setting-a-device-target-snapshot) as the **Remote Instance Target** snapshot. This model allows you to develop your flows in FlowFuse and only push it out to the registered Remote Instances when you're happy with what you've created. ## Starting Node-RED on the Remote Instance without deploying a snapshot A Remote Instance can be assigned to an application without a snapshot being deployed to it. In this mode, the Remote Instance will start Node-RED with a default set of flows that can be edited on the Remote Instance see [Editing the Node-RED flows on a Remote Instance that is assigned to an application](https://flowfuse.com/#editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application) below ## Editing the Node-RED flows on a Remote Instance that is assigned to an instance When running in the default of Fleet Mode, the device agent does not allow local access to the Node-RED editor. This ensures the Remote Instance is running the deployed snapshot without modification. When running on FlowFuse Cloud, or a premium licensed FlowFuse instance (with the [MQTT broker enabled](https://flowfuse.com/docs/contribute/local/#setting-up-mosquitto-optional){rel=""nofollow""}) the Remote Instance can be placed in Developer Mode that enables remote access to the editor. This can then be used to develop the flows directly on the Remote Instance and a new snapshot generated from the Remote Instance that can be deployed to other Remote Instances in the application. Whilst in Developer Mode the Remote Instance will not receive new updates from the platform when new snapshots are deployed. **Accessing the Editor** 1. Once developer mode is enabled, click the **Enable** button next to the 'Editor Access' option 2. When the editor is available, the Editor button in the header will become active and will take you to the device editor. **Creating a Remote Instance Snapshot** To create an instance snapshot from the Remote Instance use the **Create Snapshot** button in the Developer Mode options panel. You will be prompted to give the snapshot a name and description. See [Snapshots](https://flowfuse.com/docs/user/snapshots) for more information about working with snapshots. ## Editing the Node-RED flows on a Remote Instance that is assigned to an application Access to the editor is only available when: - The Remote Instance is in Developer Mode - When running on FlowFuse Cloud, or a premium licensed FlowFuse instance (with the [MQTT broker enabled](https://flowfuse.com/docs/contribute/local/#setting-up-mosquitto-optional){rel=""nofollow""} the Remote Instance can be placed in Developer Mode that enables remote access to the editor. - Local access to the editor can be enabled by defining a Username & Password in the Device Settings -> Security and enabling "Allow offline access" ![Device Allow Offline Access Settings](https://flowfuse.com/docs/device-agent/images/device-local-access.png){dataZoomable=""}*Device Allow Offline Access Settings* Whilst in Developer Mode the Remote Instance will not receive new updates from the platform. **Enabling Developer Mode** 1. Go to your team's **Remote Instances** page. 2. Select the Remote Instance you want to edit by clicking its name. 3. Click the "Developer Mode" button to enable developer mode. 4. Once enabled, Developer Mode options are available under the tab labelled "Developer Mode" on the Remote Instance page. **Accessing the Editor** 1. Once developer mode is enabled, click the **Enable** button next to the 'Editor Access' option 2. When the editor is available, the Editor button in the header will become active and will take you to the Remote Instance editor. **Creating a Remote Instance Snapshot** To create a snapshot from an application owned Remote Instance use the **Create Snapshot** button in the Developer Mode options panel. You will be prompted to give the snapshot a name and description. See [Snapshots](https://flowfuse.com/docs/user/snapshots) for more information about working with snapshots. **Auto Remote Instance Snapshots** For Remote Instances that are assigned to an application, the platform will automatically create a snapshot of the Remote Instance when it detects flows modified. This snapshot will be created with the name "Auto Snapshot - yyyy-mm-dd hh\:mm-ss". Only the last 10 auto snapshots are kept, others are deleted on a first in first out basis. **Custom Node Catalogues** For Remote Instances that want to make use of custom node catalogues, these can be configured under the Remote Instance settings page on the Palette tab **.npmrc file** Likewise for Remote Instances that need to be provided with a custom `.npmrc` file to allow access to a custom npm registry or to provide an access token this can also be set on the Remote Instance settings Palette tab ### Important Notes - Remote access to the editor requires Device Agent v0.8.0 or later. - The Web UI requires Device Agent v0.9.0 or later. - Assigning a Remote Instance to an application requires Device Agent v1.11.0 and FlowFuse v1.11.0 or later. - Snapshots of Remote Instances assigned to an application are supported in FlowFuse V1.12.0 or later. - Deploying a snapshot from a different Hosted Instance or Remote Instance to an application owned Remote Instance is supported in FlowFuse V1.13.0 or later. - When a Remote Instance is assigned to a Hosted Instance: - It must first have a snapshot applied before editor access is possible. - Disabling Developer Mode and returning to Fleet Mode will cause the Remote Instance to check in with the platform. If the Remote Instance flows have changed, it will be reloaded with the current target snapshot assigned to that Remote Instance, causing any changes made in Developer Mode to be overwritten. Therefore, it is recommended to create a snapshot of the changes before disabling Developer Mode. - When a Remote Instance is assigned to an application: - It will start with a set of default flows. - The Remote Instance will not receive any updates from the platform while in Developer Mode. - The Remote Instance must be online and connected to the platform to enable "Editor Access". - To minimise server and Remote Instance resources, it is recommended to disable "Editor Access" when not actively developing flows on a Remote Instance. - Auto snapshots were introduced in FlowFuse V2.1. - Auto snapshots are only supported for Remote Instance assigned to an application. - If an auto snapshot is set as the target snapshot for a Remote Instance or assigned to a pipeline stage, it will not be auto cleaned up meaning it is possible to have more than 10 auto snapshots. # FlowFuse Device Agent Installer ## What is the FlowFuse Device Agent Installer? The FlowFuse Device Agent Installer is a CLI tool for the FlowFuse Device Agent that automatically sets up Node.js runtime, installs the device agent package, and configures it as a system service. Additionally, it provides a simple interface for keeping the device agent up to date. The FlowFuse Device Agent Installer is the easiest way to get the FlowFuse Device Agent up and running on your remote device. ## Requirements - Linux, macOS, or Windows - Internet connection for downloading dependencies - Administrator/root privileges for system service installation ### Networking requirements Please see [Networking requirements](https://flowfuse.com/docs/device-agent/install/overview#networking-requirements). ## Use the FlowFuse Device Agent Installer to install or update the FlowFuse Device Agent ### One-line install For the fastest one-line install experience, see the [Quick Start guide](https://flowfuse.com/docs/device-agent/quickstart). ### Manual install If you prefer to install the FlowFuse Device Agent manually with the Installer, you can follow these steps: #### 1. Download the installer script: ##### Linux/macOS ```bash /bin/bash -c "$(curl -fsSL https://flowfuse.github.io/device-agent/get.sh)" ``` ##### Windows ```bash powershell -c "irm https://flowfuse.github.io/device-agent/get.ps1|iex" ``` #### 2. Install the Device Agent using One Time Code ##### Linux/MacOS ```bash ./flowfuse-device-agent-installer --otc ``` ##### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash flowfuse-device-agent-installer.exe --otc ``` ### Other installation options #### Install without One Time Code You can also install the FlowFuse Device Agent without a One Time Code by providing the `device.yml` content during interactive installation. To do this, run the installer without the `--otc` flag. ##### Linux/MacOS ```bash ./flowfuse-device-agent-installer ``` ##### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1-2 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash flowfuse-device-agent-installer.exe ``` #### Install in custom directory There is a possibility to install the Device Agent in a custom directory by using the `--dir` option. For example: ##### Linux/MacOS ```bash ./flowfuse-device-agent-installer --dir /path/to/custom/dir ``` ##### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1-3 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash flowfuse-device-agent-installer.exe --dir C:\path\to\custom\dir ``` #### Install on custom port You can configure a custom port using the `--port` flag. The service name includes the port, for example `flowfuse-device-agent-1882` for `--port 1882`. ##### Linux/MacOS ```bash ./flowfuse-device-agent-installer --port 1882 ``` ##### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1-4 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash flowfuse-device-agent-installer.exe --port 1882 ``` ## Updating components ### Node.js runtime To update bundled Node.js runtime, specify the `--update-nodejs` flag with the desired version: ```bash ./flowfuse-device-agent-installer --update-nodejs --nodejs-version 20.19.1 ``` Specifying `--update-nodejs` without a version will pick the default version defined in the installer. From the Device Agent v4 release, the installer defaults to Node.js 22. ### Device Agent To update the Device Agent package, use the `--update-agent` flag, optionally specifying the version: ```bash ./flowfuse-device-agent-installer --update-agent --agent-version 3.3.2 ``` Specifying `--update-agent` without the `--agent-version` flag will update to the latest available version. ## Troubleshooting ### Managing the Device Agent service Services are named per-port, for example `flowfuse-device-agent-1880`. On macOS, the launchd label is `com.flowfuse.device-agent-1880`. #### Linux (systemd) ```bash sudo systemctl start flowfuse-device-agent- sudo systemctl stop flowfuse-device-agent- sudo systemctl restart flowfuse-device-agent- ``` #### Linux (SysVinit) ```bash sudo service flowfuse-device-agent- start sudo service flowfuse-device-agent- stop sudo service flowfuse-device-agent- restart ``` #### Linux (OpenRC) ```bash sudo rc-service flowfuse-device-agent- start sudo rc-service flowfuse-device-agent- stop sudo rc-service flowfuse-device-agent- restart ``` #### macOS (launchd) ```bash sudo launchctl start com.flowfuse.device-agent- sudo launchctl stop com.flowfuse.device-agent- sudo launchctl kickstart -k system/com.flowfuse.device-agent- ``` #### Windows (Service Control) ```bash sc.exe start flowfuse-device-agent- sc.exe stop flowfuse-device-agent- ``` ### Check the Device Agent service status You can check the status of the Device Agent service to verify if it is running correctly or to diagnose any issues. The status command provides information about the current state of the service, including whether it is active, inactive, or failed. #### Linux (systemd) ```bash sudo systemctl status flowfuse-device-agent- ``` #### Linux (SysVinit) ```bash sudo service flowfuse-device-agent- status ``` #### Linux (OpenRC) ```bash sudo rc-service flowfuse-device-agent- status ``` #### macOS (launchd) ```bash sudo launchctl print system/com.flowfuse.device-agent- ``` #### Windows (Service Control) ```bash sc.exe query flowfuse-device-agent- ``` ### Viewing Device Agent log files Adjust the path if custom directory has been specified during installation. #### Linux/macOS: ```bash tail -f /opt/flowfuse-device/logs/flowfuse-device-agent.log ``` #### Linux (systemd): ```bash journalctl -f -u 'flowfuse-device-agent-' ``` #### Windows: ```powershell Get-Content -Path 'C:\opt\flowfuse-device\flowfuse-device-agent.log' -Wait ``` ### Error: Disk space check failed > [ERROR] Disk space check failed: insufficient disk space in temporary directory (/tmp): need at least 500.0 MB, available 490.4 MB ##### Cause: The `Disk space check failed` error indicates that the installer has detected insufficient disk space in the temporary directory. The FlowFuse Device Agent Installer requires a minimum of 500MB of free disk space in the temporary directory to ensure proper installation. This error might also appear if there is not enough space on the disk partition where the Device Agent is being installed. Make sure that the target installation directory has at least 500MB of free space available. [Adjust installation directory](https://flowfuse.com/docs/device-agent/install/device-agent-installer/#install-in-custom-directory) accordingly. ##### Solution: To fix this issue, you can try to free up some disk space by deleting unnecessary files or moving them to another location. Alternatively, you can specify a different temporary directory with sufficient space by setting proper environmental variable before running the installer. **On Linux/macOS**, set the `TMPDIR` environment variable: ```bash export TMPDIR=/path/to/existing/directory/with/sufficient/space ``` **On Windows**, you can set the `TEMP` or `TMP` environment variable: ```powershell Set TMP="C:\path\to\existing\directory\with\sufficient\space" ``` Retry installation after making these adjustments. ## Further reading For more detailed technical information about the FlowFuse Device Agent, like list of supported parameters or how to contribute, please refer to the [documentation](https://github.com/FlowFuse/device-agent/blob/main/installer/README.md){rel=""nofollow""}. ## Footnotes 1. Run `powershell -Command "Start-Process 'cmd' -Verb RunAs"` to launch an elevated command prompt window (e.g. as an admin user) [↩](https://flowfuse.com/#user-content-fnref-1){.data-footnote-backref ariaLabel="Back to reference 1" dataFootnoteBackref=""} [↩2](https://flowfuse.com/#user-content-fnref-1-2){.data-footnote-backref ariaLabel="Back to reference 1-2" dataFootnoteBackref=""} [↩3](https://flowfuse.com/#user-content-fnref-1-3){.data-footnote-backref ariaLabel="Back to reference 1-3" dataFootnoteBackref=""} [↩4](https://flowfuse.com/#user-content-fnref-1-4){.data-footnote-backref ariaLabel="Back to reference 1-4" dataFootnoteBackref=""} # Docker Install Run the Device Agent in a container. Bind-mount your `device.yml` and expose the editor port. ## Prerequisites - Docker or Docker Compose - A `device.yml` configuration from [Register your Remote Instance](https://flowfuse.com/docs/device-agent/register) ## Docker run ```bash docker run \ --mount type=bind,src=/path/to/device.yml,target=/opt/flowfuse-device/device.yml \ -p 1880:1880 \ flowfuse/device-agent:latest ``` ### Time zone Set the container time zone using the `TZ` environment variable: ```bash docker run \ -e TZ=Europe/London \ --mount type=bind,src=/path/to/device.yml,target=/opt/flowfuse-device/device.yml \ -p 1880:1880 \ flowfuse/device-agent:latest ``` ## Docker Compose ```yaml version: '3.9' services: device: image: flowfuse/device-agent:latest ports: - "1880:1880" volumes: - /path/to/device.yml:/opt/flowfuse-device/device.yml environment: - TZ=UTC ``` ## Running as a non-root user From Device Agent v4, the container no longer runs as `root`. It runs as the unprivileged `flowfuse` user (UID `2000` / GID `2000`), following least-privilege security practices. This affects bind-mounted directories: the directory the agent uses for its state must be writable by UID/GID `2000`, otherwise the agent will fail to start with a permissions error. Before upgrading an existing container to v4, update the ownership of any mounted directory so the `flowfuse` user can access it: ```bash sudo chown -R 2000:2000 /path/to/config/dir ``` Alternatively, run the container as a user of your choosing with the `--user` flag: ```bash docker run \ --user 1000:1000 \ --mount type=bind,src=/path/to/device.yml,target=/opt/flowfuse-device/device.yml \ -p 1880:1880 \ flowfuse/device-agent:latest ``` ## Verify Once running and assigned, access the Node-RED editor at `http://:1880`. ## Notes - Device Agent 4.x defaults to Node.js 22; the `latest` tag now uses Node.js 22. Node.js 20 reached end-of-life in April 2026. - Device Agent 3.x uses Node.js 18 in the base image. To stay on a specific line, use a fixed tag instead of `latest`. - For 2.x, use a fixed tag like `2.8.0` instead of `latest`. - Ensure outbound TCP 443 to `app.flowfuse.com` and `mqtt.flowfuse.cloud` and access to `https://registry.npmjs.com` unless using a module cache. See [Running with no access to npmjs.org](https://flowfuse.com/docs/device-agent/running#running-with-no-access-to-npmjsorg). # Kubernetes Install ## When to Use Each Option Running the Device Agent in Kubernetes is appropriate when devices are containerized or managed as part of a Kubernetes-based edge or infrastructure platform. Choose your deployment pattern based on how you manage device identity: - **Fixed Configuration**:br Use when the device already exists in FlowFuse and you have a `device.yml` with its credentials. One deployment maps to one device identity. - **Automatic Provisioning**:br Use when devices should register themselves at startup using a Provisioning Token. Each instance requires writable persistent storage. Any deployment on Kubernetes is going to be specific to the environment and requirements of the solution. The following examples show two common patterns for running the FlowFuse Device Agent on Kubernetes: - Fixed configuration using a static `device.yml` - Automatic provisioning using a FlowFuse Provisioning Token Choose the approach that matches how you manage device lifecycle and credentials. ## Fixed Configuration If you have an existing `device.yml` file containing a set of Device Agent credentials. ```bash kubectl create secret generic device-one-secret --from-file=device.yml=./device.yml ``` The following manifest will create a Deployment and Service for a device using the supplied Secret as its credentials ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: device-one labels: app: device-one spec: replicas: 1 # there can only be one replica as there is one configuration revisionHistoryLimit: 10 selector: matchLabels: app: device-one template: metadata: labels: app: device-one spec: containers: - name: device-one image: flowfuse/device-agent:latest ports: - containerPort: 1880 volumeMounts: - name: config mountPath: "/opt/flowfuse-device/device.yml" subPath: "device.yml" readOnly: true resources: limits: cpu: 1000m memory: 256Mi requests: cpu: 500m memory: 128Mi volumes: - name: config secret: secretName: device-one-secret --- apiVersion: v1 kind: Service metadata: name: device-one-service spec: selector: app: device-one ports: - protocol: TCP port: 1880 targetPort: 1880 ``` ## Automatic Provisioning Using a FlowFuse Provisioning Token to automatically configure a new Device Agent on deployment. Because the Device Agent will need to re-write the `device.yml` file it can no longer be stored in a Secret and a PersistentVolume must be used for each instance of the Device Agent. A Secret is used to hold the initial `device.yml` which contains the provisioning token. ```bash kubectl create secret generic device-provisioning-secret --from-file=device.yml=./device.yml ``` The following manifest will create a Deployment, Service and PVC for a device using the supplied Secret as the source of the Provisioning token. The PVC will be used to store the updated `device.yml` and the Node-RED nodes installed by the Remote Instance. ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: device-one labels: app: device-one spec: replicas: 1 # to scale to more than one instance you should modify this to use a StatefulSet revisionHistoryLimit: 10 selector: matchLabels: app: device-one template: metadata: labels: app: device-one spec: initContainers: # on first run copies the device.yml from Secret to PVC volume - name: config-copy image: busybox:latest command: - "/bin/sh" - "-c" - "if [ ! -f /opt/flowfuse-device/device.yml ]; then cp /tmp/device.yml /opt/flowfuse-device/device.yml; fi" volumeMounts: - name: config mountPath: "/opt/flowfuse-device" - name: initial-config mountPath: "/tmp/device.yml" subPath: "device.yml" readOnly: true containers: - name: device-one image: flowfuse/device-agent:latest ports: - containerPort: 1880 volumeMounts: - name: config mountPath: "/opt/flowfuse-device" resources: limits: cpu: 1000m memory: 256Mi requests: cpu: 500m memory: 128Mi volumes: - name: initial-config secret: secretName: device-provisioning-secret - name: config persistentVolumeClaim: claimName: device-one-pvc --- apiVersion: v1 kind: Service metadata: name: device-one-service spec: selector: app: device-one ports: - protocol: TCP port: 1880 targetPort: 1880 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: device-one-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi ``` # Manual Install (NPM) Use this method if you want direct control over the Node.js runtime and filesystem. ## Prerequisites - Node.js 18 or later installed (Node.js 22 recommended; supported versions are 18, 20, 22, and 24). Node.js 20 reached end-of-life in April 2026. - Linux, macOS, or Windows - Outbound network access to FlowFuse platform and the NPM registry ### Networking requirements Please see [Networking requirements](https://flowfuse.com/docs/device-agent/install/overview#networking-requirements). ## Install the Device Agent The Device Agent is published to npm as [@flowfuse/device-agent](https://www.npmjs.com/package/@flowfuse/device-agent){rel=""nofollow""}. ### Linux/macOS ```bash sudo npm install -g @flowfuse/device-agent ``` ### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash npm install -g @flowfuse/device-agent ``` ## Working directory By default the agent uses `/opt/flowfuse-device` (Linux/macOS) or `c:\opt\flowfuse-device` (Windows) as its working directory. Override with `-d/--dir` if needed. Ensure the directory exists and is writable by the service user. ### Linux/macOS ```bash sudo mkdir -p /opt/flowfuse-device sudo chown -R $USER /opt/flowfuse-device ``` ### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1-2 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash mkdir c:\opt\flowfuse-device icacls c:\opt\flowfuse-device /grant "user":F /T ``` Where "user" is the service account that will run the device agent (ideally, not an admin account). ## Configuration Place a `device.yml` in the working directory. See [Register your Remote Instance](https://flowfuse.com/docs/device-agent/register) for obtaining the configuration via Quick Connect or provisioning. ## Listen Port Node-RED listens on port `1880` by default. Change with `-p/--port`: ```bash flowfuse-device-agent --port 1881 ``` ## Start on system boot Use the provided systemd service file on Linux to run the agent as a service. 1. Download the service file: ```bash curl -L https://raw.githubusercontent.com/FlowFuse/device-agent/main/service/flowfuse-device.service -o flowfuse-device.service ``` 2. Adjust `User`, `Group`, and `WorkingDirectory` if needed. 3. Update `ExecStart` to include a custom port if required. 4. Move the file into place and enable the service: ```bash sudo mv flowfuse-device.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable flowfuse-device sudo systemctl start flowfuse-device ``` On Windows or macOS, consider using the [Installer](https://flowfuse.com/docs/device-agent/install/device-agent-installer) to set up services automatically. ## Verify Start the agent from a terminal to confirm it runs: ```bash flowfuse-device-agent -v ``` Once assigned, access the Node-RED editor at `http://:1880`. ## Upgrading the agent With Device Agent 1.13+, the package moved from the `@flowforge` scope to `@flowfuse`: - npm: `@flowforge/flowforge-device-agent` ➜ `@flowfuse/device-agent` ### Linux/macOS ```bash sudo npm install -g @flowfuse/device-agent@latest ``` ### Windows (run elevated[1](https://flowfuse.com/#user-content-fn-1){#user-content-fnref-1-3 ariaDescribedBy=""footnote-label"" dataFootnoteRef=""}) ```bash npm install -g @flowfuse/device-agent@latest ``` If you must stay on 2.x, use `@2.x`. Device Agent 3.x requires Node.js 18+. Device Agent 4.x defaults to Node.js 22 and supports Node.js 18, 20, 22, and 24. ## Footnotes 1. Run `powershell -Command "Start-Process 'cmd' -Verb RunAs"` to launch an elevated command prompt (e.g. as an admin user) [↩](https://flowfuse.com/#user-content-fnref-1){.data-footnote-backref ariaLabel="Back to reference 1" dataFootnoteBackref=""} [↩2](https://flowfuse.com/#user-content-fnref-1-2){.data-footnote-backref ariaLabel="Back to reference 1-2" dataFootnoteBackref=""} [↩3](https://flowfuse.com/#user-content-fnref-1-3){.data-footnote-backref ariaLabel="Back to reference 1-3" dataFootnoteBackref=""} # Installing Device Agent ## Choose your install path **Recommended for most users:** Use the Device Agent Installer (Quick Start), the fastest way to deploy with minimal configuration. Power users can choose Manual (using `npm`), Docker, or Kubernetes deployments. ### Quick install Run the one-line installer on your device. It installs Node.js, registers the device on your FlowFuse platform, and configures it to run as a local service. **Linux / macOS** ```bash /bin/bash -c "$(curl -fsSL https://flowfuse.github.io/device-agent/get.sh)" && ./flowfuse-device-agent-installer ``` **Windows** — run in an elevated PowerShell terminal: ```powershell Set-Location $env:USERPROFILE; powershell -c "irm https://flowfuse.github.io/device-agent/get.ps1 | iex"; .\flowfuse-device-agent-installer.exe ``` See the [Quick Start guide](https://flowfuse.com/docs/device-agent/quickstart) for the full walkthrough, or the [Installer reference](https://flowfuse.com/docs/device-agent/install/device-agent-installer) for all options and service management. - Recommended: Use the Device Agent Installer - Fastest way to get started with a one-line command in the [Quick Start guide](https://flowfuse.com/docs/device-agent/quickstart) - Full options and service management in the [Installer reference](https://flowfuse.com/docs/device-agent/install/device-agent-installer) - Alternative: Manual install (using `npm`) - Install the npm package, set working directory, configure, and run as a service. See [Manual install](https://flowfuse.com/docs/device-agent/install/manual) - Alternative: Docker / Docker Compose - Run the agent in a container; bind-mount the configuration. See [Docker install](https://flowfuse.com/docs/device-agent/install/docker) - Alternative: Kubernetes - Deploy the agent in a Kubernetes cluster. See [Kubernetes install](https://flowfuse.com/docs/device-agent/install/kubernetes) ## Prerequisites - Node.js 18 or later (for Manual install and for running locally). Supported versions are 18, 20, 22, and 24. Device Agent v4 (and its installer and official Docker image) defaults to Node.js 22 — this is the recommended runtime. Note that Node.js 20 reached end-of-life in April 2026. - Supported OS: Linux, macOS, Windows, or Docker container - Networking: allow outbound access on 443 to: - `app.flowfuse.com` - `mqtt.flowfuse.cloud` - Access to npm registry when snapshots are installed: {rel=""nofollow""} Note: The Device Agent downloads the required Node-RED version and any nodes specified by the assigned snapshot. Ensure firewall/proxy permits access to the npm registry or see [Running with no access to npmjs.org](https://flowfuse.com/docs/device-agent/running#running-with-no-access-to-npmjsorg). ### Networking requirements If you're working behind a firewall, and need to configure it to allow the Device Agent to connect to FlowFuse and the npm registry, see the following: Allow outbound TCP 443 to: - `app.flowfuse.com` - `mqtt.flowfuse.cloud` - `registry.flowfuse.cloud` - `registry.flowfuse.com` Ensure access to npm registry to download Node-RED and nodes: - `https://registry.npmjs.com` For offline environments, see [Running with no access to npmjs.org](https://flowfuse.com/docs/device-agent/running#running-with-no-access-to-npmjsorg). ## Verify the installation After installing by any method: 1. Ensure a working directory exists (default is `/opt/flowfuse-device` or `c:\opt\flowfuse-device`). 2. Run the agent and it will help you get it registered, or provide a device configuration you have already created (via Quick Connect, provisioning, or manual `device.yml`). See [Register your Remote Instance](https://flowfuse.com/docs/device-agent/register). 3. Start the agent (service or CLI) and open `http://:1880` when assigned and running. ## What’s next - Follow the [Quick Start guide](https://flowfuse.com/docs/device-agent/quickstart) to add and connect a Remote Instance - Learn [how to run and configure the agent](https://flowfuse.com/docs/device-agent/running) - Use [DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines) to deploy flows # FlowFuse Device Agent ![The installer running in a terminal, walking through sign-up and connection to FlowFuse](https://flowfuse.com/docs/device-agent/images/device-agent-install.gif){dataZoomable=""} ### Overview FlowFuse Device Agent allows you to remotely manage Node-RED instances running on your hardware, for example, devices on your factory floor. The Agent creates a secure connection between the FlowFuse Platform and your devices, enabling you to manage and deploy your applications. ### Get Started You can install the Device Agent on any hardware capable of running Node.js / Node-RED. The [Quick Start Guide](https://flowfuse.com/docs/device-agent/quickstart) will get the Device Agent installed and connected to FlowFuse in under 5 minutes. For more detailed information on installing and running the Device Agent, the full [installation guide](https://flowfuse.com/docs/device-agent/install/overview) has you covered. # Quick Start Guide: Device Agent This guide will walk you through the process of adding a device to FlowFuse, connecting it to the platform, and deploying your Node-RED flows remotely. FlowFuse's Device Agent helps unlock the power of your devices by allowing you to manage and deploy Node-RED flows running on those devices securely and remotely. ## Before you begin You will need: - A terminal or command prompt on the device you wish to install the Agent on - A FlowFuse platform account; either on [FlowFuse Cloud](https://app.flowfuse.com){rel=""nofollow""} or a self-hosted platform. If you do not currently have a FlowFuse platform account, this guide will help you set up a trial account on FlowFuse Cloud. ## Step 1: Install the Device Agent The Device Agent Installer is a one-line command that will: - Install Node.js - Get your device registered on a FlowFuse platform - Configure it to run as a local service It is the quickest way to get started. If you already have Node.js v22+ installed, or need to customize the setup, check the [manual installation guide](https://flowfuse.com/docs/device-agent/install/manual). ##### Linux/MacOS ```bash /bin/bash -c "$(curl -fsSL https://flowfuse.github.io/device-agent/get.sh)" && ./flowfuse-device-agent-installer ``` ##### Windows 1. Open a powershell terminal with elevated permission: ```bash # From Windows Run dialog or a terminal window, enter: powershell -Command "Start-Process 'powershell' -Verb RunAs" ``` 2. Run the following command in the elevated terminal to download and run the installer: ```bash Set-Location $env:USERPROFILE; powershell -c "irm https://flowfuse.github.io/device-agent/get.ps1 | iex"; .\flowfuse-device-agent-installer.exe ``` ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} The installer checks to see if port 1880 is available to use. If it isn't, it will let you know before exiting. This is typically because you already have Node-RED running locally. You can tell the installer to configure its Node-RED to use a different port using the `--port ` argument. Pick a different port, for example `1881` and re-run the above command with `--port 1881` added to the end. ::: :: ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} By default, the installer will use `/opt/flowfuse-device` (Linux/MacOS) or `c:\opt\flowfuse-device` (Windows) as the install location. To use a different location, use the `--dir` option with the install command. For example, `--dir /path/to/custom/location`. ::: :: ## Step 2: Follow the installer prompts The installer will step you through the whole setup process. It will ask if you are registering a new instance or connecting with a One-Time Code (OTC). ##### Registering a new instance Select this option if you have not yet registered the instance on your FlowFuse platform. It will give you a URL to open in your browser where you can register the instance. Once you complete the registration, keep the browser tab open and switch back to the terminal. ##### Connecting with a One-Time Code If you have already registered the instance on FlowFuse, the platform will have given you a One-Time Code to use when installing the Device Agent. Enter the code when prompted and the Device Agent will connect. ## Step 3: Import existing flows The Agent will check common locations for existing Node-RED flows. If it finds any, it will ask if you want to import those flows into your Device Agent managed Node-RED. This makes it easy to migrate an existing Node-RED setup into a fully managed FlowFuse instance. If you want to run an entirely separate instance, you can skip this step. ## Step 4: Set up a system service The Installer will then setup a system service so the Device Agent automatically runs when the device restarts. ## Step 5: Start editing your flows Once the Device Agent is running, and Node-RED has started, you can start editing your flows. If you still have the browser window open from registering the device, it will automatically put the instance into 'Developer Mode' and open up the Node-RED editor. From there you can start building your applications. ## Deploy Flows to Remote Instances There are two approaches to deploying flows to your Remote Instances. - **Developer Mode**: This mode allows you to edit and deploy flows directly from the FlowFuse platform. - **DevOps Pipelines**: FlowFuse provides [DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines) as a way of pushing flows from one Hosted Instance/Remote Instance to another (or several in the case of [Device Groups](https://flowfuse.com/docs/user/device-groups)). This is the recommended approach if you're pushing from development environments (e.g. remote test instances) out to remote production instances. ### Developer Mode 1. Navigate to **Applications** and select the application your device was added to. 2. Go to the **Remote Instances** tab within the application. 3. Locate your newly added device and **click** on your Remote Instance. 4. Then Click **Developer Mode** toggle button on upper right. :br![The "Developer Mode" toggle button available on the Device screen](https://flowfuse.com/docs/device-agent/images/developer.png){dataZoomable=""}*The "Developer Mode" toggle button available on the Device screen* 5. This will enable editor access for your device. :br![The "Device Editor" button available on the Device screen](https://flowfuse.com/docs/device-agent/images/editorEnabled.png){dataZoomable=""}*The "Device Editor" button available on the Device screen* 6. Clicking **Device Editor** will launch the editor. :br![Screenshot of a Node-RED Editor for a Device](https://flowfuse.com/docs/device-agent/images/nr_editor.png){dataZoomable=""}*Screenshot of a Node-RED Editor for a Device* ### DevOps Pipelines ![Screenshot showing the user interface for creating and running DevOps Pipelines in FlowFuse](https://flowfuse.com/docs/device-agent/images/ui-devops-pipelines.png){width="750"}{data-zoomable} *Screenshot showing the user interface for creating and running DevOps Pipelines in FlowFuse* To work with Pipelines, you need at least one other Hosted Instance or Remote Device to push *from*/*to*. You can follow the instructions on setting up a Pipeline and deploying your flows between Hosted Instances/Remote Instances [DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines). ## Next Steps Now you have a device connected to the platform, there are many features you can use to manage and monitor your Remote Instances. Here are a few to get you started: - [Snapshots](https://flowfuse.com/docs/user/snapshots) - [Pipelines](https://flowfuse.com/docs/user/devops-pipelines) - [Environment Variables](https://flowfuse.com/docs/user/envvar) - [Logs](https://flowfuse.com/docs/user/logs) # Register your Remote Instance To connect your hardware to FlowFuse, you will need to: 1. Install the Device Agent on your hardware 2. Add a "Remote Instance" to FlowFuse, via the FlowFuse UI 3. Connect your hardware to FlowFuse by configuring the Device Agent The best configuration to use will depend on how many Remote Instances you want to connect: - **[Single Instance Registration](https://flowfuse.com/#single-remote-instance-registration)**: for connecting a single Remote Instance, or a small number of Remote Instances, to the platform. - **[Bulk Registration](https://flowfuse.com/#bulk-registration)**: for setting up one or more Remote Instances which will automatically register themselves to the platform when the device agent is run. ## Single Remote Instance Registration For a single Remote Instance, or small batch of Remote Instances, you can manually register each Remote Instance individually, naming each Remote Instance and assigning it to an application. ### Add Remote Instance 1. Go to your team's **Remote Instances** page. 2. Click the **Add Remote Instance** button. 3. You will be prompted to give the Remote Instance a **Name**, an optional **Type** and to choose which **Application**, if any, the Instance should be assigned to. - ![](https://flowfuse.com/docs/device-agent/images/add_remote_instance.png){width="500"} - The **Type** field can be used to record additional meta information about the Remote Instance. - If you do not wish to assign the Remote Instance to an **Application** at this time, you can do so later. 4. Click **Add** ### Connect Hardware to FlowFuse Once the Remote Instance has been added in the FlowFuse UI, you will be shown the **Device Agent Configuration** dialog which contains all the information needed to connect your hardware to the FlowFuse platform. #### Quick Connect By default, you are offered the [Setup command](https://flowfuse.com/#quick-connect) method that was introduced in FlowFuse V2.1, and provides a one-time passcode to automatically connect the hardware to FlowFuse. ![](https://flowfuse.com/docs/device-agent/images/config_yml1.png){width="500px"} Running this command on hardware with the Device Agent installed will automatically configure it and connect it to FlowFuse. #### Manual Setup For older versions of the device agent, you can expand the **Manual Setup** section and use the configuration data with the [Device Agent Web UI](https://flowfuse.com/#device-agent-web-ui) or the [Manual Download](https://flowfuse.com/#manual-download) methods instead. Repeat these steps for each Remote Instance you want to connect to the platform. ## Bulk Registration If you have dozens, or hundreds of pieces of hardware to connect, you can use the **Device Provisioning Configuration** method. This approach provides you with a single "Provisioning Token" for all of your Remote Instances. When passed to the `flowfuse-device-agent`, this token will automatically register your Remote Instances with the relevant instance or application. There is no need to "Add Remote Instance" each time, as is the case with the [Single Device Registration](https://flowfuse.com/#single-remote-instance-registration) method. ### Generating a "Device Provisioning Configuration" 1. Go to your **Team Settings** page. 2. Open the **Provisioning** tab. 3. Click the **Add Token** button. 4. Enter a value for **Token Name** 5. Optionally, choose whether the device should Auto Assign to an **Instance**, an **Application**, or be left unassigned. - Select an **Instance** if you want the device to be automatically assigned to an instance. - Select an **Application** if you want the device to be automatically assigned to an application. 6. Click **Create** ![Screenshot of a FlowFuse Remote Instance Provisioning Token](https://flowfuse.com/docs/device-agent/images/create-provisioning-token.png){dataZoomable=""} Once the Provisioning Token has been created, you will be shown the **Device Provisioning Configuration** dialog: ![Screenshot of a FlowFuse Remote Instance Provisioning Token](https://flowfuse.com/docs/device-agent/images/provisioning-token.png){dataZoomable=""} **IMPORTANT:** This is the only time the platform will show you this information. Make sure to take a copy or use the **Download** button to save the configuration file locally. Once the token is created, it will be shown in the list of tokens on the **Provisioning** tab. ![Screenshot of FlowFuse Remote Instance Provisioning Tokens](https://flowfuse.com/docs/device-agent/images/provisioning-tokens.png){dataZoomable=""} ## Connecting your Hardware ### Install the configuration The Device Agent requires information about the FlowFuse Platform, and how to connect. This comes in the form of a **Device Configuration** file or a **Device Provisioning Configuration** file present in its working directory There are three methods by which you can get this configuration onto your hardware: - **[Quick Connect](https://flowfuse.com/#quick-connect):** Copy the Setup Command (with one-time passcode) and run it in a terminal window on the hardware. Your hardware will then automatically configure itself. - **[Device Agent Web UI](https://flowfuse.com/#device-agent-web-ui):** Copy the configuration file (`device.yml`) to your hardware using its built in Web UI. - *The Device Agent must be running and the [command line flag](https://flowfuse.com/docs/device-agent/running#device-agent-command-line-options) for the Web UI must be enabled.* - **[Manual Download](https://flowfuse.com/#manual-download):** Download the configuration file directly into the hardware's [Working Directory](https://flowfuse.com/docs/device-agent/install/manual#working-directory). ### Methods #### Quick Connect The Quick Connect method was introduced in FlowFuse v2.1. This is the fastest way to connect your hardware to the platform. When registering your hardware you would have been presented the following dialog, with a one-time-passcode that the Device Agent can use to retrieve its configuration: ![](https://flowfuse.com/docs/device-agent/images/config_yml1.png){style="margin: auto;" width="500px"} If you're no longer able to see that dialog, you can regenerate the configuration by following the [Regenerating Configurations](https://flowfuse.com/#regenerating-configurations) steps, or clicking **"Finish Setup"** on the Remote Instance's page: ![](https://flowfuse.com/docs/device-agent/images/finish-setup.png){style="margin: auto; margin-bottom: 12px;" width="750px"} When the quick connect command has been run, the terminal window will report that the Remote Instance has connected to the platform and will output a new `command` for you to use to start the Remote Instance agent with the new configuration. NOTES - The Setup command is only valid for 24h. If you do not use it within this time, you will need to [regenerate](https://flowfuse.com/#regenerating-configurations) it. - The 3 word One-Time-Code (OTC) contained in the Setup command is single use and is deleted immediately upon use. #### Device Agent Web UI ![](https://flowfuse.com/docs/device-agent/images/device_gui.png){style="margin: auto; margin-bottom: 12px;" width="550px"} If the Device Agent is running [with the Web UI enabled](https://flowfuse.com/docs/device-agent/running#device-agent-command-line-options), you can download the configuration file to the Remote Instance using the Web UI. This is useful if you don't have direct access to the Remote Instance's file system. Once the configuration file is downloaded, the device agent will automatically restart and load the configuration. #### Manual Download Place the **Device Configuration** or **Device Provisioning Configuration** file onto your hardware. in the [Working Directory](https://flowfuse.com/docs/device-agent/install/manual#working-directory) By default, the device agent expects the configuration file to be named `device.yml`, if not, you will need to start the device agent with the `-c` [Command Line Option](https://flowfuse.com/docs/device-agent/running#device-agent-command-line-options) and specify the path of the configuration file. The agent can then be started with the command: [^global-install] ```bash flowfuse-device-agent ``` You will see the Device Agent start and perform a 'call-home' where it connects back to the platform to check what it should be running. #### Additional Information If you copy or download a **Device Provisioning Configuration** file to your hardware, you will see the Device Agent start and perform a 'call-home' where it connects back to the platform to auto register itself in the Team's Remote Instances. If successful, the real **Device Configuration** is generated and downloaded to the device. The original **Provisioning Configuration** will be overwritten meaning subsequent runs will not need to perform the auto registration again. ## Assign the Remote Instance The next step is to assign the device to a Node-RED instance or application. Note, that if you've followed [Single Device Registration](https://flowfuse.com/#single-remote-instance-registration) or [Bulk Registration](https://flowfuse.com/#bulk-registration) to register your device, it will automatically be assigned to an Application or Instance. ### Applications This step will permit you to push Snapshots to your Remote Instance via [DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines), or via a [Target Snapshot](https://flowfuse.com/docs/user/snapshots/#application-owned-devices) from the Application. #### Assign to Application 1. Go to your team's **Remote Instances** page. 2. Open the dropdown menu to the right of the Remote Instance you want to assign and select the **Add to Application** option. 3. Select the application in the dialog and click **Add** to continue. #### Remove from Application To remove the Remote Instance from an application: 1. Go to your team's **Remote Instances** page. 2. Open the dropdown menu to the right of the Remote Instance you want to remove and select the **Remove from Application** option. 3. Confirm the action by clicking the **Remove** option. The Remote Instance will stop running the current Node-RED flows. It will then wait until it is assigned to another application or instance. #### Bulk Assigning Remote Instances to an Application If you have a large number of Remote Instances to assign to an application, you can do them all at once: 1. Select the Remote Instances you want to assign and open the **Actions** dropdown menu. 2. Select the **Move to Application** option and then select the application or instance you want to assign the Remote Instances to. 3. Click **Move** to continue. ##### Details: - Remote Instances that are already assigned to the chosen application will not be changed or updated. - For any Remote Instance that is moved by the operation: - Remote Instances moved by the operation will have their target snapshot cleared, device group membership cleared and will be sent an update command. - Remote Instances in Fleet Mode will automatically apply the changes resulting in Remote Instances restarting with the basic starter snapshot flows. - Remote Instances in [Developer Mode](https://flowfuse.com/docs/device-agent/quickstart/#developer-mode) will continue to run their current flows until they are switched to fleet mode at which point they will update accordingly. NOTE: If you wish to keep the flows currently running on the Remote Instance, it must be in developer mode at the time of operation. Once the Remote Instance is moved, create a new snapshot with the "Set as Target" option checked. ### Hosted Instances This method establishes a deployment relationship where a Hosted Instance becomes the source for snapshot deployments to your Remote Instance(s). **Important:** This is a legacy feature; [assigning to Applications](https://flowfuse.com/docs/device-agent/register#applications) is the recommended approach as it provides better fleet management and DevOps Pipeline capabilities. For guidance, see [when to use each approach](https://flowfuse.com/docs/user/concepts#when-to-use-instance-assignment-vs-devops-pipelines). #### Assign to Hosted Instance 1. Go to your team's **Remote Instances** page. 2. Open the dropdown menu to the right of the Remote Instance you want to assign and select the **Add to Instance** option. 3. Select the Hosted Instance in the dialog and click **Add** to continue. **Note:** There are constraints on which instances can be assigned to each other. For detailed information, refer to [Assignment Rules and Constraints](https://flowfuse.com/docs/user/concepts#assignment-rules). ### Remove from Hosted Instance To remove the Remote Instance from a Node-RED instance: 1. Go to your team's **Remote Instances** page. 2. Open the dropdown menu to the right of the Remote Instance you want to remove and select the **Remove from Hosted Instance** option. 3. Confirm the action by clicking the **Remove** option. The Remote Instance will stop running the current Node-RED flows. It will then wait until it is assigned to another Hosted Instance or Application ### Bulk Assigning Remote Instances to a Hosted Instance If you have a number of Remote Instances to assign to an instance, you can do them all at once. 1. Select the Remote Instances you want to assign and open the **Actions** dropdown menu adjacent to the **Add Remote Instance** button. 2. Select the **Move to Instance** option and then select the instance you want to assign the Remote Instances to. 3. Click **Move** to continue. #### Details: - Remote Instances that are already assigned to the chosen instance will not be changed or updated. - For any Remote Instance that is moved by the operation: - If the chosen instance has a target snapshot set, the newly assigned Remote Instances will inherit this. - If the chosen instance does not have a target snapshot set, newly assigned Remote Instances will have their target snapshot cleared. - Remote Instances in fleet mode will automatically apply the changes. - Remote Instances in developer mode will continue to run their current flows until they are switched to fleet mode at which point they will update accordingly. ### Bulk Removing Remote Instances from an Application or Instance If you have a number of Remote Instances to remove from an application or instance, you can do them all at once. 1. Select the Remote Instances you want to remove and open the **Actions** dropdown menu adjacent to the **Add Remote Instance** button. 2. Select the **Unassign**option. 1. Depending on where you are viewing Remote Instances, this may say "Remove from Application" or "Remove from Instance". 3. Confirm the action by clicking **Unassign**. #### Details: - Remote Instances that are already unassigned will not be changed or updated. - For any Remote Instance that was previously assigned to an application or instance: - Remote Instances in developer mode will be switched to fleet mode. - Remote Instances will have their target snapshot and Remote Instance group membership cleared. - Remote Instances will be informed of the changes resulting in their flows being cleared and the Remote Instance entering a stopped state waiting for a new assignment. ## Regenerating Configurations To regenerate Remote Instance configurations: 1. Go to your team's or instance's **Remote Instances** page. 2. Open the dropdown menu to the right of the Remote Instance and select the **Regenerate Configuration** option. 3. You will need to confirm this action as the existing configuration will be immediately revoked. If the Remote Instance tries to use the old configuration it will fail to connect and will delete its local copy of the snapshot it was running. Click **Regenerate Configuration** to continue. You will then be shown the **Remote Instance Configuration** dialog again with a new setup command and the manual configuration to copy or download. ## Deleting a Remote Instance To delete a Remote Instance: 1. Go to your team's or instance's **Remote Instances** page. 2. Open the dropdown menu to the right of the Remote Instance and select the **Delete Remote Instance** option. 3. Confirm the action by clicking the **Delete** option. The next time the Remote Instance attempts to connect to the platform it will find it is no longer authorised and will stop and delete its local copy of the flows it was running. ## Node-RED Settings Most Node-RED settings are managed by the platform as part of deploying an instance to the Remote Instance. However some settings can be overridden locally on the Remote Instance. ### HTTPS configuration *Available in Device Agent 0.10+* The `https` configuration option in `device.yml` can be used to enable HTTPS within Node-RED. The values are passed through to the [Node-RED `https` setting](https://nodered.org/docs/user-guide/runtime/configuration){rel=""nofollow""}. The `ca`, `key` and `cert` properties can be used to provide custom certificates and keys. The values should be set to the contents of the certificate/key. Alternatively, the properties `caPath`, `keyPath` and `certPath` can be used instead to provide absolute paths to files containing the certificates/keys. ```yml [device.yml] https: keyPath: /opt/flowfuse-device/certs/key.pem certPath: /opt/flowfuse-device/certs/cert.pem caPath: /opt/flowfuse-device/certs/ca.pem ``` ### `httpStatic` configuration *Available in Device Agent 0.10+* This option can be used to serve content from a local directory. If set to a path, the files in that directory will be served relative to `/`. ```yml [device.yml] httpStatic: /opt/flowfuse-device/static-content ``` It is also possible to configure it with a list of directories and the corresponding path they should be served from. ```yml [device.yml] httpStatic: - path: /opt/flowfuse-device/static-content/images root: /images - path: /opt/flowfuse-device/static-content/js root: /js ``` ### `localAuth` configuration *Available in Device Agent 3.20+* This option can be used to enable local login for the Node-RED editor. This option is not recommended for day to day use. It can be configured under the Remote Instance's Settings ![Local Auth settings for Remote Instance](https://flowfuse.com/docs/device-agent/images/device-local-access.png){dataZoomable=""}*Local Auth settings for Remote Instance* Or by adding the following to the `device.yml` file ```yml [device.yml] localAuth: enabled: true user: user-name pass: $hashed-password ``` NOTE: The password should be hashed version generated using the Node-RED admin CLI. For example: ```bash node-red admin hash-pw ``` Alternatively, the password can be set in FlowFuse on the settings -> security tab of the Remote Instance. ## Troubleshooting If you have problems with the device agent the first thing to do is to enable the verbose logging mode. To do this add a `-v` to the command line. This will present a lot more information about what the agent is doing. It will show that is has connected to the FlowFuse instance and every time it checks in, it will also log all the local HTTP requests made when accessing the Node-RED Editor via the FlowFuse application. # Running the Device Agent ## Running If the agent was installed as a global npm module, the command `flowfuse-device-agent` will be on the path. If the default working directory and config file are being used, then the agent can be started with: ```bash flowfuse-device-agent ``` By default, Node-RED will listen to port `1880`, you can change it using the options detailed [here](https://flowfuse.com/docs/device-agent/install/manual#listen-port). This will start the agent, set the Remote Instance in the default of fleet mode, and connect to FlowFuse, waiting until a Target Snapshot has been assigned to it, or it is assigned to an Application. ### When assigned to an instance: Once the agent has been assigned a Target Snapshot, it will download the Snapshot and deploy it to the Remote Instance. ### When assigned to an application: Once the agent has been assigned to an application it starts up. If the device is new, it will get a default set of flows which can be edited directly. See [Editing the Node-RED flows on a Remote Instance that is assigned to an application](https://flowfuse.com/docs/device-agent/deploy#editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application) for details. ### Device Agent Command Line Options The following command line options are available: ```text Options -c, --config file Device configuration file. Default: device.yml -d, --dir dir Where the agent should store its state. Default: /opt/flowfuse-device -i, --interval secs -p, --port number -m, --moduleCache Use local npm module cache rather than install --node-options string Node.js command-line options to pass to the Node-RED process. Can be specified multiple times. You must use `=` between the option and its value. Web UI Options -w, --ui Start the Web UI Server (optional, does not run by default) --ui-host string Web UI server host. Default: (0.0.0.0) (listen on all interfaces) --ui-port number Web UI server port. Default: 1879 --ui-user string Web UI username. Required if --ui is specified --ui-pass string Web UI password. Required if --ui is specified --ui-runtime mins Time the Web UI server is permitted to run. Default: 10 Setup command -o, --otc string Setup device using a one time code --otc-no-start Do not start the agent after setup --otc-no-import Do not ask to import Node-RED flows during setup -u, --ff-url url URL of FlowFuse. Required for setup Global Options -h, --help print out helpful usage information --version print out version information -v, --verbose turn on debugging output --log-format json output logs in JSON format ``` ### Command Line Examples *Start the agent with a different port number* ```bash flowfuse-device-agent -p 8080 ``` *Start the agent with a different working directory and the Web UI enabled* ```bash flowfuse-device-agent -d /path/to/working/directory -w --ui-user admin --ui-pass password --ui-port 8081 ``` ## Structured JSON logging By default the Device Agent prints plain-text logs, which can be hard to parse with log aggregation tools. From Device Agent v4, you can output machine-readable, structured logs in JSON format using the `--log-format json` flag: ```bash flowfuse-device-agent --log-format json ``` ## Configuring Node.js Options Node.js command-line arguments can be passed to the Node-RED process started by the Device Agent. This is useful for memory-intensive workflows or certificate management. ### Via command line Use `--node-options` — it can be specified multiple times: ```bash # Set a custom heap size limit flowfuse-device-agent --node-options='--max-old-space-size=256' # Enable system certificate authorities (Linux) flowfuse-device-agent --node-options='--use-openssl-ca' # Enable system certificate authorities (Windows/macOS) flowfuse-device-agent --node-options='--use-system-ca' # Combine multiple options flowfuse-device-agent --node-options='--max-old-space-size=256' --node-options='--use-openssl-ca' ``` ### Via `device.yml` Add a `nodeOptions` array to the `device.yml` configuration file: ```yaml [device.yml] nodeOptions: - "--max-old-space-size=256" - "--use-openssl-ca" ``` ## Running behind a HTTP Proxy If the Remote Instance is behind a HTTP proxy, the agent can be configured to use the proxy by setting the `http_proxy`, `https_proxy` or `all_proxy` environment variables. If necessary, the `no_proxy` environment variable can be used to specify a list of hosts that should not be accessed via the proxy. For connecting to FlowFuse Cloud, the `https_proxy` variable should be set to your proxy URL. This environment variable will be used by the agent for both the HTTP and MQTT connections. ### Example setting the proxy environment variables on Linux ```bash # Set the https_proxy environment variable export https_proxy=http://my-proxy:3128 # Set the no_proxy environment variable to exclude local addresses and all hosts in the .mydomain.com domain export no_proxy=localhost,127.0.0.1,.mydomain.com # Start the agent flowfuse-device-agent ``` *To make these settings permanent, see the documentation for your Linux distribution.* ### Example setting the proxy environment variables on Windows ```bash # Set the https_proxy environment variable set https_proxy=http://my-proxy:3128 # Set the no_proxy environment variable to exclude local addresses and all hosts in the .mydomain.com domain set no_proxy=localhost,127.0.0.1,.mydomain.com # Start the agent flowfuse-device-agent ``` *To make these settings permanent, see the documentation for your version of Windows.* ## Running with no access to npmjs.org By default, the Device Agent will try and download the correct version of Node-RED and any nodes required to run the Snapshot that is assigned to run on the Remote Instance. If the Remote Instance is being run on an offline network or security policies prevent the Device Agent from connecting to npmjs.org then it can be configured to use a pre-cached set of modules. You can enable this mode by adding `-m` to the command line or adding `moduleCache: true` to the `device.yml` file. This will cause the Device Agent to load the modules from the `module_cache` directory in the Device Agent's [Working Directory](https://flowfuse.com/docs/device-agent/install/manual#working-directory) (or whatever is set with the `-d` option) (e.g. `/opt/flowfuse-device/module_cache`.). ### Creating a module cache To create a suitable module cache, the device must be assigned to a Remote Instance. You will need to install the modules on a local device with access to npmjs.org, ensuring you use the same OS and Architecture as your target device, and then copy the modules on to your Remote Instance. 1. From the Snapshot page, select the snapshot you want to deploy and select the option to download its `package.json` file. 2. Place this file in an empty directory on your local device. 3. Run `npm install` to install the modules. This will create a `node_modules` directory. 4. On your target Remote Instance, create a directory called `module_cache` inside the Device Agent Configuration directory. 5. Copy the `node_modules` directory from your local instance to the target instance so that it is under the `module_cache` directory. # ctrlX Device Agent App **Currently not available - will be available soon** ## Installation Procedure 1. In the ctrlX CORE web interface, navigate to the window **Settings** ➔ **Apps**. 2. Switch the ctrlX device to the **Service** mode. 3. In the app overview, navigate to the category **Available apps**. This category displays all apps saved in the app storage on the ctrlX device and all apps provided via the ctrlX Store. 4. Search for the FlowFuse Device Agent ctrlX App to be installed and click on the Installation button. If multiple app versions are provided for installation, a list of available versions and app sources will be displayed. In this case, select the desired app version from the list to start the installation. If only one app version is provided, the installation will start directly. After the installation, the app will be shown in the app overview, under the category **Installed apps**. 5. Switch the ctrlX device back to the **Operating** mode. ## Device Agent Configuration for ctrlX 1. After successful installation, [generate and download the "Device Credentials" in FlowFuse](https://flowfuse.com/docs/device-agent/register/#manual-setup) 2. In the ctrlX CORE web interface, navigate to the window **Settings** ➔ **Apps** ➔ **Manage App Data** 3. Select the folder "FlowFuse Device Agent" 4. Click on upload file and select the **device.yml** configuration from step 1 5. The Device Agent connects automatically to your FlowFuse instance. Ensure your ctrlX has a network connection to your FlowFuse platform (e.g. FlowFuse Cloud) and to the npmjs registry. # ctrlX Node-RED App Learn more about the Node-RED App in the [ctrlX World Store](https://developer.community.boschrexroth.com/t5/Store-and-How-to/FlowFuse-Node-RED/ba-p/82135){rel=""nofollow""} ## Installation Procedure Follow these steps to install the Rexroth CtrlX App by FlowFuse on your ctrlX device: 1. In the ctrlX CORE web interface, navigate to the window **Settings** ➔ **Apps**. 2. Switch the ctrlX device to the **Service** mode. 3. In the app overview, navigate to the category **Available apps**. This category displays all apps saved in the app storage on the ctrlX device and all apps provided via the ctrlX Store. 4. Search for the `Rexroth CtrlX App` to be installed and click on the Install button. If multiple app versions are provided for installation, a list of available versions and app sources will be displayed. In this case, select the desired app version from the list to start the installation. If only one app version is provided, the installation will start directly. After the installation, the app will be shown in the app overview, under the category **Installed apps**. 5. Switch the ctrlX device back to the **Operating** mode. ## Start and Login 1. Open the Flow Editor from the Node-RED menu in your ctrlX. 2. **Log in using your ctrlX username and password**. 3. A successful login requires a valid license and user permission. 4. Create your Node-RED flow. # Device Agent - Hardware The FlowFuse Device Agent can be installed on any Mac, Linux, or Windows-supported device. If you want to learn more about how to install the Device Agent in general, [click here](https://flowfuse.com/docs/device-agent/install/overview). Nevertheless, this overview provides a guide for specific hardware platforms. ## Official FlowFuse Hardware Partners [![](https://upload.wikimedia.org/wikipedia/commons/0/0d/Logo_of_Bosch_Rexroth_AG.svg){height="75" width="150"}](https://developer.community.boschrexroth.com/t5/Store-and-How-to/FlowFuse-Node-RED/ba-p/82135) - [ctrlX - Node-RED App](https://flowfuse.com/docs/hardware/ctrlx-node-red) - [ctrlX - Device Agent App](https://flowfuse.com/docs/hardware/ctrlx-device-agent) - will be available soon ## Other Hardware Guides [![](https://upload.wikimedia.org/wikipedia/de/c/cb/Raspberry_Pi_Logo.svg){height="30" width="80"}](https://flowfuse.com/docs/hardware/raspbian) - [Raspberry Pi](https://flowfuse.com/docs/hardware/raspbian) # Raspberry Pi The Raspberry Pi section provides instructions for installing and setting up the FlowFuse Device Agent on your Raspberry Pi device, enabling seamless integration for efficient device management and automation. ## Installing the Device Agent FlowFuse provides a script to install Node.JS, npm, and the FlowFuse Device Agent onto a Raspberry Pi. This script won't work on ARMv6 builds as the standard Node.JS builds don't support it, as result Pi Zero's are not supported. ```sh bash <(curl -sL https://raw.githubusercontent.com/FlowFuse/device-agent/main/service/raspbian-install-device-agent.sh) ``` **This script will:** 1. Detect if Node.js is already installed, it will ensure it is at least v14. If less than v14 it will stop. If nothing is found it will install the Node.js 18 LTS release 2. Install the latest version of the FlowFuse Device Agent using npm. 3. Setup the FlowFuse Device Agent to run as a service ## Running as a service You can run the device agent as a service, which means it can run in the background and be enabled to automatically start on boot. The install script will automatically set up the FlowFuse Device Agent to run as a service. The following commands can be useful for controlling the service or changing the default service settings. ### Starting the service on boot (optional) If you want Node-RED to run when the device is turned on, or re-booted, you can enable the service to autostart by running the command: `sudo systemctl enable flowfuse-device-agent.service` To disable the service, run the command: `sudo systemctl disable flowfuse-device-agent.service` ### Controlling the service You can start the service with the command: `sudo systemctl start flowfuse-device-agent` You can check the current status with the command: `sudo systemctl status flowfuse-device-agent` You can stop your with the command: `sudo systemctl stop flowfuse-device-agent` # FlowFuse Documentation Welcome to the documentation for FlowFuse, an open-source, industrial data platform that enables engineers to build, manage, scale, and secure their Node-RED solutions. It covers everything from setup, to usage, and development. All [contributions](https://flowfuse.com/docs/contribute/introduction/) are welcome. ## Getting Started ::div{.ff-offering-tiles.grid-cols-1.sm:grid-cols-2} :::div{.ff-tile.ff-offering-tile} FlowFuse Self-Hosted Run FlowFuse yourself on your own infrastructure. - [Quick Start Instructions :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/quick-start/) - [Install FlowFuse Self-Hosted :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/install/introduction/) - [Upgrade Your FlowFuse Instance :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/upgrade) - [Unlock Enterprise Features :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/upgrade/open-source-to-premium/) - [Administering FlowFuse :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/admin/introduction/) ::: :::div{.ff-tile.ff-offering-tile} FlowFuse Cloud Hosted solution, nothing to install anything, jump straight in. - [Sign Up for Free :icon-chevron-right{.ff-icon.ff-icon-sm}](https://app.flowfuse.com/account/create) - [Upgrading Teams :icon-chevron-right{.ff-icon.ff-icon-sm}]() - [Billing :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/cloud/billing/) - [Single Sign On :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/cloud/introduction/#single-sign-on) ::: :: ## Using FlowFuse ### Getting Started Here are some quick reference links to our most popular topics. You can also view the full documentation available for FlowFuse in our [Getting Started](https://flowfuse.com/docs/user/introduction/){rel=""nofollow""} guide. ::div{.ff-product-feature-tiles.grid-cols-1.md:grid-cols-2} :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/instance-settings/){.ff-tile.ff-product-feature-tile} ::: :::div [Configuring Instances [Understand the options available for your Node-RED Instances]](https://flowfuse.com/docs/user/instance-settings/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/snapshots/){.ff-tile.ff-product-feature-tile} ::: :::div [Snapshots [Version Control for your Node-RED Instances.]](https://flowfuse.com/docs/user/snapshots/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/shared-library/){.ff-tile.ff-product-feature-tile} ::: :::div [Team Library [Centralized management of re-usable flows and functions.]](https://flowfuse.com/docs/user/shared-library/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/snapshots/){.ff-tile.ff-product-feature-tile} ::: :::div [Pipelines [Deploy flows between Test, Staging & Production Environments.]](https://flowfuse.com/docs/user/snapshots/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/device-groups/){.ff-tile.ff-product-feature-tile} ::: :::div [Remote Instances [Deploy flows remotely with FlowFuse "Devices".]](https://flowfuse.com/docs/user/device-groups/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/device-groups/){.ff-tile.ff-product-feature-tile} ::: :::div [Device Groups [Manage large numbers of devices together.]](https://flowfuse.com/docs/user/device-groups/){.ff-tile.ff-product-feature-tile} ::: :: ### Advanced Features Once you're more comfortable with FlowFuse, you may want to explore some of our more advanced features that will help elevate your Node-RED experience even further. ::div{.ff-product-feature-tiles.grid-cols-1.md:grid-cols-2} :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/team/){.ff-tile.ff-product-feature-tile} ::: :::div [Managing Teams [Host your Instances at a custom subdomain]](https://flowfuse.com/docs/user/team/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/custom-hostnames/){.ff-tile.ff-product-feature-tile} ::: :::div [Custom Domains [Host your Instances at a custom subdomain]](https://flowfuse.com/docs/user/custom-hostnames/){.ff-tile.ff-product-feature-tile} ::: :::div{.ff-product-feature-tile-decorator} [](https://flowfuse.com/docs/user/device-groups/){.ff-tile.ff-product-feature-tile} ::: :::div [High Availability [Automatically distribute workload across multiple copies of your Node-RED Instance.]](https://flowfuse.com/docs/user/device-groups/){.ff-tile.ff-product-feature-tile} ::: :: ## FlowFuse Extras ::div{.ff-offering-tiles.grid-cols-1.sm:grid-cols-2.lg:grid-cols-2} :::div{.ff-tile.ff-offering-tile} FlowFuse Node-RED Nodes A complete set of FlowFuse-maintained nodes for data sharing between instances, MQTT messaging, AI/ONNX models, MCP agent integrations, and enterprise data features. - [FlowFuse Project Nodes :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/user/projectnodes/) - [FlowFuse MCP Nodes :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/node-red/flowfuse/mcp/) - [FlowFuse Tables Nodes :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/node-red/flowfuse/flowfuse-tables/) - [FlowFuse AI Nodes :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/node-red/flowfuse/ai/) - [FlowFuse MQTT Nodes :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/user/mqtt-nodes/) - [See all the Nodes :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/node-red/flowfuse/) ::: :::div{.ff-tile.ff-offering-tile} FlowFuse Dashboard Create interactive, responsive and secure Dashboards in Node-RED. - [Install FlowFuse Dashboard :icon-chevron-right{.ff-icon.ff-icon-sm}](https://dashboard.flowfuse.com) - [Build Your First Dashboard :icon-chevron-right{.ff-icon.ff-icon-sm}](https://dashboard.flowfuse.com) - [Multi Tenant Dashboards :icon-chevron-right{.ff-icon.ff-icon-sm}](https://dashboard.flowfuse.com/user/multi-tenancy.html) ::: :::div{.ff-tile.ff-offering-tile} FlowFuse Device Agent Manage thousands of Node-RED Instances remotely with the FlowFuse Device Agent. - [Install the Device Agent :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/device-agent/install/) - [Registering Devices in FlowFuse :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/device-agent/register/) - [Deploying Flows to your Device :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/device-agent/deploy/) - [Editing Flows on your Device :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/device-agent/deploy/) ::: :::div{.ff-tile.ff-offering-tile} FlowFuse Assistant AI in the Node-RED Editor to help build your flows. - [Getting Started Guide :icon-chevron-right{.ff-icon.ff-icon-sm}](https://flowfuse.com/docs/user/expert/) ::: :: ## Support - [Troubleshooting](https://flowfuse.com/docs/debugging/) - [FlowFuse Cloud Support](https://flowfuse.com/docs/premium-support/) ## Contributing to FlowFuse - [Useful Information](https://flowfuse.com/docs/contribute/introduction#contributing-to-flowfuse) - Learn the foundational concepts of how FlowFuse is built & structured. - [Development Setup](https://flowfuse.com/docs/contribute/introduction#development-setup) - Configure your local development environment to contribute to FlowFuse. - [Testing](https://flowfuse.com/docs/contribute/introduction#testing) - Understand our testing philosophy at FlowFuse. # Configuring FlowFuse The base configuration of the FlowFuse platform is provided in the file `/opt/flowforge/etc/flowforge.yml`. This assumes the default install location of `/opt/flowforge`. To run a local install, you can use the default options. This section describes the options available in the configuration file. ## Server configuration | Option | Description | | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `host` | The address to serve the web ui on. This defaults to `localhost` which means the ui will only be available when browsing from the same server that is running the platform. To make it accessible to other devices on the network, set it to `0.0.0.0`. :br NOTE: If `host` is changed, please also update `base_url` to match e.g. `http://[ip-address-of-host]:3000` | | `port` | The TCP port the platform serves its web ui. Default: `3000` | | `base_url` | The url to access the platform. This defaults to `http://localhost:3000` which means a number of internally generated URLs will only work when browsing on the same device as is running the platform. To be able to access the platform remotely, replace `localhost` with the ip address of the device running FlowFuse. | | `domain` | The domain that instance names will be prepended to on Docker & Kubernetes platforms to create a hostname to access the instance. A wildcard DNS A record should point be configured to point to the FlowFuse entry IP Address. | | `support_contact` | a URL or string with contact details for the administrator e.g `mailto:support@example.com` or `https://support.example.com` . Defaults to the email address of the first admin user or `the administrator` if no email address set. | | `create_admin` | If set to `true` will create a default admin user on first run, the username/password is written to the logs. Default: `false` | | `create_admin_access_token` | If set to `true` an access token (ffpat) is created for the default admin user on first run. Its value is written to the logs. Default: `false` | | `license` | Can be used to pass in a license key for FlowFuse. Default not set | NOTE: Changing the `base_url` and `domain` after Node-RED instances have been created is possible, but the original hostname and domain must remain active in order to access the instances and for an them to be able to access the FlowFuse resources. An example workflow would be: 1. Register new domain 2. Set up DNS entries for: - A record for the forge app - wildcard A record for the domain 3. Leave the existing entries for the old domain in place 4. Stop the forge app 5. Edit the flowforge.yml to set the base\_url and domain entries 6. Restart the forge app ## Database configuration FlowFuse supports `sqlite` and `postgres` databases. | Option | Description | | --------- | ----------------------------------------------- | | `db.type` | The type of database to use. Default: `sqlite`. | ### SQLite configuration | Option | Description | | ------------ | ------------------------------------------------------------------------------------------------ | | `db.storage` | Path to the SQLite Database file to use, relative to `/opt/flowforge/var/`. Default: `forge.db`. | ### Postgres configuration | Option | Description | | ------------- | ------------------------------------------------------- | | `db.host` | Hostname of the Postgres Database. Default: `postgres`. | | `db.database` | Database name on Postgres Server. Default: `flowforge`. | | `db.user` | Username used when connecting to Postgres Server. | | `db.password` | Password used when connecting to Postgres Server. | | `db.ssl` | Client should connect with SSL/TLS. Default: `false` | ## Node-RED Driver configuration This configures how Node-RED instances are run by the platform. | Option | Description | | ------------- | ------------------------------------------------------- | | `driver.type` | The type of deployment model to use. Default: `localfs` | ### Localfs Driver options | Option | Description | | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `driver.options.start_port` | The port number to start assigning to Node-RED instances as they are created. Default: `12080` | | `driver.options.node_path` | The path to find the node.js executable - useful if Node.js has been installed with `nvm` so isn't necessarily on the system path. | | `driver.options.logPassthrough` | Prints the Node-RED logs in JSON format to stdout of the nr-launcher process. Default: `false` | | `driver.options.privateCA` | The fully qualified path to a pem file containing locally trusted CA cert chain. Default: not set | ### Docker Driver options | Option | Description | | ------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `driver.options.socket` | The path to the Docker control unix domain socket. Default `/var/run/docker.sock` | | `driver.options.logPassthrough` | Prints the Node-RED logs in JSON format to stdout of the Instance containers. Default: `false` | | `driver.options.privateCA` | The fully qualified path to a pem file on the host machine containing locally trusted CA cert chain. Default: not set | ### Kubernetes Driver options | Option | Description | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `driver.options.namespace` | The namespace to run Node-RED instances in. Default: `flowforge` | | `driver.options.cloudProvider` | Enables specific options for certain platforms e.g. `aws`. Default: not set | | `driver.options.projectSelector` | A YAML object containing node annotations to use to filter which nodes Node-RED instances run on. Default: `role: projects` | | `driver.options.logPassthrough` | Prints the Node-RED logs in JSON format to stdout of the instance pods. This should be set with the `forge.logPassthrough=true` Helm chart value. Default: `false` | | `driver.options.privateCA` | The name of a ConfigMap containing a file called `certs.pem` which holds locally trusted CA cert chain. Default: not set | | `driver.options.customHostname.enabled` | Enables the custom hostname feature. Default: `false` | | `driver.options.customHostname.cnameTarget` | The hostname users should configure their DNS entries to point at. This value is required to enable this feature. Default: not set | | `driver.options.customHostname.ingressClass` | The name of the Ingress Class that should be used for the custom hostname. Default: not set | | `driver.options.customHostname.certManagerIssuer` | The name of the CertManager ClusterIssuer to provision HTTPS certificates for custom hostnames. Default: not set | ## MQTT Broker configuration By default, the platform runs without an MQTT broker. This restricts some features in the platform, such as the Project Nodes, Device Actions and Remote Device Editing. If a broker has been setup in the platform, the following configuration is required: | Option | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `broker.url` | The url for the platform to access the broker. For example: `mqtt://localhost:4800`. | | `broker.public_url` | The url used by devices to connect to the broker, if different to `broker.url`. For example, this may require devices to use WebSockets instead: `ws://localhost:4881`. | ## Email configuration By default, email is disabled. This restricts some features in the platform around inviting new users to join. | Option | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------- | | `email.enabled` | Enables the email sending functionality of the platform. Default: `false` | | `email.from` | Sets the address email will appear from. Default: `"FlowFuse" ` | | `email.smtp.host` | Hostname of the SMTP server to send email through. Default: `localhost` | | `email.smtp.port` | Port of the SMTP server to send email through. Default: `587` if `secure` is `false`, `465` otherwise | | `email.smtp.secure` | Whether to use TLS to connect to the SMTP server. Default: `false` | | `email.smtp.auth.user` | Username to authenticate the connection with. Default: `unset` | | `email.smtp.auth.pass` | Password to authenticate the connection with. Default: `unset` | | `email.debug` | If set to true, it will log the full content of emails it tries to send. Default: `false` | See [here](https://flowfuse.com/docs/install/email-providers) for example configuration with common email providers. ### AWS SES Email There is also support for using AWS SES email, this is mainly intended to be used when deployed on AWS EKS. This assumes that the instance is running with a Service Account that has a AWS Role with SES access enabled. | Option | Description | | --------------------- | ------------------------------------------------------------------------------------------------- | | `email.ses.region` | The AWS region to connect to. Default `unset` | | `email.ses.sourceArn` | The AWS ARN of a SES Identity to send email as. Default: `unset` | | `email.ses.fromArn` | The AWS ARN of a SES Identity to set as the from field. Default to value of `email.ses.sourceArn` | ## Telemetry configuration By default, the platform will send anonymous usage information back to us at FlowForge Inc. This can be disabled via the Admin Settings in the UI, or turned off in the configuration file with the `telemetry.enabled` option. **IMPORTANT: Licensed installations cannot disable telemetry** Additionally, you can configure your own instance of FlowFuse to report back to you on how users are using your instance of FlowFuse. FlowFuse supports integration with two different services: - [PostHog](https://posthog.com/){rel=""nofollow""} *(recommended)*: You will require your own API key to pass into the `yml`, which will begin the logging of user interactions. - [Plausible](https://plausible.io/){rel=""nofollow""}: *(deprecated since 0.9 and will be removed in the future)*: You can setup your own account, and pass the relevant domain to the `yml` in the telemetry configuration For more information about this feature, see [here](https://flowfuse.com/docs/admin/telemetry) | Option | Description | | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `telemetry.enabled` | Enables the anonymous usage telemetry of the platform. Default: `true` | | `telemetry.frontend.posthog.apiurl` | The API URL for PostHog, either '{rel=""nofollow""}' or '{rel=""nofollow""}'. Default: `https://app.posthog.com` | | `telemetry.frontend.posthog.apikey` | The API key provided to you from your own PostHog account. Default: `null` | | `telemetry.frontend.posthog.capture_pageview` | FlowFuse is designed as to provide custom posthog `$pageview` events that provide more detail on navigation than the default, and suit a single page application better. As such, we recommend setting this to false in order to prevent duplicate `pageleave`/`pageview` events firing. Default: `true` | | `telemetry.frontend.google.tag` | A Google Analytics (gtag) measurement ID. Default: `null` | | `telemetry.frontend.google.events` | An object with keys matching the names of tag events to be enabled and any payload values. Default `null` | | `telemetry.frontend.google.gtm` | A Google Tag Manager container ID (e.g. `GTM-XXXXXXX`). Loads GTM once cookie consent is accepted. Default: `null` | ## Rate Limiting configuration By default, rate limiting is disabled and the platform will not rate limit any requests. To enable rate limiting, you can set the `rate_limits.enabled` option to `true`. When enabled, all routes will be limited to 1000 requests per 1 minute window. These defaults can be adjusted by setting values in the configuration options listed below. | Option | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `rate_limits.enabled` | Enables rate limiting. Default: `false` | | `rate_limits.global` | Enables rate limiting for all routes. Default: `true` (defaults to all routes being rate limited) | | `rate_limits.timeWindow` | The time window in which requests are counted. Default: `60000` (1 minute) | | `rate_limits.max` | The maximum number of requests allowed in the time window. Default: `1000` | | `rate_limits.maxAnonymous` | The maximum number of requests allowed in the time window for anonymous users. Default: not configured (defaults to `rate_limits.max`) | For additional options, see [fastify-rate-limit](https://github.com/fastify/fastify-rate-limit#options){rel=""nofollow""} documentation. ## Session timeouts Allows control of the maximum user session life. | Option | Description | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `sessions.maxDuration` | The maximum number of seconds a user session can last. Default: `604800` (1 week) | | `sessions.maxIdleDuration` | The maximum number of seconds a session can be idle. Must be less than `sessions.maxDuration`. Default: `115200` (32 hours) | ## Support configuration It is possible to add a [HubSpot Support Widget](https://knowledge.hubspot.com/chatflows/create-a-live-chat){rel=""nofollow""} into FlowFuse. This will appear as a floating chat bubble on the bottom-right corner of the screen. To enable this, you'll need to provide the | Option | Description | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `support.enabled` | Enables the chat support widget in the UI. Default: `false` | | `support.frontend.hubspot.trackingcode` | The numerical identifier within your [HubSpot Tracking Code](https://knowledge.hubspot.com/conversations/chat-widget-is-not-appearing-on-your-pages){rel=""nofollow""}. Default: `null` | ## MQTT Broker configuration The platform uses an MQTT broker to provide real-time messaging between devices, Node-RED instances and the platform. The broker shipped with the platform's Docker Compose and Kubernetes installations is [EMQX](https://www.emqx.io/){rel=""nofollow""}: the Docker Compose installation includes it by default, and the Helm chart deploys it when the broker is enabled (`forge.broker.enabled`), which requires the [EMQX Operator](https://docs.emqx.com/en/emqx-operator/latest/getting-started/getting-started.html#install-emqx-operator){rel=""nofollow""} to be installed on the cluster. This is currently an *optional* component - the platform will work without the broker, but some features will not be available: - Without a broker: Project Nodes, Device Actions and Remote Device Editing are unavailable. - The following features additionally require the platform broker to be EMQX: the [Team Broker](https://flowfuse.com/docs/user/teambroker), [FlowFuse Expert](https://flowfuse.com/docs/user/expert/) and live device log and performance views in the platform UI. [Mosquitto](https://mosquitto.org/){rel=""nofollow""} is supported at a legacy level for existing installations that manage their own broker: core platform messaging works, but the EMQX-dependent features listed above are unavailable. Replacing the platform broker with a different customer-supplied broker is not supported. | Option | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `broker.url` | The full url to the platform broker. This is used by the platform and Node-RED instances to connect to the broker. For example: `mqtt://localhost:1883`. | | `broker.public_url` | If set, this is the url provided to Devices to connect to the broker with. When running in a Docker or K8S environment, this url should be the externally addressable url the broker is provided on. This could be via WebSockets, for example: `ws://example.com:1884` | ## AI Configuration | Option | Description | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ai.enabled` | Enables all AI features on the platform (still requires an Enterprise License). When set to `false`, all AI functionality is disabled regardless of individual feature configuration (assistant, expert, inline completions, snapshot descriptions). Default: `true` | ## FF Tables | Option | Description | | ----------------------- | -------------------------------------------------------------------------------------------------- | | `tables.enables` | Enables the FlowFuse Tables feature. Default: `false` | | `tables.driver.type` | Selects the FF Tables driver to use (`postgres-localfs` or `postgres-supavisor`). Default: not set | | `tables.driver.options` | Configuration options for the driver. Details depend on the driver used. Default: not set | The options for the `postgres-localfs` driver require connection details for an admin user on a Postgres instance e.g. ```text database: user: root password: password host: localhost port: 5432 database: postgres ssl: false ``` ## Logging configuration By default the forge app is set to `info` level logging, with the HTTP routes logged at `warn` | Option | Description | | ---------------- | ------------------------------------------------------------------------------ | | `logging.level` | Change the default logging level. Default: `info` | | `logging.http` | Change the default HTTP route logging level. Default: `warn` | | `logging.pretty` | Enable/Disable pretty-printing of the log output. Default: `false` - see below | Setting `logging.http` to `info` will log every HTTP request and response details. The `pretty` option controls the formatting of the log output. When running in developer mode, (for example, if `NODE_ENV` is set to `developer`), then pretty formatting is enabled by default. This makes the logs more human-readable. Otherwise, the log output is JSON formatted for consumption by other tools. ## File storage FlowFuse includes a service that can be used by Node-RED instances to read and write files in their flows as well as providing persistent storage for flow context information. Details of configuring the File Storage service are available [here](https://flowfuse.com/docs/install/file-storage). The main `flowforge.yml` file needs to contain the following properties so it can access the File server. | Option | Description | | --------------- | ------------------------------------------------------------ | | `fileStore.url` | The URL of the FlowFuse File Server to use. Default: not set | ## Enabling Persistent File Storage - File Nodes These nodes are enabled by default on the FlowFuse Cloud platform. If you're running a self-hosted environment you should follow the next steps. FlowFuse file nodes replace the core Node-RED file nodes. To make use of these nodes, the FlowFuse platform Administrator must ensure the core file nodes are not loaded. This is done by adding `10-file.js` in the **Exclude nodes by filename** section of your instance settings under the **Palette** section. This setting is modifiable only by a Team owner and only if it has not been locked in the [template](https://flowfuse.com/docs/user/concepts#template) by the platform Administrator. [Click here](https://flowfuse.com/docs/user/filenodes), to learn more about the usage of the FlowFuse File Nodes. ## Content Security Policy | Option | Description | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `content_security_policy.enabled` | Enabled `Content-Security-Policy` headers. Default: `false` | | `content_security_policy.directives` | Overrides the default set of directives, supplied as a JSON object defined by HelmetJS [here](https://helmetjs.github.io/#content-security-policy){rel=""nofollow""} | | `content_security_policy.report_only` | Enables reporting only mode. Default: `false` | | `content_security_policy.report_uri` | Provides at URI for reporting to be sent to if enabled | ## NPM Registry These settings enable per team Node-RED private catalogue generation | Option | Description | | ---------------------------- | ----------------------------------------------------- | | `npmRegistry.enabled` | Enables NPM Registry support. Default: `false` | | `npmRegistry.url` | The URL for the Verdaccio NPM Registry. Default: none | | `npmRegistry.admin.username` | Username for Verdaccio admin user | | `npmRegistry.admin.password` | Password for Verdaccio admin user | ## BluePrint Updates For Licenses instances these options control how new BluePrints are imported `blueprintImport.enabled` | Enables the import of new BluePrints from FlowFuse. Default: `true``blueprintImport.export` | Enables the API endpoint to export the local BluePrints. Default: `false``blueprintImport.url` | The URL to import BluePrints from. Default: `https://app.flowfuse.com/api/v1/flow-blueprints/export-public` # DNS Setup When running FlowFuse on Docker or Kubernetes you will need to be able to setup an entry in a DNS server. This is because FlowFuse uses hostname based routing to know which Node-RED instance you want to access. By default the instance name is used in combination with a supplied domain. In this document we will use `example.com` as the domain. (It doesn't need to be a "whole" domain, it could also be a sub domain of one you already own. e.g. `ff.example.com`). If you are running Docker/Kubernetes on the same machine as the DNS server and Web Browser do not use `127.0.0.1` as the IP address to point the wild card domain at. This is because the host names will be looked up by the Web Browser, the Forge Application, and the Node-RED instances. The last 2 are running in containers and `127.0.0.1` will resolve to the container, not the entry point. ## Production For a production deployment you will need to have access to modify DNS, if you are not sure how to set up DNS records talk to whoever manages your DNS. As mentioned earlier you will need them to create a [wildcard DNS](https://en.wikipedia.org/wiki/Wildcard_DNS_record){rel=""nofollow""} entry that points to either the Docker host machine or the Kubernetes Nodes which are running the Ingress Controller. This can be either: - a `A` (and `AAAA` for IPv6) record pointing to an IP address e.g. `*.example.com 8600 A 192.0.2.1` - a `CNAME` record pointing to the hostname of the entry point e.g. `*.ff.example.com 8600 CNAME forge.example.com` ### AWS ALB Ingress When using AWS ALB (Application Load Balancer) as an Ingress Controller for FlowFuse deployed into an EKS cluster then you would create a wildcard CNAME entry pointing to the hostname of the ALB ### Digital Ocean You should create an A record pointing to the public IP address of the Load Balancer created when you install the Traefik Helm Chart. ## Local Testing and Development For development and testing we probably only need to set up DNS entries for the developers local machine. The easiest way to do this is to use an application called dnsmasq. Dnsmasq is a tool that can be used as a DNS caching proxy (and a DHCP server, but we don't need that). We can set it up to point to an upstream DNS server to resolve all normal addresses, but we can also give it a list of hostname/IP address pairs to use locally. ### DNSMasq Setting up dnsmasq is not too complex, what is harder is setting it up in a way that works well with the network configuration on a laptop that might move between different networks and expects to get its default DNS configuration automatically assigned by DHCP. The following headings cover how to do this on a number of different operating systems #### Ubuntu For Docker on Linux you can use `172.17.0.1` as the address for the domain which is the IP address assigned to the `docker0` interface. ```bash sudo apt-get install dnsmasq sudo echo "bind-interfaces" >> /etc/dnsmasq.conf sudo echo "no-resolv" >> /etc/dnsmasq.conf sudo echo "conf-dir=/etc/dnsmasq.d" >> /etc/dnsmasq.conf sudo echo "address=/example.com/172.17.0.1" > /etc/dnsmasq.d/02-flowforge.conf sudo service dnsmasq restart sudo echo "DNS=127.0.0.1" >> /etc/systemd/resolved.conf sudo echo "DOMAINS=~example.com" >> /etc/systemd/resolved.conf sudo service systemd-resolved restart ``` #### Fedora For Docker on Linux you can use `172.17.0.1` as the address for the domain which is the IP address assigned to the `docker0` interface. ```bash sudo dnf install dnsmasq sudo echo "bind-interfaces" >> /etc/dnsmasq.conf sudo echo "no-resolv" >> /etc/dnsmasq.conf sudo echo "conf-dir=/etc/dnsmasq.d" >> /etc/dnsmasq.conf sudo echo "address=/example.com/172.17.0.1" > /etc/dnsmasq.d/02-flowforge.conf sudo systemctl enable dnsmasq.service sudo service dnsmasq restart sudo echo "DNS=127.0.0.1" >> /etc/systemd/resolved.conf sudo echo "DOMAINS=~example.com" >> /etc/systemd/resolved.conf sudo service systemd-resolved restart ``` #### Windows Unfortunately dnsmasq will not run on Windows and I have not found something similar yet. #### MacOS On MacOS you can alias a private IP address to the loop back interface e.g. `10.128.0.1` with ```bash sudo ifconfig lo0 alias 10.128.0.1 ``` You will need install dnsmasq using [homebrew](https://docs.brew.sh/Installation){rel=""nofollow""} ```bash brew install dnsmasq ``` It appears that the install location differs based on the Apple Hardware. For Intel hardware Macs it's in `/usr/local` and for M1 hardware it's `/opt/homebrew`. Please check where it installed things when the previous command has completed. Then edit a configuration file M1 mac ```bash echo "conf-dir=/opt/homebrew/etc/dnsmasq.d" >> /opt/homebrew/etc/dnsmasq.conf echo "address=/example.com/10.128.0.1" > /opt/homebrew/etc/dnsmasq.d/ff.conf ``` Intel mac ```bash echo "conf-dir=/usr/local/etc/dnsmasq.d" >> /usr/local/etc/dnsmasq.conf echo "address=/example.com/10.128.0.1" > /usr/local/etc/dnsmasq.d/ff.conf ``` Set dnsmasq to run as a service ```bash sudo brew services start dnsmasq sudo dscacheutil -flushcache ``` Tell MacOS to use dnsmasq for our test domain ```bash sudo mkdir -p /etc/resolver sudo tee /etc/resolver/example.com > /dev/null < When using automatic TLS certificate generation, the platform will take a few minutes to generate them on the first platform startup. > For a short period of time browsers may report untrusted certificate warning. This is expected behavior and should resolve itself once the certificate is generated. #### Custom TLS Certificate If you have own TLS certificate, you can use it in FlowFuse platform installation as well. As mentioned before, the certificate must be a wildcard one for the domain you are using. ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} If your TLS certificate is issued by a private Certificate Authority, additional configuration is required so that Hosted Instances trust the CA. See [What additional configuration is required when the TLS certificate is issued by a private Certificate Authority?](https://flowfuse.com/#what-additional-configuration-is-required-when-the-tls-certificate-is-issued-by-a-private-certificate-authority) for the step-by-step instructions. ::: :: To configure FlowFuse platform with your certificate, you need to have: - certificate key file - certificate's full chain (server certificate and intermediate certificates bundled into single file) To add your certificate to the platform, edit the `.env` file downloaded earlier and set values for `TLS_ENABLED`, `TLS_CERTIFICATE` and `TLS_KEY` variables. `TLS_ENABLED` variable should be set to `true`. `TLS_CERTIFICATE` should contain the full chain of the certificate while `TLS_KEY` should contain the key file. Example of `.env` file with the custom TLS certificate configuration: ```bash TLS_ENABLED=true TLS_CERTIFICATE=" -----BEGIN CERTIFICATE----- MIIFfzCCBKegAwIBAgISA0 ... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFfzCCBKegAwIBAgISA0 ... -----END CERTIFICATE----- " TLS_KEY=" -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD ... -----END PRIVATE KEY----- " ``` ## Start FlowFuse platform **Note: Make sure all configuration are done above before proceeding.** **Note: Commands must be executed within the same directory where the Docker Compose and `.env` files are located.** #### With automatic TLS certificate generation ```bash docker compose --profile autotls -p flowfuse up -d ``` #### In all other scenarios, including custom TLS certificate ```bash docker compose -p flowfuse up -d ``` The platform will take a few minutes to start up. You can check the status of the containers by running: ```bash docker compose -p flowfuse ps ``` Visit `forge.example.com` (replace `example.com` with the domain configured in the `.env` file) in your browser to access the FlowFuse platform. ## First Run Setup The first time you access the platform in your browser, it will take you through creating an administrator for the platform and other configuration options. For more information, follow [this guide](https://flowfuse.com/docs/install/first-run). Once you have finished setting up the admin user there are some Docker specific items to consider. ## Upgrade **Note: If you are upgrading from version `2.10.0` or lower, please follow [this guide](https://github.com/FlowFuse/docker-compose/blob/main/UPGRADE.md){rel=""nofollow""}** 1. Find the Docker Compose project name: ```bash docker compose ls ``` The output will show the project name, as well as the location of Docker Compose file used for creating the project. 2. Stop the existing project (replace `$projectName`with your project name): ```bash docker compose -p $projectName down --rmi all ``` 3. Download the latest Docker Compose files: ```bash curl -L -o docker-compose.yml https://github.com/FlowFuse/docker-compose/releases/latest/download/docker-compose.yml ``` 4. Make sure the `.env` file is present and contains your installation-specific configuration. Download an example `.env`file if needed: ```bash curl -o .env.example https://raw.githubusercontent.com/FlowFuse/docker-compose/refs/heads/main/.env.example ``` 5. Pull the latest version of the default stack container (and any other stacks you have configured) ```bash docker pull flowfuse/node-red:latest ``` 6. Start the project depending on the TLS configuration (replace `$projectName` with your project name): - automatic TLS: ```bash docker compose --profile autossl -p $projectName up -d ``` - any other scenario: ```bash docker compose -p $projectName up -d ``` ## Common Questions ### How to use external database server? FlowFuse platform uses PostgreSQL database to store its data. By default, the database is created and managed by the Docker Compose. If you want to use an external database server, you need to: - on your database server, create `flowforge` and `ff-context` databases as well as a user with access to both of them (see `configs.postgres_db_setup` and `configs.postgres_context_setup` in the `docker-compose.yml` file for the reference) - configure the connection to the database in the `.env` file. Set the `DB_HOST`, `DB_USER`, `DB_PASSWORD` variables to the connection details of the external database server Once ready, [start the application](https://flowfuse.com/#start-flowfuse-platform) . ### How can I provide my own TLS certificate? If you have your own TLS certificate, you can use it in FlowFuse platform installation as well. See [Enable HTTPS](https://flowfuse.com/#enable-https-optional) section for more details. Additionally, if your TLS certificate is issued by a private Certificate Authority, you will need to perform some additional configuration to make hosted Node-RED instances trust the CA. See [What additional configuration is required when the TLS certificate is issued by a private Certificate Authority](https://flowfuse.com/#what-additional-configuration-is-required-when-the-tls-certificate-is-issued-by-a-private-certificate-authority) for the step-by-step instructions. ### What additional configuration is required when the TLS certificate is issued by a private Certificate Authority? When the FlowFuse platform is configured with a TLS certificate issued by a private Certificate Authority (CA), hosted Node-RED instances will not trust the CA by default. As a result, opening the editor for a hosted instance will fail with a certificate trust error. To make hosted Node-RED instances trust the private CA, follow the steps below: 1. **Update the environment file.** In the `.env` file created during the installation process, set `DOCKER_DRIVER_PRIVATE_CA_PATH` to the path of your Certificate Authority certificate file. This must be the path to the file stored on the Docker host server. For example, if the `ca.pem` file is located at `/usr/local/ssl/ca.pem`: ```bash DOCKER_DRIVER_PRIVATE_CA_PATH="/usr/local/ssl/ca.pem" ``` 2. **Modify the Compose file.** In the `docker-compose.yml` file used to create the FlowFuse Docker stack, uncomment the `NODE_EXTRA_CA_CERTS` environment variable and the corresponding volume mount on the `forge` service so the certificate is passed correctly to the platform. You can apply both changes with a single `sed` command: ```bash sed -i.bak -E -e 's/^#([[:space:]]*- "NODE_EXTRA_CA_CERTS=)/\1/' -e 's/^#([[:space:]]*- \$\{DOCKER_DRIVER_PRIVATE_CA_PATH\})/\1/' docker-compose.yml && rm docker-compose.yml.bak ``` 3. **Restart the Forge service** to apply the changes: ```bash docker compose restart forge ``` 4. **Apply changes to running instances.** Once the platform is online, manually suspend and start all running hosted instances so they pick up the new TLS settings. ### I would like to invite my team members to the platform with e-mail, how can I do that? In order to configure FlowFuse platform with external e-mail server, you need to adjust `EMAIL_*` variables in the `.env` file. Find the `.env` file end edit `Email configuration` section with following details: - `EMAIL_ENABLED` - set to `true` to enable e-mail functionality - `EMAIL_HOST` - provide SMTP server host - `EMAIL_PORT` - provide SMTP server port (default is `587`) - `EMAIL_SECURE` - set to `true` if the connection should be secured - `EMAIL_USER` - provide SMTP server username - `EMAIL_PASSWORD` - provide SMTP for the user defined in `EMAIL_USER` Restart the core application to apply the changes: ```bash docker compose restart forge ``` ### Connection Refused error After starting the platform, I can't access it in the browser - I see "Connection Refused error" If you are using the Digital Ocean Docker Droplet to host FlowFuse you will need to ensure that port 80 & 443 are opened in the UFW firewall before starting. FlowFuse platform is running on ports 80 and 443, so you need to open these ports in the firewall. Below are examples of commands to open these ports: Ubuntu: ```bash sudo ufw apply http sudo ufw apply https ``` CentOS: ```bash sudo firewall-cmd --zone=public --add-service=http --permanent sudo firewall-cmd --zone=public --add-service=https --permanent sudo firewall-cmd --reload ``` Windows (command prompt): ```bash netsh advfirewall firewall add rule name="Open Port 80" dir=in action=allow protocol=TCP localport=80 netsh advfirewall firewall add rule name="Open Port 443" dir=in action=allow protocol=TCP localport=443 ``` Windows (PowerShell): ```powershell New-NetFireWallRule -DisplayName 'WSL 8080TCP' -Direction Inbound -LocalPort 8080 -Action Allow -Protocol TCP New-NetFireWallRule -DisplayName 'WSL 8080TCP' -Direction Outbound -LocalPort 8080 -Action Allow -Protocol TCP ``` ### I installed FlowFuse on Windows with WSL2, application is running but I can't access it in the browser Next to [opening the ports in the firewall](https://flowfuse.com/#connection-refused-error), you need to configure port forwarding from Windows host to WSL2 server. To forward traffic from an external IP to your container, run the following PowerShell command (administrator privileges required): ```powershell netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=127.0.0.1 ``` This command forwards traffic from port 80 on your external IP address to port 80 on your localhost, where the Nginx Proxy container is listening for connections. ### How can I enable persistent storage for Node-RED instances? Node-RED instances running in Docker do not have direct access to a persistent file system to store files or use for storing context data. FlowFuse includes a File Storage service that can be enabled to provide persistent storage. To disable the default File nodes, edit the Template and add `10-file.js,23-watch.js` to the "Exclude nodes by filename" section ![](https://flowfuse.com/docs/install/images/file-node-template.png){width="500"} FlowFuse Docker Compose files includes FlowFuse File Storage component by default and starts it along with the platform. Full details on configuring the File Storage service are available [here](https://flowfuse.com/docs/install/file-storage). ## Uninstall - Bring the services down with `docker compose -p flowfuse down -v` (note the extra `-v` to delete all the volumes, only include this if you do not want to reuse this install) - Use `docker images` to list container images - Use `docker rmi [imagename]:[tag]` to remove all images that start with `flowfuse/` # Docker Stacks A Stack defines a set of platform configuration options that will get applied to each Node-RED instance when created. For container based deployment models, this covers three things: - `memory` - the amount of memory (in MB) to limit container to. Recommended minimum: `256`. - `cpu` - a value between 1 and 100 that is the % of a CPU core the container should be allowed to consume. - `container location` - this is the fully qualified name of the container to use. The default container built when following the install instructions is named `flowfuse/node-red:latest` If you wish to use different Node-RED version, you need to specify the name of the container and the version you want to use. For example, if you want to use Node-RED v3.1.x, you should enter `flowfuse/node-red:latest-3.1.x` in the `container location` section of the Stack configuration. Full list of available pre-built containers can be found on [Docker Hub](https://hub.docker.com/r/flowfuse/node-red/tags){rel=""nofollow""}. ## Creating Own Containers As mentioned in the previous paragraph, we encourage to use our pre-built containers in your stacks. However, if you want to create your own container, you can do so by creating a `Dockerfile` and `package.json` files. There is an example `Dockerfile` and `package.json` in the [node-red-container](https://github.com/FlowFuse/docker-compose/tree/main/node-red-container){rel=""nofollow""} directory of the [docker-compose](https://github.com/FlowFuse/docker-compose){rel=""nofollow""} project. This will start with `nodered/node-red:latest` as it's base and then add the required FlowFuse components. Builds of this container for amd64, arm64 and armv7 are built for every release and published to Docker hub as [flowfuse/node-red](https://hub.docker.com/r/flowfuse/node-red){rel=""nofollow""}. These can be used as a base to build custom stacks. If you wanted to pin at Node-RED v3.0.2 you would change the first line to: ```docker FROM nodered/node-red:3.0.2 ARG REGISTRY RUN if [[ ! -z "$REGISTRY" ]] ; then npm config set @flowfuse:registry "$REGISTRY"; fi COPY package.json /data ... ``` To add nodes to the default image you can extend the supplied container. The following Dockerfile will install the node-red-dashboard ```docker FROM flowfuse/node-red WORKDIR /usr/src/node-red RUN npm install node-red-dashboard WORKDIR /usr/src/flowforge-nr-launcher ``` To build the container run the following: ```shell docker build node-red-container/Dockerfile-dashboard -t flowfuse/node-red-dashboard:3.0.2 ``` You would then enter `flowfuse/node-red-dashboard:3.0.2` in the `container` section of the Stack configuration. Stacks can be changed on a per instance basis, see also the [user stack documentation](https://flowfuse.com/docs/user/changestack). # Using Docker on Windows While Docker is inherently a Linux-based technology and we recommend running FlowFuse on Linux when ever possible, there are several ways to run Docker on Windows. Below, we outline the primary methods available, along with recommendations based on specific needs and use cases. ### Docker Desktop [Docker Desktop](https://docs.docker.com/desktop/install/windows-install/){rel=""nofollow""} is the most straightforward option for running Docker on Windows, offering a complete Docker environment with GUI support. It’s well-suited for users seeking ease of setup and use. **Recommendation:** Use Docker Desktop if licensing is not a constraint for your organization, as it provides a user-friendly, fully-integrated Docker experience on Windows. ### Rancher Desktop [Rancher Desktop](https://rancherdesktop.io/){rel=""nofollow""} is a free, open-source alternative to Docker Desktop, providing a similar experience but without the licensing concerns. Rancher Desktop includes both container management and Kubernetes support, making it a flexible choice for containerized workloads on Windows. **Recommendation:** Consider Rancher Desktop if you prefer an open-source tool with no licensing restrictions. ### Windows Subsystem for Linux (WSL2) [WSL2](https://docs.microsoft.com/en-us/windows/wsl/install){rel=""nofollow""} enables users to run a Linux environment directly on Windows. WSL2 supports running Docker Engine without the need for a GUI, making it ideal for headless configurations or for those looking to operate Docker from a Linux command line on Windows. **Recommendation:** WSL2 is best suited for advanced users who are comfortable with command-line tools and who want to run Docker Engine directly within a Linux environment on Windows. For the installation of Docker Engine using WSL2, refer to the next paragraph. ## How to Install Docker Engine (Docker CE) on Windows using WSL2 This guide explains how to install Docker Engine (Docker CE) on Windows using Windows Subsystem for Linux (WSL2). ### Prerequisites Ensure your system meets the following Windows Subsystem for Linux v2 requirements: - Windows Server 2022 - Windows 10 version 2004 and higher (Build 19041 and higher) - Windows 11 ### Step 1: Install Windows Subsystem for Linux Open PowerShell as an administrator and run the following commands: 1. Install WSL and the default Linux distribution (Ubuntu) using the following command: ```powershell wsl --install ``` 2. Reboot your system to apply changes ```powershell shutdown -r -t 5 ``` After the reboot, WSL will automatically start installing the Ubuntu Linux distribution. You will be prompted to create a new UNIX account. Follow the instructions to create a new user account and set a password. ![wsl-unix-user-creation](https://flowfuse.com/docs/install/images/wsl-unix-user.png) Once completed, you will be dropped into a new Ubuntu shell. ![wsl-install-complete](https://flowfuse.com/docs/install/images/wsl-install-complete.png) 3. Confirm using proper WSL version, by running (in Powershell window): ```powershell wsl --status ``` ### Step 2: Install Docker on Ubuntu Once the Ubuntu system is ready, follow these steps to install Docker: 1. **Remove Conflicting Packages**:br First, remove any existing Docker packages that might conflict with the installation: ```bash for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done ``` 2. **Add Docker’s Official GPG Key and APT Repository**:br Next, update your package list and add Docker's official GPG key and APT repository: ```bash sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc ``` :brAdd the Docker repository to your APT sources: ```bash echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update ``` 3. **Install Docker Packages**:br Now, install Docker and its associated packages: ```bash sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ``` 4. **Add user to Docker group**:br Add your user to the `docker` group to run Docker commands without `sudo`: ```bash sudo usermod -aG docker ubuntu ``` 5. **Start the Docker Service**:br Start Docker using the following command: ```bash sudo /etc/init.d/docker start ``` 6. **Verify Docker Installation**:br Run a test Docker container to verify that Docker is installed and running correctly: ```bash sudo docker run hello-world ``` :brIf Docker is installed correctly, you should see a similar output: :br![wsl-docker-installation-complete](https://flowfuse.com/docs/install/images/wsl-docker-complete.png) Once Docker is installed, you can [install the FlowFuse platform using docker compose](https://flowfuse.com/docs/install/docker). # Example configuration for common email platforms ## GMail ```yaml email: enabled: true debug: false smtp: host: smtp.gmail.com port: 465 secure: true auth: user: [USER]@gmail.com pass: [PASSWORD] ``` Note: Gmail may require an app specific password to be created if you are using 2FA on the account you can set that up [here](https://security.google.com/settings/security/apppasswords){rel=""nofollow""} ## Office365 ```yaml email: enabled: true debug: false smtp: host: smtp.office365.com, secure: false tls: ciphers: "SSLv3", rejectUnauthorized: false auth: user: [USERNAME] pass: [PASSWORD] ``` # FlowFuse File Storage FlowFuse has two storage-related features for Node-RED instances in container-based deployments. They are not mutually exclusive, and some deployments use both. - **Persistent Storage** mounts a persistent volume into the Node-RED instance at `/data/storage`. This was introduced in FlowFuse v2.6.0 for Kubernetes. - **The File Storage service** provides the FlowFuse File nodes and Persistent Context. The rest of this page is structured around those two features so it is clearer which configuration you need. ## Which configuration do I need? Use the following guide when choosing what to configure: | Requirement | FlowFuse version | What to configure | | ----------------------------------------------------------------------------------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Files written directly by the Node-RED instance must survive restart, suspend/resume, or stack upgrades | v2.6.0 or later | Configure [Persistent Storage for Node-RED instances](https://flowfuse.com/#persistent-storage-for-node-red-instances) | | | Before v2.6.0 | Configure [The File Storage service](https://flowfuse.com/#the-file-storage-service) | | You want to use [FlowFuse Persistent Context](https://flowfuse.com/docs/user/persistent-context/){rel=""nofollow""} | v2.6.0 or later | Configure [The File Storage service](https://flowfuse.com/#the-file-storage-service) and the [Persistent Context configuration](https://flowfuse.com/#persistent-context-configuration). If the instance also needs persistent filesystem access, configure [Persistent Storage for Node-RED instances](https://flowfuse.com/#persistent-storage-for-node-red-instances) as well. | | | Before v2.6.0 | Configure [The File Storage service](https://flowfuse.com/#the-file-storage-service) and the [Persistent Context configuration](https://flowfuse.com/#persistent-context-configuration). The File Storage service also provides persistent storage for the instance. | ## Persistent Storage for Node-RED instances As part of the FlowFuse v2.6.0 release, a new Persistent Storage approach was implemented for Kubernetes. Docker support will follow. This mounts a Persistent Volume into the container running the Node-RED instance on `/data/storage`. Files written to this location are preserved for the life of the instance and across suspend/resume operations and stack upgrades. If you are using FlowFuse version lower than v2.6.0, you will need to use the File Storage service to provide persistent storage to Node-RED or upgrade to v2.6.0 or later to use this feature. ### Configuring Create a Kubernetes `StorageClass` that allows dynamic provisioning of `PersistentVolumes` from `PersistentVolumeClaims`, for example the [AWS EFS CSI driver](https://github.com/kubernetes-sigs/aws-efs-csi-driver){rel=""nofollow""}. Then pass the following values to the FlowFuse Helm Chart when upgrading. ```yaml forge: persistentStorage: enabled: true storageClass: '' size: '5Gi' ``` Where `size` is the default size for the volume. #### Azure If you are using the `azurefile-csi` Persistent Storage driver then we recommend adding the following to the `StorageClass mountOptions`: ```yaml mountOptions: - dir_mode=0777 - file_mode=0777 - mfsymlinks - nobrl ``` See [the Azure Kubernetes documentation](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/storage/mountoptions-settings-azure-files){rel=""nofollow""} for more details. ## The File Storage service The FlowFuse Platform includes a File Storage service that provides: - A set of custom File nodes that behave the same way as the standard Node-RED File nodes - An optional Persistent Context store for storing context data within flows. This feature is only available for platforms running with a premium license. On FlowFuse v2.6.0 or later, the File Storage service is used exclusively for Persistent Context. Instance-level Persistent Storage is handled separately via [Persistent Storage for Node-RED instances](https://flowfuse.com/#persistent-storage-for-node-red-instances). Before FlowFuse v2.6.0, this service was also the primary way to provide persistent storage to Node-RED in container-based environments, in addition to Persistent Context. ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} The File Storage service is only required in Docker or Kubernetes environments. If you are using the LocalFS platform driver, Node-RED already has direct access to the local filesystem. ::: :: ### Configuring The File Storage server has its own configuration file: `etc/flowforge-storage.yml`. - **Docker Compose** - edit the file directly before starting the service - **Kubernetes/Helm** - include the options in your `customization.yml`, using `forge.fileStore.*` as the property name prefix. You must also set `forge.fileStore.enabled` to `true` to tell Helm to deploy the service. There are three parts to the configuration: - [Platform Configuration](https://flowfuse.com/#platform-configuration) - how to access the main FlowFuse platform application - [File Storage service configuration](https://flowfuse.com/#file-storage-service-configuration) - what storage to use for the File nodes - [Persistent Context configuration](https://flowfuse.com/#persistent-context-configuration) - what storage to use for Persistent Context ### Platform Configuration | Option | Description | | ---------- | ------------------------------------------------------------------------------------ | | `host` | Where to listen for incoming connections. Default: `0.0.0.0`. | | `port` | The port to listen on. Default: `3001` | | `base_url` | The url to access the FlowFuse platform on. This defaults to `http://localhost:3000` | ### File Storage service configuration The File Storage configuration determines where the files used by the Node-RED File nodes are stored. You can configure it to store files either on the local filesystem of the File Storage server or in an AWS S3-compatible service. #### LocalFS Stores the files locally, for example by using a volume mounted into the File Storage server container. | Option | Description | | --------------------- | -------------------------------------------------------------------------------------------------------------- | | `driver.type` | `localfs` | | `driver.quota` | A per-instance quota for how much data will be stored - in bytes. If this is not set, no limit will be applied | | `driver.options.root` | The root path under which Node-RED instance data should be stored. | The following shows an example configuration using the `localfs` file driver. ```yaml driver: type: localfs quota: 104857600 options: root: var/root ``` #### S3 Compatible Storage Stores the files in an external service using the AWS S3 API. | Option | Description | | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | `driver.type` | `s3` | | `driver.quota` | A per-instance quota for how much data will be stored - in bytes. If this is not set, no limit will be applied | | `driver.options.bucket` | Name of the S3 bucket to use (required) | | `driver.options.region` | Name of AWS region of the bucket (required) | | `driver.options.endpoint` | S3 ObjectStore Endpoint, if not using AWS S3 | | `driver.options.forcePathStyle` | `true` | | `driver.options.credential.accessKeyId` | Account ID / Username | | `driver.options.credential.secretAccessKey` | Secret Key / Password | The full list of valid options under `driver.options` is available in the [AWS S3Client documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/s3clientconfig.html){rel=""nofollow""}. For example: ```yaml driver: type: s3 quota: 104857600 options: bucket: flowforge-files credentials: accessKeyId: XXXXXXXXXXXXXXXXXXX secretAccessKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX forcePathStyle: true region: us-east-1 ``` #### Enabling the FlowFuse File Nodes The FlowFuse File nodes have been written to be direct replacements for the Node-RED core file-in and file-out nodes. This means that only one version of these nodes can be active in Node-RED at a time. The FlowFuse File nodes will automatically disable themselves if the core nodes are present. This means to enable the nodes you need to exclude the code nodes. This can be done in the FlowFuse Template. ![](https://flowfuse.com/docs/install/images/file-node-template.png){width="500"} Adding `10-file.js` to the list of "Excluded nodes by filename" section will ensure that the core file nodes are not loaded by Node-RED. ### Persistent Context configuration The Context Storage configuration determines where Node-RED Context data is stored. This feature is only available when running with a FlowFuse Premium license. Due to the different access patterns for context data, this requires a separate storage configuration to the File store. It can use either an SQLite or PostgreSQL database. #### SQLite | Option | Description | | ------------------------- | -------------------------------------------------------------------------------------------------------------- | | `context.type` | `sequelize` | | `context.quote` | A per-instance quota for how much data will be stored - in bytes. If this is not set, no limit will be applied | | `context.options.type` | `sqlite` | | `context.options.storage` | Path to the sqlite database file to use | For example: ```yaml context: type: sequelize quota: 1048576 options: type: sqlite storage: ff-context.db ``` #### PostgreSQL | Option | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------- | | `context.type` | `sequelize` | | `context.quote` | A per-instance quota for how much data will be stored - in bytes. If this is not set, no limit will be applied | | `context.options.type` | `postgres` | | `context.options.host` | The hostname of the database server | | `context.options.port` | The port of the database server | | `context.options.database` | The name of the database to store context data in | | `context.options.username` | The username to access to the database with | | `context.options.password` | The password to access to the database with | For example: ```yaml context: type: sequelize quota: 1048576 options: type: postgres host: flowforge-postgresql port: 5432 database: ff-context username: postgres password: password ``` ## Working with FlowFuse Devices The FlowFuse Device Agent does *not* use the File Storage service. Any flows using the file nodes or persistent context that are deployed to a Device will use the local filesystem directly. # First-run Setup Following a successful install, you will be able to access the platform to go through the initial setup. ## 1. Start setup ### - LocalFS Open FlowFuse in your browser `http://localhost:3000` ### - Docker or Kubernetes Open FlowFuse in your browser `http://forge.example.com` (Change `.example.com` to match the domain you set up in DNS) Click the **START SETUP** button ![](https://flowfuse.com/docs/install/images/setup-01.png){width="500"} ## 2. Create an Administrator The first user you create will be an Administrator. They will have full access to the platform, be able to set platform-wide configuration and manage users and teams. **Note**: with the 0.3 release, it is possible to reset your password *if* you have email configured and enabled the `user:reset-password` option in Admin settings. Otherwise, make sure you make a note of the password you set. We will provide tools to manage passwords outside of the platform in a future release. ![](https://flowfuse.com/docs/install/images/setup-02-user.png){width="500"} ## 3. Upload a license FlowFuse Community Edition is Open Source and can be used freely without a license. If you have a FlowFuse Enterprise Edition license you can upload it here. ![](https://flowfuse.com/docs/install/images/setup-03-license.png){width="500"} ## 4. Finish setup Once you complete the setup, you will be able to log in as the Administrator user you created and start using the platform. You can setup your Team and create your first Node-RED instance. More information about using the FlowFuse platform is available in the main [user guide](https://flowfuse.com/docs/user/introduction). # Installing FlowFuse FlowFuse can be installed to run in Docker or Kubernetes based environments. - **Docker:** - [Quick Start Guide](https://flowfuse.com/docs/quick-start) - [Full Install](https://flowfuse.com/docs/install/docker/) - **Kubernetes:** - [Install Guide](https://flowfuse.com/docs/install/kubernetes/) We also provide one-click installs of the Docker version: - [Digital Ocean Docker Install Guide](https://flowfuse.com/docs/install/docker/digital-ocean) - [AWS Docker Install Guide](https://flowfuse.com/docs/install/docker/aws-marketplace) ## Upgrading FlowFuse If you are upgrading FlowFuse, please refer to the [Upgrade Guide](https://flowfuse.com/docs/upgrade/) for any specific actions required. ## Do You Need Help? Installation Service If you need assistance, request our complimentary Installation Service, and we will help you install FlowFuse. # AWS EKS Specific details This document includes details of installing FlowFuse on AWS EKS. The following assumptions have been made in the examples: 1. The user has the correct AWS IAM policy access to complete all tasks 2. All AWS services are running in `eu-west-1` ## Prerequisites ### AWS Cli This is used to interact with the whole AWS environment {rel=""nofollow""} From here onwards this document assumes that you have configured the AWS CLI tools with a user that has permission to carry out the steps. This document does not include details of how to configure such a user in AWS IAM. Please show this document to you AWS Account Admin if you need help. ### eksctl This tool is used to create/modify AWS EKS Clusters, it uses the credentials from the AWS Cli. {rel=""nofollow""} ## Setup a new domain on Route53 Do this in the AWS Console/Your DNS provider ## Create an AWS Certificate Request a certificate for `*.[DOMAIN]` from Amazon Certificate Manager Do this in AWS Console, with Route53 validation ## Create EKS Cluster Edit the `cluster.yml` file in `aws_eks` to set your preferred instance type and count along with AWS Region ```bash eksctl create cluster -f cluster.yml ``` Example cluster.yml (Please visit [eksctl.io](https://eksctl.io/usage/creating-and-managing-clusters/#using-config-files){rel=""nofollow""} to be sure you understand what this does.) ```yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: FlowFuse region: eu-west-1 iam: withOIDC: true addons: - name: aws-ebs-csi-driver resolveConflicts: overwrite nodeGroups: - name: management labels: role: "management" instanceType: t2.small desiredCapacity: 1 volumeSize: 20 ssh: allow: false iam: withAddonPolicies: ebs: true - name: instance labels: role: "projects" tags: k8s.io/cluster-autoscaler/enabled: "true" k8s.io/cluster-autoscaler/flowforge: "owned" instanceType: t2.small desiredCapacity: 2 volumeSize: ssh: allow: false ``` ## Ingress Controller ### Traefik It is recommended to run the [Traefik](https://doc.traefik.io/traefik/) even on AWS EKS (The AWS ALB load balancer currently appears to only support up to 100 Ingress Targets which limits the number of Hosted Instances that can be run). Create a `traefik-values.yaml` file to pass the values to the Traefik helm file. ```bash touch traefik-values.yaml ``` Fill the `traefik-values.yaml` file with the following content. Replace `` with the certificate ARN created earlier ```yaml service: enabled: true type: LoadBalancer annotations: service.beta.kubernetes.io/aws-load-balancer-type: "nlb" service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "" service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "120" service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: "proxy_protocol_v2.enabled=true" spec: externalTrafficPolicy: Cluster deployment: replicas: 2 ports: web: port: 8000 expose: default: true exposedPort: 80 protocol: TCP forwardedHeaders: trustedIPs: - "10.0.0.0/8" proxyProtocol: trustedIPs: - "10.0.0.0/8" websecure: port: 8443 expose: default: true exposedPort: 443 protocol: TCP http: middlewares: - traefik-force-https@kubernetescrd - traefik-large-body@kubernetescrd # Disable TLS since NLB handles termination tls: enabled: false forwardedHeaders: trustedIPs: - "10.0.0.0/8" proxyProtocol: trustedIPs: - "10.0.0.0/8" ingressClass: enabled: true isDefaultClass: false name: traefik additionalArguments: - "--entryPoints.web.proxyProtocol.insecure=true" - "--entryPoints.websecure.proxyProtocol.insecure=true" - "--entryPoints.web.forwardedHeaders.insecure=true" - "--entryPoints.websecure.forwardedHeaders.insecure=true" providers: kubernetesIngress: enabled: true kubernetesCRD: enabled: true api: dashboard: false insecure: false logs: access: enabled: true fields: headers: defaultMode: keep extraObjects: - apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: force-https namespace: traefik spec: headers: customRequestHeaders: X-Forwarded-Proto: "https" - apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: large-body namespace: traefik spec: buffering: maxRequestBodyBytes: 10485760 ``` Install the Traefik with the following command: ```bash helm repo add traefik https://traefik.github.io/charts helm repo update helm upgrade --install traefik traefik/traefik \ --create-namespace \ -n traefik \ -f traefik-values.yaml \ --wait \ --atomic ``` ### References {rel=""nofollow""} ## AWS ALB Ingress AWS ALB has a hard limit of 100 Ingress endpoints which limits the number of Projects/Instances that can be deployed. ## Setup AWS SES for email {rel=""nofollow""} Setup identity to match sending domain (requires DNS entries) Setup email identity to send test emails to Request move to production from sandbox (need to include examples of emails being sent and why/when those emails will be sent should only need this for prod) `ses_policy.json` (with suitable aws id, aws region and domain modifications): ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "ses:SendEmail", "ses:SendTemplatedEmail", "ses:SendRawEmail" ], "Resource": "arn:aws:ses:[aws region]:[aws id]:identity/[domain name]" } ] } ``` ```bash IAM_POLICY_ARN=$(aws iam create-policy --policy-name FlowForgeSendEmail --policy-document file://ses_policy.json --output json | jq -r .Policy.Arn) ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) OIDC_PROVIDER=$(aws eks describe-cluster --name flowforge --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") read -r -d '' TRUST_RELATIONSHIP < trust.json aws iam create-role --role-name flowforge_service_account_role --assume-role-policy-document file://trust.json --description "Role to bind to flowforge service account" aws iam attach-role-policy --role-name flowforge_service_account_role --policy-arn=$IAM_POLICY_ARN ``` Make a note of the ARN for the IAM Role (flowforge\_service\_account\_role) is needed in the helm chart values yaml file. `aws iam get-role --role-name flowforge_service_account_role` ### References Create a IAM Role to bind IAM Policies to the service account {rel=""nofollow""} Create IAM Policy to allow sending emails (example: {rel=""nofollow""}) ## Use AWS RDS PostgreSQL instance The following script creates a AWS RDS PostgreSQL instance, it also sets up some network access rules so only the FlowFuse app can access it from inside the cluster (and not the Node-RED instances). Please read it carefully before running it to ensure you understand it. A copy of this file can be found [here](https://github.com/FlowFuse/flowforge/blob/f8c06e3cea0ffb539350797af429f1a0366243f1/docs/install/kubernetes/setup-rds.sh){rel=""nofollow""} Run the following command ```bash ./setup-rds.sh ``` Make a note of the postgres hostname ```bash aws rds describe-db-instances | jq .DBInstances[].Endpoint.Address ``` ### References {rel=""nofollow""} # FlowFuse-oriented infrastructure in AWS using Terraform and Helm This step-by-step guide will help you use the Terraform modules available in the FlowFuse repository to setup resources required to run FlowFuse platform on AWS. After following the commands in this documentation, the following resources will be created: - [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) - [EKS](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html) (Elastic Kubernetes Service) - [RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html) (Relational Database Service) - [VPC Peering](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-peering.html) - [Route53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html) domain zone - [AWS Certificate](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html) - [SES](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/Welcome.html) (Simple Email Service) The full list of the resources created by each module can be found in the documentation of the respective modules. To ensure proper infrastructure deployment, these modules must be executed in a specific order: `vpc`, `eks`, `rds`, `vpc-peering`, `route53` and `ses`. Additionally, a shared variables file (`terraform.tfvars`) will be created to manage configuration used by all modules. While Terraform supports nested modules for complex infrastructure, this guide will treat each module as a root module for simplicity. All modules are configurable, allowing you to customize the infrastructure to meet your specific needs. Detailed information on configuring each module can be found in its documentation. This guide will uses minimal configuration to demonstrate the basic setup. ## Prerequisites - [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli), [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl), [Helm](https://helm.sh/docs/helm/helm_install/) and [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed on your machine. - Access to an AWS account through [AWS access key](https://repost.aws/knowledge-center/create-access-key), that allows the creation of new resources. The specific resources created by each module are detailed in the documentation of the respective modules. ## Step 1: Clone the Repository First, clone the FlowFuse Terraform repository to your local machine: ```bash git clone https://github.com/FlowFuse/terraform-aws-flowfuse.git cd terraform-aws-flowfuse ``` ## Step 2: Set AWS Environment Variables Set the following environment variables to authenticate Terraform with your AWS account: ```bash export AWS_ACCESS_KEY_ID= export AWS_SECRET_ACCESS_KEY= export AWS_REGION= ``` Replace ``, ``, and `` with your actual AWS credentials and desired AWS region. All resources must be created in the same region. For example, if you want to create resources in `us-west-2`, set the region as follows: ```bash export AWS_REGION=us-west-2 ``` Ensure that these environment variables are set for the duration of the session or included in your shell profile to persist across sessions. ## Step 3: Create the `terraform.tfvars` File Create a `terraform.tfvars` file in the root directory of the repository. This file will contain the shared variables for all the modules. ```bash touch terraform.tfvars ``` Edit the `terraform.tfvars` file and add the following content. Replace `` with the [ARN](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html) of the AWS user that will have access to the EKS cluster. This user will be granted the `ClusterAdmin` role in the EKS cluster. ```hcl namespace = "my-company" stage = "production" route53_zone_name = "my-domain.com" eks_access_entry_map = { "" = { access_policy_associations = { ClusterAdmin = {} } } } ``` ## Step 4: Create AWS Resources **The order of creating resources is important. The documentation specifies the correct order. Attempts to create resources in a different order may fail.** ### 1. VPC Module To create basic networking, initialize the VPC module: ```bash terraform -chdir=vpc init ``` Apply the VPC module using the shared variables file: ```bash terraform -chdir=vpc apply -var-file=../terraform.tfvars ``` ### 2. EKS Module To create EKS cluster, initialize the EKS module: ```bash terraform -chdir=eks init ``` Apply the EKS module using the shared variables file: ```bash terraform -chdir=eks apply -var-file=../terraform.tfvars ``` ### 3. RDS Module To create RDS database, initialize the RDS module: ```bash terraform -chdir=rds init ``` Apply the RDS module using the shared variables file: ```bash terraform -chdir=rds apply -var-file=../terraform.tfvars ``` ### 4. VPC Peering Module To create a peering between EKS and RDS networks, initialize the VPC Peering module: ```bash terraform -chdir=vpc-peering init ``` Apply the VPC Peering module using the shared variables file: ```bash terraform -chdir=vpc-peering apply -var-file=../terraform.tfvars ``` ### 5. Route53 Module To create a domain and certificate, initialize the Route53 module: ```bash terraform -chdir=route53 init ``` Apply the Route53 module using the shared variables file: ```bash terraform -chdir=route53 apply -var-file=../terraform.tfvars ``` Remember to change NS records in your domain registrar to the ones provided by the Route53 module. NS records are printed after the Route53 module is applied. They can be also printed using the following command: ```bash terraform -chdir=route53 output domain_dns_records ``` ### 6. SES Module To create an email service, initialize the SES module: ```bash terraform -chdir=ses init ``` Apply the SES module using the shared variables file: ```bash terraform -chdir=ses apply -var-file=../terraform.tfvars ``` To get the IAM role ARN created by the SES module and required during [FlowFuse platform configuration](https://flowfuse.com/docs/install/configuration#aws-ses-email), run the following command: ```bash terraform -chdir=eks output flowfuse_role_arn ``` ## Step 5: Deploy Traefik It is recommended to run the [Traefik](https://doc.traefik.io/traefik/) even on AWS EKS (The AWS ALB load balancer currently appears to only support up to 100 Ingress Targets which limits the number of Hosted Instances that can be run). Create a `traefik-values.yaml` file for Traefik configuration: ```bash touch traefik-values.yaml ``` Get the certificate ARN (``) from the `route53` module outputs: ```bash terraform -chdir=route53 output acm_certificate_arn ``` Fill the `traefik-values.yaml` file with the following content. Replace `` with the certificate ARN. ```yaml service: enabled: true type: LoadBalancer annotations: service.beta.kubernetes.io/aws-load-balancer-type: "nlb" service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "" service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "120" service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: "proxy_protocol_v2.enabled=true" spec: externalTrafficPolicy: Cluster deployment: replicas: 2 ports: web: port: 8000 expose: default: true exposedPort: 80 protocol: TCP forwardedHeaders: trustedIPs: - "10.0.0.0/8" proxyProtocol: trustedIPs: - "10.0.0.0/8" websecure: port: 8443 expose: default: true exposedPort: 443 protocol: TCP http: middlewares: - traefik-force-https@kubernetescrd - traefik-large-body@kubernetescrd # Disable TLS since NLB handles termination tls: enabled: false forwardedHeaders: trustedIPs: - "10.0.0.0/8" proxyProtocol: trustedIPs: - "10.0.0.0/8" ingressClass: enabled: true isDefaultClass: false name: traefik additionalArguments: - "--entryPoints.web.proxyProtocol.insecure=true" - "--entryPoints.websecure.proxyProtocol.insecure=true" - "--entryPoints.web.forwardedHeaders.insecure=true" - "--entryPoints.websecure.forwardedHeaders.insecure=true" providers: kubernetesIngress: enabled: true kubernetesCRD: enabled: true api: dashboard: false insecure: false logs: access: enabled: true fields: headers: defaultMode: keep extraObjects: - apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: force-https namespace: traefik spec: headers: customRequestHeaders: X-Forwarded-Proto: "https" - apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: large-body namespace: traefik spec: buffering: maxRequestBodyBytes: 10485760 ``` Update your kubeconfig file to point to the EKS cluster and set the correct context ```bash aws eks update-kubeconfig --name $(terraform -chdir=eks output -raw cluster_name) ``` Install the Traefik Ingress controller with the following command: ```bash helm repo add traefik https://traefik.github.io/charts helm repo update helm upgrade --install traefik traefik/traefik \ --create-namespace \ -n traefik \ -f traefik-values.yaml \ --wait \ --atomic ``` ## Conclusion You have successfully set up the necessary AWS infrastructure to run the FlowFuse platform and performed minimal EKS cluster configuration required. With this infrastructure in place, your environment is now ready for the installation of the FlowFuse platform. For detailed installation instructions, please visit: [FlowFuse Kubernetes Installation Guide](https://flowfuse.com/docs/install/kubernetes). # Installing FlowFuse on a Digital Ocean Kubernetes cluster ## Prerequisites ### Digital Ocean Account You will need an active Digital Oceans Account, you can sign up for an account [here](https://cloud.digitalocean.com/registrations/new){rel=""nofollow""} ### Utilities - kubectl - {rel=""nofollow""} - helm - {rel=""nofollow""} ### DNS You will need a domain that FlowFuse will run on and access to configure a wildcard entry for that domain in it's root DNS server. In this guide I will use `example.com` as the domain, remember to substitute your domain. ## Create Cluster - Click on the big green "Create" button at the top of the screen - Select "Kubernetes" from the list - Pick a suitable region (normally the one physically closest to you) - Reduce the number of nodes from 3 to 2 - Reduce the "Node Plan" to 1GB Ram/2vCPU - Change the cluster name to `k8s-flowforge` - Hit "Create Cluster" button When the cluster has finished provisioning you should be able to download the `k8s-flowforge-kubeconfig.yaml` file which will allow you to connect to the cluster. ## Install Traefik Prepare values for the Traefik Helm chart by creating a file called `traefik-values.yaml` with the following content: ```yaml service: enabled: true type: LoadBalancer annotations: service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true" spec: externalTrafficPolicy: Cluster deployment: replicas: 2 ports: web: port: 8000 expose: default: true exposedPort: 80 protocol: TCP forwardedHeaders: trustedIPs: - "10.0.0.0/8" proxyProtocol: trustedIPs: - "10.0.0.0/8" websecure: port: 8443 expose: default: true exposedPort: 443 protocol: TCP http: middlewares: - traefik-force-https@kubernetescrd - traefik-large-body@kubernetescrd # Disable TLS since NLB handles termination tls: enabled: false forwardedHeaders: trustedIPs: - "10.0.0.0/8" proxyProtocol: trustedIPs: - "10.0.0.0/8" ingressClass: enabled: true isDefaultClass: false name: traefik additionalArguments: - "--entryPoints.web.proxyProtocol.insecure=true" - "--entryPoints.websecure.proxyProtocol.insecure=true" - "--entryPoints.web.forwardedHeaders.insecure=true" - "--entryPoints.websecure.forwardedHeaders.insecure=true" providers: kubernetesIngress: enabled: true kubernetesCRD: enabled: true api: dashboard: false insecure: false logs: access: enabled: true fields: headers: defaultMode: keep extraObjects: - apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: force-https namespace: traefik spec: headers: customRequestHeaders: X-Forwarded-Proto: "https" - apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: large-body namespace: traefik spec: buffering: maxRequestBodyBytes: 10485760 ``` Install the Traefik with the following command: ```bash helm repo add traefik https://traefik.github.io/charts helm repo update helm --kubeconfig=./k8s-flowforge-kubeconfig.yaml upgrade --install traefik traefik/traefik \ --create-namespace \ -n traefik \ -f traefik-values.yaml \ --wait \ --atomic ``` ### Setup DNS Run the following to get the external IP address of the Traefik controller ```bash kubectl --kubeconfig=./k8s-flowforge-kubeconfig.yaml \ -n traefik get service traefik ``` You will need to update the entry in your DNS server to point `*.example.com` to the IP address listed under `EXTERNAL-IP` ## Install Cert-manager This will use LetsEncrypt to issue certificates for both the FlowFuse application but also for the Node-RED instances. ```bash helm repo add jetstack https://charts.jetstack.io helm repo update helm install \ --kubeconfig=./k8s-flowforge-kubeconfig.yaml \ cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --version v1.13.3 \ --set installCRDs=true ``` After installing you will need to create a `ClusterIssuer` to access LetsEncrypt. Create the following YAML file called `letsencrypt.yml`, please replace `user@example.com` with your email address. ```yaml apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt spec: acme: # The ACME server URL server: https://acme-v02.api.letsencrypt.org/directory # Email address used for ACME registration email: user@example.com # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-prod # Enable the HTTP-01 challenge provider solvers: - http01: ingress: ingressClassName: traefik ``` Then use `kubectl` to install this ```bash kubectl --kubeconfig=./k8s-flowforge-kubeconfig.yaml apply -f letsencrypt.yml ``` ## Install FlowFuse Then setup the FlowFuse Helm repository ```bash helm repo add flowforge https://flowfuse.github.io/helm helm repo update ``` Now create a `customizations.yml` file. This is how we configure the FlowFuse instance. The following is the bare minimum to get started: ```yaml forge: domain: example.com https: true localPostgresql: true projectSelector: managementSelector: broker: enabled: true postgresql: global: storageClass: do-block-storage ingress: certManagerIssuer: letsencrypt ``` Again, please replace `example.com` with the domain you configured earlier in the [Setup DNS](https://flowfuse.com/#setup-dns) section Then we use this to install FlowFuse ```bash helm upgrade --install --kubeconfig ./k8s-flowforge-kubeconfig.yaml \ flowforge flowforge/flowforge -f customizations.yml \ --wait ``` Once complete you should be able to sign into the FlowFuse setup wizard at `http://forge.example.com` # Kubernetes Install This guide walks you through a detailed set up of FlowFuse Platform on a container environment managed by Kubernetes. Typically suited for large on premise deployments or deployment in Cloud infrastructure. By the end, you will have a fully functioning FlowFuse instance running on a Kubernetes cluster. # Checklist ::div{.grid.grid-cols-2.gap-8} :::div{.checklist} Prerequisites ::::div :checklist-item{task="Domain Name"} :checklist-item{task="Kubernetes cluster"} :checklist-item{task="Setup Dedicated Database" type="recommended"} :checklist-item{task="Prepare TLS Certificates" type="recommended"} :::: ::: :::div{.checklist} Installation ::::div :checklist-item{task="Download FlowFuse"} :checklist-item{task="Configure FlowFuse"} :checklist-item{task="Enable HTTPS" type="recommended"} :::: ::: :: ## Prerequisites Before you begin, ensure you have the following: 1. **Domain Name & DNS:** A domain name that you own and can configure DNS settings for (explained in [DNS](https://flowfuse.com/#dns)) 2. **kubectl:** To manage a Kubernetes cluster you will need a copy of the `kubectl` utility. See the [kubectl install docs](https://kubernetes.io/docs/tasks/tools/){rel=""nofollow""} 3. **Helm:** FlowFuse provides the Helm chart to manage platform deployment. Install it by following the instructions on [their website](https://helm.sh){rel=""nofollow""} 4. **Kubernetes Cluster:**The deployment has currently been tested on the following environments: - [AWS EKS](https://flowfuse.com/docs/install/kubernetes/aws-terraform) - [Digital Ocean](https://flowfuse.com/docs/install/kubernetes/digital-ocean) - MicroK8s 5. **Ingress Controller:** Install [Traefik](https://doc.traefik.io/traefik/){rel=""nofollow""} on the cluster. 6. **Cert-Manager:** EMQX requires the CRDs from cert-manager. See the [installation instructions](https://cert-manager.io/docs/installation/){rel=""nofollow""} for details. 7. **EMQX Operator:** This installs the operator that deploys the platform's MQTT broker, which is required whenever the broker is enabled. You must install exactly version 2.2.29 — later versions are not supported. Follow the [installation instructions](https://docs.emqx.com/en/emqx-operator/latest/getting-started/getting-started.html#install-emqx-operator){rel=""nofollow""} and pin the version by adding `--version 2.2.29` to the install command. For a production-ready environment, we also recommend: - **Database:** Prepare dedicated database on an external database server (see [FAQ](https://flowfuse.com/#how-to-use-external-database-server) for more details) - **TLS Certificate:** Prepare a TLS certificate for your domain and configure FlowFuse platform to use it (see [Enable HTTPS](https://flowfuse.com/#i-would-like-to-secure-the-platform-with-https-how-can-i-do-that)) ### Hardware requirements For a Kubernetes-based deployment, resource requirements depend on the number of FlowFuse and Node-RED instances running. As a baseline, we suggest: Control Plane: At least 2 vCPUs, 4 GB RAM Worker Nodes: Minimum 2 vCPUs, 4 GB RAM per node, 2 nodes for high availability Storage: 20Gb of host storage (for container images), StorageClass of your choice available for Hosted Node-RED instances (optional) Each Node-RED instance you host will use 0.1 CPU cores and 256 MB of memory by default. These parameters can be adjusted in admin area of FlowFuse platform. Keep this in mind when sizing your hardware, especially if you plan to create multiple hosted instances. ### DNS You will need a [wildcard DNS entry](https://en.wikipedia.org/wiki/Wildcard_DNS_record){rel=""nofollow""} pointing to the domain that is used for the project instances. This will need to point to the kubernetes Ingress controller. For example if you want projects to be accessible as `[instance-name].example.com` you will need to ensure that `*.example.com` is mapped to the IP address used by your Kubernetes cluster's Ingress controller. By default the FlowFuse application will be mapped to `forge.example.com` assuming that you set the domain to `example.com`. Notes on how to setup DNS can be found [here](https://flowfuse.com/docs/install/dns-setup). ## Installing FlowFuse ### Add FlowFuse Helm Repository ```bash helm repo add flowfuse https://flowfuse.github.io/helm helm repo update ``` ### Customize Helm Chart All the initial configuration is handled by the Helm chart. This is done by creating a `customization.yml` file that will be passed to the Helm along with the chart. To create `customization.yml` file with a minimal required configuration (replace `example.com` with your domain): ```bash cat < customization.yml forge: entryPoint: forge.example.com domain: example.com https: false localPostgresql: true EOF ``` A full list of all the configuration options can be found in the [Helm Chart README](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#configuration-values){rel=""nofollow""}. ### Label Nodes By default FlowFuse platform expects that Kubernetes nodes have specific labels applied. The main reason behind this approach is to separate core application components from Node-RED instances. You will need to label at least one node to run the management application and one to run the Node-RED Projects: List all nodes in the cluster: ```bash kubectl get nodes ``` Label management nodes: ```bash kubectl label node role=management ``` Label project nodes: ```bash kubectl label node role=projects ``` To override this behavior, you can remove the node selectors with the following entry in the `customization.yml` file which will mean that all pods can run on any nodes. ```yaml [customization.yml] forge: projectSelector: managementSelector: ``` ## Start FlowFuse Platform Once you have the `customization.yml` file created, you can install FlowFuse using our Helm chart. This will automatically create all required objects and start services: ```bash helm upgrade --atomic --install --timeout 10m flowfuse flowfuse/flowfuse -f customization.yml ``` ## First Run Setup The first time you access the platform in your browser, it will take you through creating an administrator for the platform and other configuration options. For more information, follow [this guide](https://flowfuse.com/docs/install/first-run). Once you have finished setting up the admin user there are some [Kubernetes specific items to consider](https://flowfuse.com/#common-questions). ## Upgrade All technical aspects of the upgrade process of Flowfuse application running on Kubernetes and managed by Helm chart are maintained in our repository. Please refer to the [Flowfuse Helm Chart documentation](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#upgrading-chart){rel=""nofollow""} for more details about the upgrade process. ## Common Questions ### I would like to secure the platform with HTTPS, how can I do that? In cloud environments, it is recommended to use a Load Balancer to terminate SSL traffic. However, if you want to use SSL termination on the Kubernetes Ingress Controller, this is possible by using the [Cert-Manager](https://cert-manager.io/docs/){rel=""nofollow""} tool (not part of the FlowFuse Helm chart). Once you have Cert-Manager installed, you can enable TLS support in the `customization.yml` file by specifying the [ClusterIssuer](https://cert-manager.io/docs/configuration/#cluster-resource-namespace){rel=""nofollow""} name: ```yaml [customization.yml] {2} ingress: clusterIssuer: ``` Apply changes with [platform startup command](https://flowfuse.com/#start-flowfuse-platform). #### Using Internal/Private Certificate Authorities If you are issuing your own HTTPS certificates (rather than using a Public Certificate Authority) you will need to tell the Hosted Node-RED Instances to trust this CA. To do this you need to pass base64 encoded CA certificate chain to the helm chart in the `customization.yml`. Details are in the Helm chart [README.md](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#private-certificate-authority){rel=""nofollow""}. ```yaml [customization.yml] {3} forge: privateCA: certs: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk... ``` Assuming the certificate chain is held in `chain.pem` the value would be created by running `base64 -w 0 certs.pem` ### I use Kubernetes Network Policies, how can I configure them? If your cluster uses Network Policies to restrict traffic between namespaces, you'll need to create appropriate policies. Here's an example Network Policy that allows traffic from the `flowforge` namespace (default namespace for Node-RED instances) to the `flowfuse` namespace: ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-flowforge-to-flowfuse namespace: flowfuse spec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: flowforge policyTypes: - Ingress ``` You may need to adjust this policy based on your specific network requirements and namespace configuration. ### How to use external database server? FlowFuse platform uses PostgreSQL database to store its data. By default, the Helm chart creates and manages the internal database instance. If you want to use an external database server, you need to edit `customization.yml` file and provide the database connection details: ```yaml [customization.yml] {2} forge: localPostgresql: false # Disable internal database postgresql: host: port: auth: username: password: database: ``` Apply changes with [platform startup command](https://flowfuse.com/#start-flowfuse-platform). Check the [FlowFuse Helm chart documentation](https://github.com/FlowFuse/helm/tree/main/helm/flowfuse#postgresql){rel=""nofollow""} for more details about the parameters that can be configured for the PostgreSQL database. ### How to backup embedded database? If you are using the internal database (value `forge.localPostgresql` set to `true`), you can use Kubernetes [CronJobs](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/){rel=""nofollow""} to backup the database. Apply below `CronJob` and `PersistentVolumeClaim` definitions to create a backup job which will be executed every day at 23:05 and store the backup in a PVC named `db-backup-pvc`: ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: db-backup-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi --- apiVersion: batch/v1 kind: CronJob metadata: name: postgres-backup spec: schedule: "5 23 * * *" jobTemplate: spec: ttlSecondsAfterFinished: 60 template: metadata: labels: app: flowforge spec: containers: - name: backup image: postgres env: - name: PGPASSWORD valueFrom: secretKeyRef: name: flowfuse-postgresql key: postgres-password command: - /bin/sh - -c - | pg_dump -h flowfuse-postgresql -U postgres -d flowforge -F c -b -v -f /backup/db_backup.dump volumeMounts: - name: backup-volume mountPath: /backup restartPolicy: OnFailure volumes: - name: backup-volume persistentVolumeClaim: claimName: db-backup-pvc ``` ### I would like to invite my team members to the platform with e-mail, how can I do that? FlowFuse platform allows you to invite team members to the platform using their e-mail addresses. To enable this feature, you need to configure the e-mail settings in the `customization.yml` file. Check this [page](https://flowfuse.com/docs/install/configuration#email-configuration) for more details about the parameters. Check [FlowFuse Helm chart documentation](https://github.com/FlowFuse/helm/tree/main/helm/flowfuse#email){rel=""nofollow""} for information where configuration values should be placed in `customization.yml` file. If you use AWS EKS (Elastic Kubernetes Service) and want to use AWS SES (Simple Email Service) for sending e-mails, you need to provide the IAM role with the required permissions to use SES. ```yaml [customization.yml] {6} forge: entryPoint: forge.example.com domain: example.com cloudProvider: aws aws: IAMRole: arn:aws:iam:::role/flowforge_service_account_role email: ses: region: eu-west-1 ``` Apply changes with [platform startup command](https://flowfuse.com/#start-flowfuse-platform). ### I would like to use embedded MQTT broker, how can I do that? Click to expand The FlowFuse Helm chart provides the MQTT broker service. To enable the MQTT broker you need to add the following to the \`customization.yml\` file: \`\`\`yaml \[customization.yml] forge: broker: enabled: true \`\`\` Apply changes with \[platform startup command]\(#start-flowfuse-platform). Check the \[FlowFuse Helm chart documentation]\(https\://github.com/FlowFuse/helm/tree/main/helm/flowfuse#mqtt-broker) for more details about the parameters that can be configured for the MQTT broker. ### I would like to use Kubernetes Persistent storage to store data, how can I do that? Starting with the `2.6.0` release the Pods running the Node-RED Instances have a Persistent Volume mounted on `/data/storage` in which files can be written. These files will persist for the lifetime of the Instance including across Suspend/Resume and Stack upgrades. To enable this feature the following configuration needs to be added to the `customization.yml` file (replace ' :storage-class-name[' with the name of the StorageClass you have in the cluster):] ```yaml [customization.yml] {5} forge: persistentStorage: enabled: true size: 5Gi storageClass: ``` Apply changes with [platform startup command](https://flowfuse.com/#start-flowfuse-platform). ### I would like to use FlowFuse File Storage to store context data, how can I do that? To enable the FlowFuse File Storage component add the following to the `customization.yml` file: ```yaml [customization.yml] forge: fileStore: enabled: true ``` Apply changes with [platform startup command](https://flowfuse.com/#start-flowfuse-platform). Check the [FlowFuse Helm chart documentation](https://github.com/FlowFuse/helm/tree/main/helm/flowfuse#file-storage){rel=""nofollow""} for more details about the parameters that can be configured for the File Storage. ### I would like to run FlowFuse on AWS EKS. Do you have any guidance? Yes, we have a dedicated guide on how to deploy FlowFuse on AWS EKS. You can find it [here](https://flowfuse.com/docs/install/kubernetes/aws). Furthermore, we also provide terraform scripts to automate the deployment process of all required AWS service. You can find the guide [here](https://flowfuse.com/docs/install/kubernetes/aws-terraform). # Ingress Controller Migration ::div{.ff-callout.ff-callout--note} Note :::div{.ff-callout__content} This guide should not be treated as a one-size-fits-all solution. Consider it as a blueprint and adapt it to your specific Kubernetes cluster setup. Test the migration in a testing/staging environment before applying it to production. If you have any questions about the migration, please contact . ::: :: This document describes how to migrate a FlowFuse Platform Kubernetes deployment from the NGINX Ingress Controller to Traefik. The main reason for this migration is [the retirement of Ingress NGINX](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/){rel=""nofollow""} by the Kubernetes project due to long-term maintenance and security sustainability challenges. Best-effort maintenance continues only until March 2026, after which there will be no further releases, bug fixes, or security updates. It describes a DNS-based migration approach where the actual traffic switch happens at the domain level by changing DNS records to point to the new ingress controller. It is based on the FlowFuse Ingress Migration Tool and using the ingress classes separation capability provided by the FlowFuse Helm chart: - Hosted Instances (project ingresses) via `forge.projectIngressClassName` - Core application components via `ingress.className` That separation allows you to migrate project traffic and core application traffic in stages. ## Prerequisites Before you begin, ensure you have the following: 1. A working FlowFuse deployment installed with the `flowfuse/flowfuse`Helm chart - note down the FlowFuse Platform Helm release name and namespace, guide uses `` and `` placeholders for these values. 2. Access to the Helm values file used for your deployment - note down the path to the values file, guide uses `` placeholder as a reference 3. `kubectl` and `helm` configured for the target cluster 4. A new Traefik ingress controller installed alongside the existing NGINX ingress controller. Check our [ingress installation guide](https://flowfuse.com/docs/install/kubernetes/aws/#ingress-controller){rel=""nofollow""} for instructions on how to do this. 5. Access to the DNS records for: - the FlowFuse application domain - the wildcard domain used by Hosted Instances 6. A maintenance window We also recommend the following before starting the migration: - Reduce DNS TTL values in advance to minimize propagation delay - Do not create new Hosted Instances during the migration window - Test the procedure in a non-production cluster first ## Important notes The FlowFuse Helm chart provides an ingress migration job controlled by the `ingressMigration` values block. The migration job operates on: - the project namespace (`forge.projectNamespace`) - the release namespace, but only in `copy` mode This guide therefore separates the process into two phases: 1. Copy existing ingress resources to the new ingress class 2. Clean up old ingress resources after traffic has moved ## Step 1: Install Traefik alongside NGINX Install Traefik without removing the existing NGINX ingress controller. Check our [ingress installation guide](https://flowfuse.com/docs/install/kubernetes/aws/#ingress-controller){rel=""nofollow""} for instructions on how to do this. Before continuing, verify that both ingress controllers are running. Also, write down the ingress class name used by Traefik, as you will need it for the migration configuration: ```bash kubectl get ingressclass ``` ## Step 2: Record the Traefik LoadBalancer address You will need the public address of the new ingress controller before changing DNS. For example, list services in the Traefik namespace: ```bash kubectl get svc -n traefik ``` Record the external hostname or IP address of the Traefik LoadBalancer service. ## Step 3: Run the migration job in dry-run mode Start by enabling the migration tool in `copy` mode with `dryRun: true`. Update your values file: ```yaml ingressMigration: enabled: true mode: copy dryRun: true newIngressClassName: traefik nameSuffix: -tfk ``` Then run the Helm upgrade: ```bash helm upgrade --install flowfuse/flowfuse -n -f ``` After the upgrade completes, inspect the Job created by the chart in the release namespace. The Job name is generated by Helm, so first list the Jobs: ```bash kubectl get jobs -n ``` Then inspect the logs of the migration job: ```bash kubectl logs -n job/ ``` Verify that the dry run identifies all Ingress resources. ## Step 4: Optionally scale down Traefik before copying resources If your environment allows it, and especially if you have many ingress resources, you can temporarily scale Traefik down before executing the ingress resources copy step. This is optional, but it can reduce repeated Traefik configuration reloads during the migration. Example: ```bash kubectl -n traefik scale deployment traefik --replicas 0 ``` Only do this if your Kubernetes cluster hosts the FlowFuse Platform and stopping Traefik will not cause a downtime for other applications. ## Step 5: Copy ingress resources to the new ingress class Once the dry run has been validated, disable dry-run mode and run the actual ingress resources copy. Update your values file: ```yaml ingressMigration: enabled: true mode: copy dryRun: false newIngressClassName: traefik nameSuffix: -tfk ``` Run the Helm upgrade again: ```bash helm upgrade --install flowfuse/flowfuse -n -f ``` This creates new Ingress resources that point to the new ingress class and use the configured suffix. ## Step 6: Scale Traefik back up If you scaled Traefik down in step 4, scale it back up now. Example: ```bash kubectl -n traefik scale deployment traefik --replicas 2 ``` Use the replica count that matches your environment. ## Step 7: Change DNS to point to Traefik Update the DNS records used by the FlowFuse Platform so that incoming traffic is sent to the Traefik LoadBalancer address recorded earlier. This includes: - the FlowFuse application hostname (e.g. `forge.example.com`) - the wildcard DNS record used by Hosted Instances (e.g. `*.example.com`) Do not proceed to cleanup until you have confirmed that DNS is resolving to the new ingress controller. ## Step 8: Make new Hosted Instances use Traefik After the copied ingress resources are in place, update the project ingress class so that newly created Hosted Instances use Traefik. Add or update the following value in the FlowFuse Helm values file: ```yaml forge: projectIngressClassName: traefik ``` Apply the change: ```bash helm upgrade --install flowfuse/flowfuse -n -f ``` ## Step 9: Wait for DNS propagation and validate traffic Allow time for DNS propagation according to your TTL settings. During this period, validate the migration by checking: - the FlowFuse Platform is reachable - Hosted Instances are reachable - TLS certificates are served correctly - Traefik logs and metrics do not show routing errors - Ingress NGINX logs do not show incoming traffic Do not continue to cleanup until traffic is consistently served by Traefik. Otherwise, you risk potential downtime of the FlowFuse Platform and Hosted Instances. ## Step 10: Move core application components to Traefik Once you are confident that the Traefik ingress controller is handling all the incoming traffic, update the ingress class used by the core application components. Add or update the following value in the FlowFuse Helm values file: ```yaml ingress: className: traefik ``` Apply the change: ```bash helm upgrade --install flowfuse/flowfuse -n -f ``` ## Step 11: Optionally scale down NGINX If the NGINX ingress controller is still used for resources outside FlowFuse, leave it in place. If it is no longer needed, you can scale it down after you have confirmed that FlowFuse Platform traffic is fully handled by Traefik. Example: ```bash kubectl -n scale deployment --replicas 0 ``` If your Ingress-Nginx installation created admission webhooks and you are about to retire that controller entirely soon, remove those webhook resources. Examples: ```bash kubectl delete validatingwebhookconfiguration kubectl delete mutatingwebhookconfiguration --ignore-not-found ``` ## Step 12: Clean up old ingress resources After traffic has fully moved to Traefik, switch the migration tool to `cleanup` mode. In this mode, the tool removes old ingress resources and renames the migrated ones back to their original names. This step can cause a short interruption while Kubernetes updates the resources. Update your values file: ```yaml ingressMigration: enabled: true mode: cleanup dryRun: false oldIngressClassName: nginx newIngressClassName: traefik nameSuffix: -tfk ``` Run the Helm upgrade again: ```bash helm upgrade --install flowfuse/flowfuse -n -f ``` If you want to validate the cleanup plan first, run the same configuration with `dryRun: true` before applying it. ## Step 13: Remove any remaining suffixed ingress resources from the release namespace The migration job in `copy` mode creates new ingress resources with a suffix in the release namespace. In `cleanup` mode, the job deletes old ingress resources and renames the new ones to match the original names, but it does not remove the suffixed copies from the release namespace. This is to avoid any potential issues with the release namespace if it contains non-ingress resources or custom configurations. Because of that, after cleanup you should inspect the release namespace and remove any remaining suffixed ingress resources that were copied there. List ingress resources in the release namespace: ```bash kubectl get ingress -n ``` If any copied ingress resources with the migration suffix still exist (`-tfk`), delete them manually: ```bash kubectl delete ingress -n ``` ## Step 14: Disable the migration job After the migration and cleanup are complete, disable the migration tool: ```yaml ingressMigration: enabled: false ``` Apply the change one final time: ```bash helm upgrade --install flowfuse/flowfuse -n -f ``` ## Related configuration values This guide uses the following FlowFuse Helm chart values: - `ingress.className` - `forge.projectIngressClassName` - `ingressMigration.enabled` - `ingressMigration.mode` - `ingressMigration.dryRun` - `ingressMigration.newIngressClassName` - `ingressMigration.oldIngressClassName` - `ingressMigration.nameSuffix` For the full list of ingress migration options, refer to the [FlowFuse Helm chart README](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#ingress-migration-tool){rel=""nofollow""}. ## Need help? If you would like guidance on planning this migration, we can provide migration consultation. To discuss this option, contact . ## Resources - [Ingress Nginx retirement announcement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/){rel=""nofollow""} - [Zero-downtime Ingress controller migration guide](https://georg-schwarz.com/blog/zero-downtime-ingress-controller-migration-kubernetes/){rel=""nofollow""} - [Migrate from Ingress NGINX Controller to Traefik](https://doc.traefik.io/traefik/migrate/nginx-to-traefik/){rel=""nofollow""} # OpenShift Install This guide walks you through detailed set up of FlowFuse Platform on a container envoronment managed by OpenShift. Typically suited for large on premise deployments or deployment in Cloud infrastructure. By the end, you will have a fully functioning FlowFuse instance running on a OpenShift cluster. # Checklist ::div{.grid.grid-cols-2.gap-8} :::div{.checklist} Prerequisites ::::div :checklist-item{task="Domain Name"} :checklist-item{task="OpenShift cluster"} :checklist-item{task="FlowFuse License"} :checklist-item{task="Setup Dedicated Database" type="recommended"} :checklist-item{task="Prepare TLS Certificates" type="recommended"} :::: ::: :::div{.checklist} Installation ::::div :checklist-item{task="Download FlowFuse"} :checklist-item{task="Configure FlowFuse"} :checklist-item{task="Enable HTTPS" type="recommended"} :::: ::: :: ## Prerequisites Before you begin, ensure you have the following: 1. **Domain Name & DNS:** A domain name that you own and can configure DNS settings for (explained in [DNS](https://flowfuse.com/#dns)) 2. **oc:** To manage a OpenShift cluster you will need a copy of the `oc` utility. Instructions on how to install `oc` can be found [here](https://docs.openshift.com/container-platform/4.17/cli_reference/openshift_cli/getting-started-cli.html){rel=""nofollow""} 3. **Helm:** FlowFuse provides the Helm chart to manage platform deployment. Installation can be done through the instructions on [their website](https://helm.sh){rel=""nofollow""} 4. **OpenShift Cluster:** an OpenShift cluster instance with at least two worker nodes 5. **Ingress Controller:** [The Traefik](https://doc.traefik.io/traefik/){rel=""nofollow""} installed on the cluster. 6. **FlowFuse License:** A valid FlowFuse license key is required to run on OpenShift. You can request a quote [here](https://flowfuse.com/pricing/request-quote/){rel=""nofollow""} For a production-ready environment, we also recommend: - **Database:** Prepare dedicated database on a external database server (see [FAQ](https://flowfuse.com/docs/install/kubernetes#how-to-use-external-database-server) for more details) - **TLS Certificate:** Prepare TLS certificate for your domain and configure FlowFuse platform to use it (see [Enable HTTPS](https://flowfuse.com/docs/install/kubernetes#i-would-like-to-secure-the-platform-with-https-how-can-i-do-that)) ### DNS A [wildcard DNS entry](https://en.wikipedia.org/wiki/Wildcard_DNS_record){rel=""nofollow""} will be needed to point to the domain that is used for the project instances. This will need to point to the Ingress controller. For example if you want projects to be accessible as `[instance-name].example.com` you will need to ensure that `*.example.com` is mapped to the IP address used by your OpenShift clusters's Ingress controller. By default the FlowFuse application will be mapped to `forge.example.com` assuming that you set the domain to `example.com`. Notes on how to setup DNS can be found [here](https://flowfuse.com/docs/install/dns-setup). ## Installing FlowFuse ### Create project in the OpenShift cluster To maintain a clean environment, it is recommended to create a new project for the FlowFuse platform: ```bash oc new-project flowfuse --description="FlowFuse Platform" --display-name="FlowFuse" ``` Describe the project to get the SCC information: ```bash oc describe project flowfuse ``` Note the `openshift.io/sa.scc.uid-range` and `openshift.io/sa.scc.supplemental-groups` values. You will need to use these values when customizing the FlowFuse platform installation. In example, if the `openshift.io/sa.scc.uid-range` value is `1000710000/10000`, the `` value will be `1000710000`. ### Add FlowFuse Helm Repository ```bash helm repo add flowfuse https://flowfuse.github.io/helm helm repo update ``` ### Customize Helm Chart All the initial configuration is handled by the Helm chart. This is done by creating a `customization.yml` file that will be passed to the Helm along with the chart. To create `customization.yml` file with a minimal required configuration (replace `example.com` with your domain and `` with the value from the project description collected on [project creation step](https://flowfuse.com/#create-project-in-the-openshift-cluster)): ```bash cat < customization.yml forge: entryPoint: forge.example.com domain: example.com https: false localPostgresql: true cloudProvider: openshift podSecurityContext: runAsUser: runAsGroup: fsGroup: postgresql: primary: podSecurityContext: fsGroup: containerSecurityContext: runAsUser: EOF ``` A full list of all the configuration options can be found in the [Helm Chart README](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#configuration-values){rel=""nofollow""}. ### Label Nodes By default FlowFuse platform expects that worker nodes have specific labels applied. The main reason behind this approach is to separate core application components from Node-RED instances. You will need to label at least one node to run the management application and one to run the Node-RED Projects: List all nodes in the cluster: ```bash oc get nodes ``` Label management nodes: ```bash oc label node role=management ``` Label project nodes: ```bash oc label node role=projects ``` To override this behavior, you can remove the node selectors with the following entry in the `customization.yml` file which will mean that all pods can run on any nodes. ```yaml forge: projectSelector: managementSelector: ``` ## Start FlowFuse Platform Once you have the `customization.yml` file created, you can install FlowFuse using our Helm chart. This will automatically create all required objects and start services: ```bash helm upgrade --atomic --install --timeout 10m flowfuse flowfuse/flowforge -f customization.yml ``` ## First Run Setup The first time you access the platform in your browser, it will take you through creating an administrator for the platform and other configuration options. For more information, follow [this guide](https://flowfuse.com/docs/install/first-run). Once you have finished setting up the admin user there are some [Kubernetes specific items to consider](https://flowfuse.com/#common-questions). ## Upgrade All technical aspects of the upgrade process of Flowfuse application running on Kubernetes and managed by Helm chart are maintained in our repository. Please refer to the [Flowfuse Helm Chart documentation](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#upgrading-chart){rel=""nofollow""} for more details about the upgrade process. ## Common Questions For non-OpenShift specific questions, please refer to the [main kubernetes documentation](https://flowfuse.com/docs/install/kubernetes#common-questions). ### I would like to use embeded MQTT broker, how can I do that? Click to expand The FlowFuse Helm chart provides the MQTT broker service. To enable the MQTT broker you need to add the following to the \`customization.yml\` file (replace the \` :project-uid[\` with the value from the project description collected on \[project creation step\](#create-project-in-the-openshift-cluster)): \`\`\`yaml forge: broker: enabled: true podSecurityContext: runAsUser: :project-uid[runAsGroup: :project-uid[fsGroup: :project-uid[\`\`\` Apply changes with \[platform startup command\](#start-flowfuse-platform). Check the \[FlowFuse Helm chart documentation\](https://github.com/FlowFuse/helm/tree/main/helm/flowfuse#mqtt-broker) for more details about the parameters that can be configured for the MQTT broker. ]]]] ### I would like to use FlowFuse File Storage to store context data, how can I do that? Click to expand To enable the FlowFuse File Storage component add the following to the \`customization.yml\` file (replace the \` :project-uid[\` with the value from the project description collected on \[project creation step\](#create-project-in-the-openshift-cluster)): \`\`\`yaml forge: fileStore: enabled: true podSecurityContext: runAsUser: :project-uid[runAsGroup: :project-uid[fsGroup: :project-uid[\`\`\` Apply changes with \[platform startup command\](#start-flowfuse-platform). Check the \[FlowFuse Helm chart documentation\](https://github.com/FlowFuse/helm/tree/main/helm/flowfuse#file-storage) for more details about the parameters that can be configured for the File Storage. ]]]] # Kubernetes Stacks A Stack defines a set of platform configuration options that will get applied to each Node-RED instance when created. For container based deployment models, this covers three things: - `memory` - the amount of memory (in MB) to limit container to. Recommended minimum: `256`. - `cpu` - a value between 1 and 100 that is the % of a CPU core the container should be allowed to consume. - `container location` - this is the fully qualified name of the container to use. The default container built when following the install instructions is named `flowfuse/node-red:latest` If you wish to use different Node-RED version, you need to specify the name of the container and the version you want to use. For example, if you want to use Node-RED v3.1.x, you should enter `flowfuse/node-red:latest-3.1.x` in the `container location` section of the Stack configuration. Full list of available pre-built containers can be found on [Docker Hub](https://hub.docker.com/r/flowfuse/node-red/tags){rel=""nofollow""}. ## Creating Own Containers As mentioned in the previous paragraph, we encourage to use our pre-built containers in your stacks. However, if you want to create your own container, you can do so by creating a `Dockerfile` and `package.json` files. There is an example `Dockerfile` and `package.json` in the [node-red-container](https://github.com/FlowFuse/helm/tree/main/node-red-container){rel=""nofollow""} directory of the [helm](https://github.com/FlowFuse/helm){rel=""nofollow""} project. This will start with `nodered/node-red:latest` as it's base and then add the required FlowFuse components. Builds of this container for amd64, arm64 and armv7 are built for every release and published to Docker hub as [flowfuse/node-red](https://hub.docker.com/r/flowfuse/node-red){rel=""nofollow""}. These can be used as a base to build custom stacks. If you wanted to pin at Node-RED v3.0.2 you would change the first line to: ```docker FROM nodered/node-red:3.0.2 ARG REGISTRY RUN if [[ ! -z "$REGISTRY" ]] ; then npm config set @flowfuse:registry "$REGISTRY"; fi COPY package.json /data ... ``` To add nodes to the default image you can extend the supplied container. The following Dockerfile will install the node-red-dashboard ```docker FROM flowfuse/node-red WORKDIR /usr/src/node-red RUN npm install node-red-dashboard WORKDIR /usr/src/flowforge-nr-launcher ``` To build the container run the following: ```shell docker build node-red-container/Dockerfile-dashboard -t [your.container.registry]/flowfuse/node-red-dashboard:3.0.2 docker push [your.container.registry]/flowfuse/node-red-dashboard:3.0.2 ``` You would then enter `[your.container.registry]/flowforge/node-red-dashboard:3.0.2` in the `container` section of the Stack configuration. Stacks can be changed on a per Node-RED instance basis, see also the [user stack documentation](https://flowfuse.com/docs/user/changestack). # Migrating a Node-RED application to FlowFuse This guide will help you to move existing Node-RED instances into FlowFuse. When migrating your Node-RED instances into FlowFuse, you'll export the flows, credentials, and environment variables. Before you start ensure you can log in to FlowFuse Cloud or your own FlowFuse server, and that you have created your Node-RED instance that you wish to move into FlowFuse. If you have not yet created a Node-RED instance that you want to manage within FlowFuse, you can create the new instance within FlowFuse directly, and the following instructions will not apply in your case. ## Migrating the flows and credentials Install the Node-RED tools plugin as explained [in the documentation](https://flowfuse.com/docs/migration/node-red-tools). After you created a snapshot for the Node-RED instance you wish to move, you'll have copied over the flows and credentials. ## Migrating Environment Variables You can use [this flow](https://flows.nodered.org/flow/8ebfe9ae218aa5105e7da13db14ac272){rel=""nofollow""} to dump a list of your environment variables into the debug window. For each variable needed for the flow should be added on FlowFuse under Instance > Settings > Environment tabs. ## Starting the snapshot Under the snapshots tab, click 'Restore Snapshot' in the kebab menu. The migrated flows will now be started with the modules installed. ## Limitations ### Static Files Check your `settings.js` file to see if `httpStatic` has been set, if so then check for any files in this path. The files in this path need to be manually migrated to [FlowFuse's Static Assets](https://flowfuse.com/docs/user/static-asset-service/) service. # FlowFuse Node-RED Tools plugin The Node-RED Tools Plugin is a module you can install into any Node-RED instance running outside of FlowFuse, that gives you the ability to work on your flows locally. The current version of the plugin allows you to create a new Instance Snapshot using the flows you have locally and push them into an instance on FlowFuse. This can make it easier to develop hardware-specific flows locally, that can then be pushed out to your devices through FlowFuse. ## Install This plugin can be installed through the Manage Palette option by searching for `@flowfuse/nr-tools-plugin` in the Node-RED editor, or on the command-line: ```bash cd ~/.node-red npm install @flowfuse/nr-tools-plugin ``` This assumes the default location of the Node-RED user directory. If you are not sure where that is, check the log output when Node-RED starts as it will log the full path to the `User directory`. ## Usage This initial version of the plugin allows you to create a snapshot of your locally developed flows and push them into one of your instances running inside FlowFuse. ### Connecting to FlowFuse Before you can do anything, you need to connect the plugin to a FlowFuse platform. 1. In the Node-RED editor, open the FlowFuse Tools sidebar and click on the cog icon to open the settings panel. 2. Enter the url of your FlowFuse platform. For example, if you have signed-up to [FlowFuse Cloud](https://app.flowfuse.com/){rel=""nofollow""} then use the URL `https://app.flowfuse.com`. 3. Click connect. This will open another window where you can log in to FlowFuse and give permission for the plugin to connect to your account. 4. Once connected, close the settings panel. ### Working with Snapshots The FlowFuse Tools sidebar allows you to browse the teams you are a member of and their instances. When you select an instance, the sidebar lists its snapshots. You can then create a new snapshot using the flows you have running locally. ### Create a Snapshot 1. Click the `+ snapshot` button to open the Create Snapshot dialog. 2. In the dialog, enter a name for the snapshot and an optional description. 3. The dialog lists the modules your flows are using, along with the version number. This information is included in the snapshot when sent back to the FlowFuse instance. Check the notes below on how this is handled within FlowFuse. 4. Click 'Create Snapshot' At this point, a new snapshot will be created in FlowFuse. You can then switch to the FlowFuse platform and from the Snapshots menu either select the 'Restore Snapshot' option to deploy that snapshot, or set it as the Device Target to deploy it to your devices. We'll be working on improving this workflow in future releases of the plugin - to allow you to manage more from within the Node-RED plugin. #### Adding modules to a snapshot The snapshot created by the sidebar includes a list of the modules used by the flows. If there are any modules included that have not already been added to your instance you will need to manually add them via the `Instance Settings -> Palette -> Installed Modules` view in the platform. #### Defining Environment Variables We do not currently support defining environment variables for the flows from within the Node-RED Plugin. This means that when you create a snapshot from the plugin, the platform will automatically merge in the currently defined environment variables for that instance. To manage your instance's environment variables, use the `Instance Settings -> Environment` view in the platform. # Customer Support Premium customers can get support by [filing a ticket](https://flowfuse.com/support). We offer support for the FlowFuse application and your account, any issues relating to Node-RED such as your flows or a 3rd party node should be raised in the [community forum](https://community.FlowFuse.com){rel=""nofollow""}. # Quick Start Guide This guide provides a streamlined process for setting up and running the FlowFuse platform using [Docker](https://docs.docker.com/get-started/){rel=""nofollow""} and [Docker Compose](https://docs.docker.com/compose/){rel=""nofollow""}. The Docker Compose file deploys the following services: - **FlowFuse Platform**: Includes the core application, MQTT broker, and file server for storage - **Database:** A pre-configured database for storing platform data - **Proxy Server:** A pre-configured proxy server for managing HTTP traffic For a full installation guide, including how to setup FlowFuse in a production environment, please refer to the dedicated page for [running FlowFuse on Docker](https://flowfuse.com/docs/install/docker). ## Prerequisites Before you begin, ensure you have [Docker](https://docs.docker.com/engine/install/){rel=""nofollow""} and [Docker Compose](https://docs.docker.com/compose/install/){rel=""nofollow""} (in `2.23.1` version or higher) installed on your system (either as a standalone binary or as a Docker plugin). ## Step 1: Prepare your domain FlowFuse requires a domain name to work properly — it uses subdomains to run each Node-RED instance separately, so `localhost` won't work here. If you own a domain (e.g., `example.com` or `flowfuse.example.com`), create an **A record** pointing to your server's IP address, and another **A record** for the wildcard subdomain (e.g., `*.example.com` or `*.flowfuse.example.com`) pointing to the same address. That's all the setup needed. If you don't have a domain yet and just want to try FlowFuse locally, see [setting up an alternative to DNS](https://flowfuse.com/docs/install/dns-setup#no-local-dns-server). ## Step 2: Download files ```bash curl -L -o docker-compose.yml https://github.com/FlowFuse/docker-compose/releases/latest/download/docker-compose.yml curl -L -o .env https://raw.githubusercontent.com/FlowFuse/docker-compose/refs/heads/main/.env.example ``` ## Step 3: Provide domain name Edit the downloaded `.env` file with the editor of your choice and update the `DOMAIN` variable with your domain. You can use `sed` to update the `DOMAIN` variable in the `.env` file: ```bash sed -i.bak 's/^DOMAIN=.*/DOMAIN=example.com/' .env ``` ## Step 4: Start the Application Run the following command to deploy FlowFuse: ```bash docker compose up -d ``` This downloads the necessary Docker images, runs initial setup, and starts all services in detached mode. ## Step 5: Complete the application Setup Open your web browser and navigate to `http://forge./setup` (e.g., `http://forge.example.com/setup`). You will be redirected to the setup page where you can create your admin account and set up your instance. For detailed information about first setup and configuration, follow [this guide](https://flowfuse.com/docs/install/first-run). ## Cleanup To stop and remove the FlowFuse application, run the following command: ```bash docker compose down -v ``` ## Troubleshooting If you encounter any issues, please check the following: 1. Ensure all prerequisites are correctly installed 2. Verify your DNS settings are correct and have propagated 3. Check the Docker logs for any error messages: ```bash docker compose logs ``` For more detailed information or advanced configuration options for running FlowFuse on Docker, please refer to our [full documentation](https://flowfuse.com/docs/install/docker). # Upgrading FlowFuse If you are upgrading an existing FlowFuse installation, this page will list any particular requirements needed to upgrade to a given level. If you are upgrading across multiple versions, make sure you check the requirements for each version you are upgrading across. Note that we do not support downgrading FlowFuse to previous levels once an upgrade has been performed. ## General guideline Details of how to upgrade can be found for each deployment model: - [LocalFS](https://flowfuse.com/docs/contribute/local#upgrade) - [Docker](https://flowfuse.com/docs/install/docker#upgrade) - [Kubernetes](https://flowfuse.com/docs/install/kubernetes#upgrade) To upgrade the version of Node-RED your Instances run (for example, moving to Node-RED 5.0), see [Upgrading the Node-RED version](https://flowfuse.com/docs/upgrade/nodered-version). ### Upgrading to 2.31.2 #### Kubernetes: MQTT broker is now EMQX From Helm chart v2.78.0 (FlowFuse 2.31.2), the chart deploys [EMQX](https://www.emqx.io/){rel=""nofollow""} as the platform MQTT broker, replacing Mosquitto. The migration happens automatically as part of upgrading the chart; it is not possible to stay on Mosquitto when using the Helm chart. If the broker is enabled, the [EMQX Operator](https://docs.emqx.com/en/emqx-operator/latest/getting-started/getting-started.html){rel=""nofollow""} must be installed on the cluster before upgrading, otherwise the upgrade will fail. See [MQTT Broker configuration](https://flowfuse.com/docs/install/configuration#mqtt-broker-configuration) for which platform features depend on the broker. ### Upgrading to 2.6.0 #### Required AWS EKS configuration change This release introduces the new Embedded Editor which integrates the Node-RED editor with the FlowFuse dashboard when using Node-RED 4.0. This has required some changes to be made on how certain HTTP headers are passed between the NGINX Ingress controller and AWS NLB. The following configuration change must be applied otherwise users will not be able to login to Node-RED 4.0 instances. The following configuration needs to be added in the values passed to the ingress-nginx helm chart. See [full configuration](https://flowfuse.com/docs/install/kubernetes/aws/#nginx-ingress){rel=""nofollow""} for the reference. ```text controller: config: use-proxy-protocol: true service: annotations: service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: proxy_protocol_v2.enabled=true externalTrafficPolicy: Cluster ``` The Proxy Protocol feature will be enabled only on newly created Target Groups. To enable the Proxy Protocol on an existing Target Group, manual intervention is required. For detailed instructions, please refer to the [official AWS documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#enable-proxy-protocol){rel=""nofollow""}. #### Persistent Storage As part of this release there is a new option for Persistent File Storage for Kubernetes based deployments. This change removes the need to use the customised File Nodes and the FlowFuse File Server by mounting a Persistent Volume into the Pods running the instances. To enable this feature the following needs to be created - A Kubernetes StorageClass that points to storage provider that can dynamically provision new Persistent Volumes. e.g. the [AWS EFS CSI driver](https://github.com/kubernetes-sigs/aws-efs-csi-driver){rel=""nofollow""} - Pass the following values to the FlowFuse Helm Chart ```text forge: persistentStorage: enabled: true storageClass: '' size: '5Gi' ``` Where size is the default size for the volume. Details for how to setup a AWS EFS backed StorageClass can be found on the aws-efs-csi-driver [site](https://github.com/kubernetes-sigs/aws-efs-csi-driver/blob/master/docs/efs-create-filesystem.md){rel=""nofollow""}. ### Upgrading to 2.0.0 > **⚠️** Breaking changes introduced! Together with new application features, this **release 2.0.0 introduces breaking changes** in Flowfuse Helm chart. If you are managing your local Flowfuse instance using our [Helm Chart](https://github.com/FlowFuse/helm/tree/main/helm/flowfuse){rel=""nofollow""}, please refer to the [upgrade](https://flowfuse.com/docs/install/kubernetes#upgrade) section of the Kubernetes installation guide or the Helm Chart [README.md](https://github.com/FlowFuse/helm/blob/main/helm/flowfuse/README.md#upgrading-chart){rel=""nofollow""} for more details. ### Upgrading to 1.10 Endpoint Rate Limiting is now available to FlowFuse. This is disabled by default, but can be enabled by setting the `rate_limits.enabled` config setting to `true`. The documentation for this is available [here](https://flowfuse.com/docs/install/configuration#rate-limiting-configuration). The [TeamType concept](https://flowfuse.com/docs/user/concepts#team-type) was expanded in this release. It is used to control what Instance Types are available for different teams, as well as any additional limits that should be applied. When creating new Instance Types, they must now be [manually enabled](https://flowfuse.com/docs/admin/introduction#managing-instance-types) for the Team Types on the platform. ### Upgrading to 1.5 The main change in this release was a change in our terminology around the individual Node-RED instances. We have introduced the [Application concept](https://flowfuse.com/docs/user/concepts#application) as a way to group individual [Node-RED instances](https://flowfuse.com/docs/user/concepts#instance) (what we previously called Projects). The term 'Project' is being phased out. You may still see it crop up, such as in some of the external APIs, but we're working our way through removing it. ### Upgrading to 1.3 To enable the Team Library and FlowFuse-based Authentication of HTTP routes each Node-RED instance will need to be updated to the [latest Stack](https://flowfuse.com/docs/user/changestack). #### Persistent Context added The new Persistent Context feature is available to projects when running with a [premium license](https://flowfuse.com/docs/upgrade/open-source-to-premium). This feature requires additional configuration to be added to the File Server component that was introduced in FlowFuse 1.1. Details of how to configure this can be found at the following links: - [LocalFS](https://flowfuse.com/docs/install/file-storage#localfs) - [Docker and Kubernetes](https://flowfuse.com/docs/install/file-storage#configuring) ### Upgrading to 1.1 #### File Server added This release introduces a system for supporting persistent file storage when running on Docker or Kubernetes (it will also work with LocalFS, but is not required as projects have access to the hosts filesystem). Details of how to configure this can be found at the following links: - [LocalFS](https://flowfuse.com/docs/install/file-storage#localfs) - [Docker and Kubernetes](https://flowfuse.com/docs/install/file-storage#configuring) ### Upgrading to 0.8 #### MQTT Broker added This release introduces an MQTT Broker into the FlowFuse platform used to communicate between devices and the core platform. For LocalFS users, they will need to manually setup the broker and ensure it is properly configured. The documentation for this is available [here](https://flowfuse.com/docs/contribute/local#setting-up-mosquitto-optional) #### LocalFS Users With the 0.8 release we have updated the version of the SQLite3 module used by the localfs container driver. We are moving from v5.0.2 to v5.0.8. There appears to be a clash with the bcrypt module when doing an in place upgrade of the SQLite3 module that gives an error similar to the following: ```bash npm ERR! path /opt/share/projects/flowforge/sqlite-test/node_modules/sqlite3 npm ERR! command failed npm ERR! command sh -c node-pre-gyp install --fallback-to-build npm ERR! sh: line 1: node-pre-gyp: command not found ``` If you see this then the simplest fix is to remove the `node_modules` directory and reinstall the modules. #### Project Nodes This release adds support for the new Project Link nodes that can be used to send messages between projects seamlessly. These nodes require the MQTT Broker to be properly configured. To deploy flows using these nodes to a Device will require the Device to be running the latest 0.2.0 release. They will also need to have their credentials regenerated once the MQTT Broker has been added. ### Upgrading to 0.7 The 0.7 release introduces the [ProjectType concept](https://flowfuse.com/docs/user/concepts#instance-type). After upgrading to 0.7, an administrator must perform the following tasks before users will be able to create new projects: 1. Create a Project Type. 1. On the Administrator Settings -> Project Types page, click 'Create project type'. 2. Provide a name and description. If you have billing enabled, copy in the default Stripe Product/Price IDs from your runtime settings file. 3. Click 'create' 2. Assign your existing stacks to that type 1. On the Administrator Settings -> Stacks page, edit each existing stack via the drop-down menu in the table. 2. As a one-time action, set its Project Type to the one just created. 3. Click 'save'. This will update the stack *and* all existing projects to be associated with the new Project Type # Upgrading the Node-RED version A new major version of Node-RED - such as Node-RED 5.0 - does **not** arrive through an in-editor or in-platform "update" button. The version of Node-RED an Instance runs is defined by its [Stack](https://flowfuse.com/docs/user/concepts#stack). To move to a new major version you add it as a new Stack, then point your Instances at it. This page uses the move to **Node-RED 5.0** as an example, but the same steps apply to any major Node-RED version upgrade. ## Why there's no update button The notification and one-click upgrade you may have seen applies to new *versions of an existing Stack* (see [Managing Stacks](https://flowfuse.com/docs/admin/introduction#managing-stacks)). Crossing a major Node-RED version is different: you create a new Stack that pins the new container, rather than upgrading the existing Stack in place. This keeps the two versions side by side so you can move Instances over at your own pace. It also ensures major version upgrades are not automatically applied by the Scheduled Maintenance feature. ## Before you start - You will need access to **Admin Settings**. - Add the new Node-RED version as a **distinct new Stack** rather than editing your existing Stack. Keeping them separate means your current Instances stay on their existing version until you choose to move them, and you can switch back from an Instance's settings if needed. ## Create the new Stack 1. Go to **Admin Settings → Stacks**. 2. Click **Create Stack**. 3. Set the **Container Location** to the image for the new Node-RED version. The exact value depends on your deployment model - see the deployment-specific guides below. For Node-RED 5.0 on Docker or Kubernetes this is `flowfuse/node-red:2.31.2-5.0.x`; the full list of pre-built tags is on [Docker Hub](https://hub.docker.com/r/flowfuse/node-red/tags){rel=""nofollow""}. (tags are of the form [FlowFuse Version] - [Node-RED Version] ) 4. Save the Stack. It is now available when you create or migrate Instances. For the details of how the Container Location is specified for each deployment model, see: - [Local Stacks](https://flowfuse.com/docs/contribute/local/stacks) - [Docker Stacks](https://flowfuse.com/docs/install/docker/stacks) - [Kubernetes Stacks](https://flowfuse.com/docs/install/kubernetes/stacks) ## Use the new Stack ### For a new Instance Select the new Stack when you create the Instance. It starts on the new Node-RED version right away. ### For an existing Instance Move the Instance onto the new Stack by following [Changing the Stack](https://flowfuse.com/docs/user/changestack): open the Instance's **Settings** tab and use **Change Instance Stack** to select the new Stack. The Instance restarts on the new Stack. # Upgrading to FlowFuse Enterprise For self-managed FlowFuse installations without a license you can unlock more features with a enterprise license. As an admin a license can be uploaded to FlowFuse in the admin panel, under the settings tab. When a license is uploaded a restart of the `forge` app is required. After the forge application has restarted, the Node-RED runtimes need to be updated to leverage these features. As restarting Node-RED might need to be coordinated, FlowFuse will not automatically restart all instances. ## Reusing FlowFuse licenses A single license may only be applied to one FlowFuse platform at any time. Running multiple FlowFuse platforms with the same license key is against the terms of the subscription. A license may be reused on another FlowFuse platform if the original platform is no longer running. For example, if the FlowFuse platform is reinstalled onto new hardware, the license from the original install can be reused. # Bill of Materials The Application Bill of Materials (BoM) provides a comprehensive overview of all instance dependencies for each application. This feature gives you visibility and control to monitor, manage, and analyze the components your applications rely on. ## Accessing the Bill of Materials The BoM is located in the **Dependencies** tab of each application. ![bom.png](https://flowfuse.com/docs/user/images/bom.png){dataZoomable=""} ## Search Functionality Use the integrated search to efficiently find devices and instances assigned to your application. Search by: - Instance or device name - Package name - Specific dependency - Dependency version ## Prerequisites ### FlowFuse Cloud - Instance Stack with launcher version **2.9.0 or higher** - **Enterprise Team Type** required ### Self-Hosted - Available only with **Enterprise license** # Changing the Stack [Stacks](https://flowfuse.com/docs/user/concepts#stack) define various aspects of how Node-RED instances run - including the version of Node-RED being used. FlowFuse allows you to change the stack an instance is using - providing a way to upgrade Node-RED. **Note:** Stacks are created by Administrators and made available to the teams and users of the platform. When an Administrator creates a new version of a Stack your instance is using, the platform will notify you that there is a new version available. To change an instance's stack: 1. Go to the instance's page and select the **Settings** tab. 2. Click the **Change Instance Stack** button. 3. You will be prompted to select the new stack. 4. Click **Change Stack** Your instance will now be restarted on the new stack. **Note:** Changing the stack causes Node-RED to be stopped and restarted. This will require a short downtime of the flows. # FlowFuse Concepts FlowFuse makes it easy to create, manage, and scale Node-RED instances. The platform introduces a few core concepts to help you organize and work with it effectively. Throughout the platform, you'll also see ⓘ icons that you can click on to get pop-up explanations for different features and terms. ## Table of Contents - [FlowFuse Concepts](https://flowfuse.com/#flowfuse-concepts) - [Table of Contents](https://flowfuse.com/#table-of-contents) - [Team](https://flowfuse.com/#team) - [Team Type](https://flowfuse.com/#team-type) - [Application](https://flowfuse.com/#application) - [DevOps Pipeline](https://flowfuse.com/#devops-pipeline) - [Instance](https://flowfuse.com/#instance) - [Hosted Instance](https://flowfuse.com/#hosted-instance) - [Remote Instance](https://flowfuse.com/#remote-instance) - [Instance Configuration](https://flowfuse.com/#instance-configuration) - [Instance Type](https://flowfuse.com/#instance-type) - [Stack](https://flowfuse.com/#stack) - [Blueprint](https://flowfuse.com/#blueprint) - [Template](https://flowfuse.com/#template) - [Snapshot](https://flowfuse.com/#snapshot) - [Hosted Instance Snapshot](https://flowfuse.com/#hosted-instance-snapshot) - [Remote Instance Snapshot](https://flowfuse.com/#remote-instance-snapshot) - [Assigning Remote Instances to Hosted Instances](https://flowfuse.com/#assigning-remote-instances-to-hosted-instances) - [How It Works](https://flowfuse.com/#how-it-works) - [Assignment Rules](https://flowfuse.com/#assignment-rules) - [When to Use Instance Assignment vs DevOps Pipelines](https://flowfuse.com/#when-to-use-instance-assignment-vs-devops-pipelines) - [Device](https://flowfuse.com/#device) - [Device Agent](https://flowfuse.com/#device-agent) - [Fleet Mode vs Developer Mode](https://flowfuse.com/#fleet-mode-vs-developer-mode) - [Provisioning Tokens](https://flowfuse.com/#provisioning-tokens) - [Device Groups](https://flowfuse.com/#device-groups) ## Team Teams are how FlowFuse organizes users on the platform. Each team can have multiple members and each user can be a member of multiple teams. The users in a team can have different roles that determine what they are [able to do](https://flowfuse.com/docs/user/team#role-based-access-control). In FlowFuse Cloud, each team has its own billing plan, managed via Stripe. ### Team Type The platform can be configured to provide different types of teams. These can be used to apply limits on what teams of a given type can do. For example, a particular team type may be restricted to certain types of [Node-RED instances](https://flowfuse.com/#instance), or how many members the team can have. ## Application **Introduced in FlowFuse 1.5** To organize your Node-RED instances, they are grouped within Applications. With the 1.5 release, each Application has a single Node-RED instance. With the 1.6 release, an application can have multiple Node-RED instances. Applications provide logical organization of related instances, support for DevOps pipeline workflows, simplified device group management, application-level audit logging, and clear organizational boundaries for managing multiple instances and devices. With FlowFuse's Granular RBAC, Applications can now also act as an authorization boundary. This means user roles and permissions can be managed at the application level, providing finer control over access to instances, snapshots, and devices within a given application. ### DevOps Pipeline **Introduced in FlowFuse 1.8** DevOps Pipelines allow you to manage staged development environments, pushing from your Development instances to Production once you have stable and well-tested flows. You can find out how to implement DevOps Pipelines [here](https://flowfuse.com/docs/user/devops-pipelines). ## Instance **This was called a Project before FlowFuse 1.5** Within your Application, you can have one or more instances of Node-RED. FlowFuse supports two types of instances based on where they run. | Feature | Hosted Instance | Remote Instance (Device) | | ------------- | --------------------------------------------------------- | ------------------------------------------------ | | Where it runs | On FlowFuse-managed infrastructure (cloud or self-hosted) | On user-provided hardware (Edge, VM, Pi, PLC) | | Provisioning | Automatic via the FlowFuse UI | Manual installation of the FlowFuse Device Agent | | Lifecycle | Fully managed by FlowFuse | Managed by FlowFuse via the Device Agent | | Common Use | Core logic, APIs, dashboards, development, testing | Local data processing, IO, edge control | ### Hosted Instance A **Hosted Instance** is a Node-RED environment that runs within the FlowFuse infrastructure, whether in the cloud or on a self-hosted FlowFuse platform. These instances run on FlowFuse-managed infrastructure and benefit from automatic scaling and high availability. They are accessed via HTTPS with TLS/SSL encryption, making them ideal for centralized data processing and transformation, dashboard hosting, cloud service integration, and development or testing environments. When running in Docker or Kubernetes (such as FlowFuse Cloud), the instance names are used as the hostname to access the instance. This means that the names must be DNS safe (made up of a-z, 0-9 and -) and not start with a number. Currently, it is not possible to change an instance name after it has been created. ### Remote Instance A **Remote Instance** refers to a Node-RED environment that is managed by FlowFuse but runs on external infrastructure—be it a PLC, Gateway, local PC, or even a server in a different network. Remote instances used to be referred to as Devices within the FlowFuse platform, and in some parts of the platform this terminology is still used. See the [Device](https://flowfuse.com/#device) section for more details. These instances run on user-provided infrastructure such as edge devices, on-premises servers, or industrial equipment while being managed centrally through the FlowFuse platform. They are ideal for edge computing, local data processing, and environments with limited connectivity. Remote instances require FlowFuse Device Agent installation to connect to the platform. ### Instance Configuration Both hosted and remote instances are customized versions of Node-RED that include various FlowFuse plugins to integrate with the platform. A number of the standard Node-RED settings are exposed for customization, and they can be preset by applying a Template when creating the instance. When an instance is being created, the user can select a blueprint to use. ### Instance Type When you create a Node-RED instance, you can pick its type from the list the platform Administrator has made available. For example, each type could provide a different amount of memory or CPU allocation. If the platform has billing enabled, each type may have a different monthly price associated with it. ### Stack A Stack describes the properties of the Node-RED runtime. This can include the version of Node-RED, memory, and CPU allocation. The specific details will depend on how and where FlowFuse is running. Stacks are created and owned by the platform Administrator. When a User comes to create a new Node-RED instance, they choose from the available stacks associated with the chosen Instance Type. The stack determines the Node-RED version, memory, and CPU usage for your instance, ensuring compatibility and optimal performance for your use case. For details on how to administer and manage Stacks, please see the [Administering FlowFuse](https://flowfuse.com/docs/admin/introduction#managing-stacks) docs. ### Blueprint Blueprints are pre-built Node-RED instances designed for industrial applications. They include ready-to-use flows along with all node configurations and the required nodes, as well as environment settings, allowing the instance to be deployed with minimal modification. ### Template A Template describes the properties of Node-RED itself. It is how many of the settings a user familiar with Node-RED would be used to modifying in their settings file. But it can also be used to customize the palette of nodes that are pre-installed, provide a set of default flows and change the look and feel of the editor. A template can also specify [Environment Variables](https://flowfuse.com/docs/user/envvar) which can then have their values customized for each Node-RED instance, or have their values locked to prevent any changes. In the current release of FlowFuse, Templates are created by the Administrator. As well as defining the values, they also get to choose whether instances can override any of the settings for themselves. ### Snapshot Snapshots are point-in-time backups that work differently depending on the instance type. #### Hosted Instance Snapshot A snapshot is a point-in-time backup of a hosted Node-RED instance. It captures the flows, credentials, and runtime settings. Snapshots can be created and deleted on the FlowFuse Platform, or using the [FlowFuse Node-RED Tools plugin](https://flowfuse.com/docs/migration/node-red-tools). The platform also allows you to roll an instance back to a previous snapshot. A user can create an hosted instance snapshot and then mark it as the *target* snapshot for remote instance. The platform will then deploy that snapshot to all of the devices assigned to the instance. Snapshots can be set as targets for DevOps pipeline stages. Auto snapshots are available for automatic backup scheduling. If an auto snapshot is set as a target or assigned to a pipeline stage, it will not be automatically cleaned up, so you may have more than 10 auto snapshots in that case. #### Remote Instance Snapshot Similar to instance snapshots, a device snapshot is a point-in-time backup of a Node-RED instance running on a remote device. It captures the flows, credentials, and runtime settings. The difference is that local changes made on a device during developer mode are pulled into the FlowFuse platform and stored as a snapshot. The dashboard also allows you to see and manage these snapshots. With devices assigned to an application, a user can create a device snapshot from the remote device. ### Assigning Remote Instances to Hosted Instances **Note:** This is a legacy feature that predates DevOps Pipelines. For new deployments, consider using [DevOps Pipelines](https://flowfuse.com/#devops-pipeline) instead, which provide more flexible and powerful deployment workflows. Instance assignment establishes a parent-child deployment relationship between a hosted instance and remote instances. When assigned, the hosted instance becomes the source of truth and automatically pushes snapshots to the remote instance. You can only push nodes and flows that are supported by both Hosted and Remote Instances. For more information on how this works, see [How Instance Assignment Works](https://flowfuse.com/docs/user/concepts#how-it-works). #### How It Works Instance assignment creates a parent-child deployment relationship with the following behavior: - **Hosted Instance (Parent)**: Acts as the deployment controller and source of truth for snapshots - **Remote Instance (Child)**: Receives and applies snapshot updates from the parent - **Deployment Flow**: When you set a [Target Snapshot](https://flowfuse.com/docs/user/snapshots/#instance-owned-devices) on the hosted instance, all assigned remote instances automatically restart on that snapshot - **One-Way Relationship**: Changes flow exclusively from the hosted instance to remote instances, ensuring consistent deployments across your fleet #### Assignment Rules FlowFuse enforces specific constraints on instance assignment: - Remote instances can be assigned to hosted instances - Remote instances cannot be assigned to other remote instances - Hosted instances cannot be assigned to any other instances #### When to Use Instance Assignment vs DevOps Pipelines Instance assignment suits straightforward scenarios where you need to push snapshots from a single hosted instance directly to one or more remote instances. It works well for maintaining existing deployments or when you simply need one hosted instance deploying to multiple remote instances without staging environments. DevOps Pipelines are recommended for most deployment workflows. They support staged environments for testing changes in development before promoting to production, enable deployment to device groups for easier fleet management, and offer more flexibility aligned with modern development practices. If you're setting up a new deployment process, use DevOps Pipelines. ## Device The FlowFuse platform can be used to manage Node-RED applications running on remote devices. A Device is essentially a **Remote Instance** that runs a software agent to connect back to the platform and receive updates. Users must [install the agent](https://flowfuse.com/docs/device-agent/install/overview) on the devices. Devices are registered to a Team and then assigned to an individual Node-RED instance or a FlowFuse application within that team. A user can create an [instance snapshot](https://flowfuse.com/#hosted-instance-snapshot) and then mark it as the *target* snapshot for devices. The platform will then deploy that snapshot to all of the devices assigned to the instance. With devices assigned to an application, a user can create a [device snapshot](https://flowfuse.com/#remote-instance-snapshot) from the remote device. ### Device Agent The FlowFuse Device Agent is the software that connects your hardware to FlowFuse and manages how Node-RED runs on it. It receives snapshots from FlowFuse, installs the required Node-RED version and nodes, and ensures the device always runs the assigned snapshot. It maintains a secure connection so FlowFuse can monitor the device and send updates remotely. Read more about the [Device Agent](https://flowfuse.com/docs/device-agent/introduction). ### Fleet Mode vs Developer Mode **Fleet Mode** When in this mode, the device runs only the assigned target snapshot. It automatically updates whenever the target snapshot changes, and the Node-RED editor is disabled to prevent any local modifications. This keeps all devices consistent across the fleet. **Developer Mode** When in this mode, the device ignores the target snapshot and stops receiving updates. A secure tunnel opens so you can access the Node-RED editor and make changes directly on the hardware. When you exit Developer Mode, all local edits are discarded and the device reloads the current target snapshot. ### Provisioning Tokens Provisioning tokens can be created to allow Remote Instances to automatically join a team and to be auto assigned to an application or an instance if required. ## Device Groups **Introduced in FlowFuse 1.15** Device groups allow you to organize your devices into logical groups. These groups can be the target of [DevOps Pipelines](https://flowfuse.com/#devops-pipeline), greatly simplifying the deployments to one or hundreds of devices. Device groups provide logical organization of devices by location, function, environment, or other criteria. They enable simplified mass deployments through DevOps pipelines, allow you to target specific device groups for staged rollouts, and let you manage subsets of devices independently. Read more [about Device Groups](https://flowfuse.com/docs/user/device-groups). # Custom Hostnames FlowFuse allows you to point a custom subdomain at your instances, such as `dashboard.example.com`. This feature is available to: - FlowFuse Cloud Enterprise teams - Self-hosted FlowFuse Enterprise, running version 2.5 or later with the kubernetes driver ## Configuring a custom hostname The Custom Hostname option is available under the General Settings tab of your Instance. Currently, FlowFuse only supports configuring a subdomain such as `dashboard.example.com`. Top level domains cannot be used. 1. Enter your custom subdomain and click save. 2. You will be shown a dialog to confirm the change, as this will require restarting your Instance to apply. It also provides information on how to configure your DNS provider. 3. Using your DNS provider, create a `CNAME` record for your chosen subdomain that points at the endpoint provided in the dialog. **Note**: it is important to configure the Instance's Custom Hostname *before* you make the DNS changes. Making the DNS changes without configuring your Instance may allow someone else to configure their instance to use your subdomain. For FlowFuse Cloud, the `CNAME` should point to `custom-loadbalancer.flowfuse.com`. For self-hosted users, use the information provided in the dialog. If you also use CAA DNS entries to control which Certificate Authorities can issue certificates for your domains, you will need to add a record allowing LetsEncrypt to issue certificate. Please see details [here](https://letsencrypt.org/docs/caa/){rel=""nofollow""}. The platform will issue certificates using the `http-01` validation method. # Custom Node Packages FlowFuse has access to the wide range of Node-RED nodes listed in the [public catalogue](https://flows.nodered.org){rel=""nofollow""}. But occasionally there will be the need for a custom node for a situation that is specific to your Team. If you decide to [develop](https://nodered.org/docs/creating-nodes/){rel=""nofollow""} your own nodes, you will need somewhere to host both the node and a Node-RED catalogue file. FlowFuse has two solutions for this: 1. FlowFuse Hosted Nodes - Use the private NPM registry hosted by FlowFuse to store and manage your custom npm packages. 2. Third-Party NPM Registries - If you already have a private npm registry, you can enable access to these in your Instance's settings. ## FlowFuse Hosted Nodes If you want to create a Node-RED node for private use by Instances in your FlowFuse Team then you can publish them to the FlowFuse Custom Node Registry (available to Enterprise level teams on FlowFuse Cloud). ### Publishing Nodes After developing your node you can publish it to your Teams Custom Nodes registry with the following steps #### Authenticating Before publishing to the registry you need to authenticate. This step should only need to be done once. The credentials can be found by navigating to the "Custom Nodes" tab under the Team Library and clicking on the "Publish" button. ![Publish Custom Package](https://flowfuse.com/docs/user/images/publish-custom-package.png){dataZoomable=""}{style="max-width: 600px;"} *Screenshot fo the "Publish Custom Package" dialog shown in the FlowFuse UI* ```text npm login --registry=https://registry.flowfuse.cloud ``` #### Packaging There are steps required to ensure your node is correctly packaged for the FlowFuse Custom Nodes registry 1. Make sure the package name contains the correct scope prefix e.g. `@flowfuse-[team id]/node-name`. The correct prefix will be shown on the in the FlowFuse application 2. Add a `publishConfig` section with a `registry` entry e.g. for a Team with ID `6Rag1kQj4k` ```json { "name": "@flowfuse-6Rag1kQj4k/bar", "version": "0.0.1", "description": "...", "publishConfig": { "registry": "https://registry.flowfuse.cloud" }, ... } ``` #### Publishing In the same directory as the `package.json` file run the following command ```text npm publish ``` Once published you should see the Node listed in the "Custom Nodes" section of the Team Library. ![Screenshot of the "Custom Nodes" view in the Team Library](https://flowfuse.com/docs/user/images/custom-node-library.png){dataZoomable=""}{style="max-width: 850px;"} *Screenshot of the "Custom Nodes" view in the Team Library* ### Installing Nodes Any packages uploaded to the Team Library will be published to your Instances under a custom catalogue with the name "FlowFuse Team [team name] Catalogue" ![Node-RED Custom Catalogue](https://flowfuse.com/docs/user/images/custom-catalogue.png){dataZoomable=""}{style="max-width: 600px;"} *Screenshot of the contents of a FlowFUse catalogue appearing in the "install" tab of the Node-RED Palette Manager* ## 3rd Party NPM Registries or Private npmjs.org packages The following features are available to Enterprise users of FlowFuse Cloud. ### NPM Registries If you have already published packages to an existing NPM Registry then you can enable access to this by adding the required values to a \`.npmrc" file in the Instance Settings. This can include authentication tokens to access private packages. ![.npmrc file](https://flowfuse.com/docs/user/images/instance-settings-npmrc.png){dataZoomable=""}{style="max-width: 600px;"} *Screenshot from the FlowFuse platform, showing the input for defining an .npmrc file* ### Node-RED Catalogues In order to be able to install packages in the Node-RED editor they need to in a Node-RED Catalogue file that is loaded from a HTTPS URL. You can supply a list of Catalogue URLs in the Instance Settings. ![Node Catalogues](https://flowfuse.com/docs/user/images/instance-settings-catalogues.png){dataZoomable=""}{style="max-width: 600px;"} *Screenshot of the listed Node Catalogues configured on an Instance* # Dashboards Any hosted instance running the [FlowFuse Dashboard](https://dashboard.flowfuse.com){rel=""nofollow""} can be viewed directly inside FlowFuse, without opening a separate browser tab. *This does not apply to Remote Instances (devices) - Dashboards are only listed and viewable here for hosted instances.* ## Team Dashboards Click **Dashboards** in the Team navigation to see every hosted instance in the Team that has a Dashboard installed, regardless of which Application it belongs to. ![The Team Dashboards page listing every instance with a Dashboard installed, across all Applications](https://flowfuse.com/docs/user/images/dashboard-team.png){dataZoomable=""} The list shows each instance's name, status, the Application it belongs to, and when its flows were last updated. Click a row to open that instance's Dashboard. Members with the **Dashboard Only** role land on this page, scoped to only the instances they have access to. ## Application Dashboards Each Application has its own **Dashboards** tab, showing only the Dashboards belonging to instances within that Application. ![The Dashboards tab on an Application page, listing Dashboards for instances in that Application](https://flowfuse.com/docs/user/images/application-dashboard.png){dataZoomable=""} ## Instance Dashboard Tab An individual hosted instance's page has a **Dashboard** tab, for viewing its Dashboard without leaving the instance's context. ![The Dashboard tab on an individual instance's page](https://flowfuse.com/docs/user/images/dashboard-instance-tab.png){dataZoomable=""} ## Viewing a Dashboard Opening a Dashboard displays it embedded inside FlowFuse. A drawer alongside it lists every other Dashboard available in the current scope (Team or Application), with a search box to find one by name. Click any entry in the drawer to switch to that Dashboard directly. ![The embedded Dashboard view with the switcher drawer open, listing other available Dashboards](https://flowfuse.com/docs/user/images/dashboard-drawer.png){dataZoomable=""} Use the icon in the top-right of the drawer to collapse or reopen it. To open a Dashboard in its own browser tab instead, use the **Dashboard** button's dropdown arrow on an instance's page and select **Open Direct URL**. ## Requirements - The instance must be **running** for its Dashboard to be viewable. - The instance must have the [FlowFuse Dashboard](https://dashboard.flowfuse.com){rel=""nofollow""} node installed and configured in its flows. Instances without a Dashboard configured do not appear in these lists. # Groups **Navigation**: Team > Application > Groups ## Overview ![Groups UI](https://flowfuse.com/docs/user/images/groups.png){dataZoomable=""} Groups help you organize and manage multiple devices that run the same [snapshot](https://flowfuse.com/docs/user/snapshots) configuration. By grouping devices logically, you can deploy updates to dozens or even hundreds of devices simultaneously through [DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines). ### Key Features **Automatic Updates** - Devices added to an active group automatically receive the current pipeline snapshot - Devices removed from an active group have their snapshot cleared **Group-Level Environment Variables** - Set environment variables at the group level that apply to all member devices - Device-specific variables take precedence over group variables - Variables are merged at runtime without modifying device settings - Updates to group variables trigger automatic device restarts ### Requirements - FlowFuse 1.15+ (Enterprise Tier) - FlowFuse Cloud (Enterprise Tier) - FlowFuse 2.10+ for Group Environment Variables ## Creating a Group ![Create Group](https://flowfuse.com/docs/user/images/create-device-group.png){dataZoomable=""} 1. Navigate to your Application 2. Select the **Groups** tab 3. Click **Add Group** 4. Enter a descriptive name for your group 5. (Optional) Add a description to help distinguish between groups 6. Click **Create** ## Managing Group Membership ### Adding and Removing Devices ![Edit Group Members](https://flowfuse.com/docs/user/images/ui-device-group-member-edit.png){dataZoomable=""} 1. Click the group you want to modify from the table 2. Click **Edit** 3. Review the two lists: - **Available Devices** (left): Devices assigned to your application that can be added - **Group** (right): Devices currently in the group 4. To add devices: Check the boxes next to devices in the Available list, then click **Add Devices** 5. To remove devices: Check the boxes next to devices in the Group list, then click **Remove Devices** 6. Click **Save** 7. Review the confirmation prompt and click **Confirm** to apply changes **Note**: If a device doesn't appear in the Available list, it's likely already assigned to another group. ### How Snapshots Are Affected **When Adding a Device** If the group has an active pipeline snapshot, newly added devices will automatically be updated to that snapshot. **When Removing a Device** If the group has an active pipeline snapshot and the device is currently running it, removing the device will clear its snapshot, effectively resetting it to a blank state. **Clearing Group Snapshots** You can remove the target snapshot from a group in the group settings. This will also clear the snapshot from all devices in the group. ## Environment Variables Group-level environment variables follow these rules: - **Precedence**: Device variables override group variables - **Scope**: Variables are merged at runtime only; device settings remain unchanged - **Removal**: Removing a device from a group removes the group's variables from that device - **Updates**: Changing group variables triggers a restart of all devices in the group # DevOps Pipelines **Navigation**: `Team > Application > Pipelines` ![Overview of a DevOps Pipeline in FlowFuse](https://flowfuse.com/docs/user/images/ui-devops-pipelines.png){dataZoomable=""} In FlowFuse it is possible to configure a DevOps pipeline for your Node-RED instances. DevOps Pipelines allow you to easily deploy from one instance to another, most commonly used for having an unstable/experimental "Development" instance, and a more stable "Production" instance. The pipeline then allows you to move your full flow and configuration along from "Development" to "Production" once it's ready. You can configure this in FlowFuse from the Application screen. Note you will need to have created any Instances you wish to include in the Pipeline before being able to add them to a Pipeline. ## Creating a Pipeline 1. Select the Application you want to configure a Pipeline for. 2. Ensure an instance is created for each stage you plan to create, e.g. development, QA, and production. 1. For the instance you want to duplicate go to the **Settings** tab 2. Click **Duplicate Instance** and provide the necessary details 3. Select the "DevOps Pipelines" tab 4. Select "Add Pipeline" 5. Name your pipeline appropriately (this can be changed later) 6. Select "Add Stage" 7. Define your Stage's name, select the [Stage Type](https://flowfuse.com/#stage-types) and select an [Action](https://flowfuse.com/#actions). 8. Click "Add Stage" 9. Repeat 5. - 7. for as many stages as you need. See [Pipeline Stage details](https://flowfuse.com/#pipeline-stage-details) below for more info. ## Running a Pipeline Stage ![Running a pipeline stage by clicking the play icon on the source stage](https://flowfuse.com/docs/user/images/ui-devops-run.png){dataZoomable=""} Each stage currently is deployed manually. To do so, click the "play" icon on the source stage. In the example above, it will push from the "Development" stage to the "Production" stage. **Environment Variables** - When pushing to a next stage, ***only your environment variable keys will be copied over***. Values must be set on the next Stage's Instance explicitly. **Instance Settings** - None of your Instance Settings will be copied over (e.g. Editor, Palette or Security Settings). This ensures a split between your staging environments. ## Pipeline Stage details - Stages of a pipeline are executed from left to right. - Actionable stages have a play button that will push from that stage to the next stage. - Every stage, except the last one, is effectively a source stage that can be pulled *from*. - Every stage, except the first one, is a target stage that can be pushed *to*. - You cannot currently insert a Stage into the middle of a Pipeline, only at the end. ### Stage Types There are four types of stage to chose from: 1. **[Instance](https://flowfuse.com/docs/user/concepts#hosted-instance)** - a single Node-RED instance. 2. **[Device](https://flowfuse.com/docs/user/concepts#remote-instance)** - a single remote instance. 3. **[Device Group](https://flowfuse.com/docs/user/concepts#device-groups)** - a group of remote instances. 4. **Git Repository**- a remote Git repository. - This stage supports GitHub, Azure DevOps, and any other Git server accessible over HTTPS (for example GitLab, Bitbucket, Gitea, or a self-hosted instance). ### Actions ![Selecting an action for a pipeline stage](https://flowfuse.com/docs/user/images/ui-devops-select-action.png){dataZoomable=""} The action defines what happens when the stage is deployed. The available actions depend on the stage type selected for the stage. These are listed below. #### Instance stage actions - **Create new instance snapshot** - A new snapshot of the instance will be created and pushed to the next stage. - **Use latest instance snapshot** - The latest existing snapshot of the instance will be pushed to the next stage. - **Prompt to select instance snapshot** - You will be prompted to choose which snapshot to push to the next stage. #### Device stage action - **Use active snapshot** - The active snapshot of the device will be pushed to the next stage. - **Use latest device snapshot** - The latest snapshot of the device will be pushed to the next stage. - **Prompt to select device snapshot** - You will be prompted to choose which snapshot to push to the next stage. #### Device Group stage When a Device Group stage is triggered, it will push the current active snapshot of the group to the next stage. #### Git Repository stage Git Repository stages can be used to push and pull snapshots to and from a remote Git repository. FlowFuse supports GitHub, Azure DevOps, and any other Git server that is accessible over HTTPS, for example GitLab, Bitbucket, Gitea, or a self-hosted instance. > This feature is available to Enterprise tier teams on FlowFuse Cloud, and to Enterprise licensed self-hosted installations from version 2.32 onwards. Support for generic HTTPS Git servers (anything other than GitHub and Azure DevOps) requires version 2.32 or later. ##### Adding a Git token Before you can create a Git Repository stage, you need to add a token that grants FlowFuse access to your repository. Tokens are managed per team, under `Team > Team Settings > Integrations`. 1. Go to **Team Settings** and open the **Integrations** tab. 2. In the Git integration section, click **Add Token**. 3. In the **Add Git Personal Access Token** popup, choose the tab that matches your Git server (**GitHub**, **Azure DevOps**, or **Other**) and follow the instructions for that provider (below). 4. Give the token a **Name**, complete the required fields, and click **Add**. ![Add Git Personal Access Token popup with tabs for GitHub, Azure DevOps, and Other providers](https://flowfuse.com/docs/user/images/generic-git-provider.png){dataZoomable=""} ###### GitHub 1. Open GitHub Personal Access Tokens Settings. 2. Click on **Generate a new token**. 3. Select the **Only select repositories** option and pick which repositories to grant access to. 4. Expand the **Repository permissions** section and ensure the **Contents** option is set to **Read and write**. 5. Click on **Generate token**. 6. This will be the only time GitHub shows you the token value. Copy the token into the **Token** field. ###### Azure DevOps 1. Open `https://dev.azure.com/[org-name]/_usersSettings/tokens` to create a new Personal Access Token. 2. Click the **+ New Token** button. 3. Give the token a name and set the **Expiry** date. 4. Select **Custom defined** for the Scopes. 5. Check **Read & Write** in the **Code** section. 6. Hit **Save**. 7. This will be the only time Azure shows you the token value. Copy the token into the **Token** field. ###### Other Use this tab to connect to any Git server (GitLab, Bitbucket, Gitea, or a self-hosted instance) over HTTPS. 1. Create a Personal Access Token (or App Password) on your Git server with read & write access to the repository. 2. Enter the **Username** associated with that token. 3. Paste the token value into the **Token** field. 4. **CA Certificate (optional)**: only needed for self-hosted servers that use a private certificate authority. Paste the certificate (in PEM format, including the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines) into this field so that FlowFuse can trust the connection. No changes to your infrastructure are required. ##### Configuring the stage When adding a Git Repository stage, select the token you created and enter the repository URL (for example `https://github.com/your-org/nodered-flows.git`). The stage can be configured with the branch to push/pull from as well as the filename to use for the snapshot. If a filename is not configured, it will generate the filename when pushing to the repository based on the name of Instance, Device or Device Group that provided the snapshot. The provided filename can include directory structures, allowing the snapshot to be stored in a subdirectory of the repository. When pulling from a repository, if the stage has not previously been used to push to the repository, the filename is a required property. It is also possible to configure the stage with different branches for the push and pull actions. This enables a Git-based review process as part of the pipeline; using a Pull Request process to review and approve the changes before merging between the two branches. #### Deploy to Devices This option is only applicable when the Stage Type is an Instance. When a pipeline stage with this action is deployed to, the same snapshot will be deployed to all devices connected to the defined instance. ## Protected Instances It is now possible to mark an instance as Protected. This means that all team members (including Owners) only have Read Only access to the Node-RED Editor and updates to the flows can only be made by a Team Owner running a DevOps pipeline that targets the instance. Protected mode is activated under Instance > Settings > Protect Instance ![The Protect Instance setting under Instance Settings](https://flowfuse.com/docs/user/images/protected-instance.png){dataZoomable=""} A Protected Instance will be marked by a status badge next to it's running state. Click on this badge will take you to the Settings page. ![Protected status badge shown next to the instance running state](https://flowfuse.com/docs/user/images/protected-instance-pill.png){dataZoomable=""} # Environment Variables Environment Variables allow you to manage variables used in your Node-RED flows from the FlowFuse application, you can read more on how to access environment variables inside Node-RED [in the Node-RED Docs](https://nodered.org/docs/user-guide/environment-variables){rel=""nofollow""}. An Environment Variable consists of a name and a value. ## Editing You can edit the environment variables from the `Settings` tab of an instance, select the `Environment` option from the side menu. Changes will only take effect when the Node-RED instance is restarted. ## Template provided variables The [Template](https://flowfuse.com/docs/user/concepts#template) may include some predefined environment variables that are automatically applied. The template may lock some of those variables to prevent an individual instance from changing them. Variables provided by the template cannot be deleted, however if they are editable, their value can be set to blank. ## Node-RED instance variables You can create additional variables for an individual Node-RED instance clicking the `Add variable` button. You can import variables from a `.env` file using the `Import .env` button. You can delete a variable using the trash can icon. The image below shows an instance with the following environment variables: - `policy_item_locked` - added by the template, locked - `policy_item_editable` - added by the template, editable - `FF_INSTANCE_ID` - provided by the platform, locked - `FF_INSTANCE_NAME` - provided by the platform, locked - `FF_PROJECT_ID` - provided by the platform, locked, depreciated - `FF_PROJECT_NAME` - provided by the platform, locked, depreciated - `INSTANCE_VAR` - added to the instance, editable ![](https://flowfuse.com/docs/user/images/project-envvar.png){width="500"} ## Standard environment variables Standard environment variables are set for all Node-RED instances running within the platform: - `FF_INSTANCE_ID` - The unique identifier of the instance - `FF_INSTANCE_NAME` - The name of the instance as set in FlowFuse - `FF_INSTANCE_URL` - The full URL of the instance (e.g. `https://my-instance.flowfuse.cloud`) In addition, the following variables are set when running on a device: - `FF_DEVICE_ID` - The unique identifier of the device - `FF_DEVICE_NAME` - The name of the device as set in FlowFuse - `FF_DEVICE_TYPE` - The device type label assigned in FlowFuse - `FF_SNAPSHOT_ID` - The unique identifier of the snapshot currently deployed to the device - `FF_SNAPSHOT_NAME` - The name of the snapshot currently deployed to the device When deploying the same set of flows out to multiple devices, these variables can be used by the flows to identify the specific device being run on. NOTE: `FF_SNAPSHOT_NAME` will not be immediately updated when the current snapshot is edited. It will only be updated when the snapshot is changed or a setting that causes the device to be restarted is changed. # Chat Interface The FlowFuse Expert chat interface is where you actively engage the AI: asking questions, building flows directly on your canvas, and querying live operational data. While other FlowFuse Expert features (like inline code completions and next-node prediction) work passively in the Node-RED editor, the chat interface is where you direct Expert and see it act on your behalf. > Team owners can disable all AI features, including the chat interface, from the team settings page. If AI has been disabled for your team, the Expert panel will not be available. ## Opening the Chat Interface To open the chat, first open your Node-RED instance using the **Open Editor** button. This launches Immersive Mode, where the Expert panel is available alongside your canvas. While in Immersive Mode you also have access to all [instance settings](https://flowfuse.com/docs/user/instance-settings/) from a drawer that sits beside the canvas. You can manage environment variables, snapshots, the palette, and other settings without leaving the editor. Use the eye icon at the top of the drawer to move it left or right, pin or unpin it, or toggle fullscreen mode. > **Note:** Note: As of v2.29, the FlowFuse Expert panel opens automatically whenever you enter the FlowFuse platform or an instance. If you close the panel, your preference is saved — Expert will remain closed on your next visit so it doesn’t get in your way. To open or close the panel while within the editor, click the FlowFuse drawer button in the top-left corner of the editor. !["Chat interface of FlowFuse Expert alongside the Node-RED editor"](https://flowfuse.com/docs/user/images/assistant/flowfuse-expert-drawer.png){dataZoomable=""} ## Chat Modes The chat interface operates in two distinct modes. You can switch between them using the **Mode Selector** at the top of the Expert panel. ::div --- style: "display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 20px 0;" --- [ Support Mode ](https://flowfuse.com/#support-mode){.assistant-feature}[ Insights Mode](https://flowfuse.com/#insights-mode){.assistant-feature} :: ### Support Mode **Support mode** is for flow-building assistance. Use it when you need help understanding, building, or debugging your Node-RED flows. Expert draws on its knowledge of Node-RED, your installed palette, and the context of your current flows to answer questions, explain and debug flows, and, when agentic flow building is enabled, build flows directly on your canvas. Typical use cases in Support mode: - "How do I convert data to CSV for writing to file & do you have any flow examples?" - "Explain what this flow is doing" - "Why is this node outputting a number instead of a string?" - "Is `node-red-contrib-string` node installed on this instance?" - "Build a flow that reads from Modbus and publishes each tag value to MQTT" #### Building Flows on the Canvas Expert can act on your requests directly by adding tabs, placing and wiring nodes, and configuring node properties on the canvas, rather than only describing what you should do yourself. To use it, describe what you want to build in the chat input, the same way you would ask a question. Expert will start working immediately, and you can follow along via real-time status updates in the chat panel as each step completes. When it finishes, the result is live on your canvas. You can continue refining it through chat by asking Expert to adjust a configuration, add a node, or change a topic path, or you can edit the canvas directly as normal. ![FlowFuse Expert building and configuring a flow directly on the Node-RED canvas](https://flowfuse.com/docs/user/images/assistant/flowfuse-expert-building-flow.gif){dataZoomable=""} Some prompts that work well: - "Create a flow that reads Modbus registers from `192.168.1.10` every 5 seconds and publishes the values to MQTT on `factory/line3/temperature`" - "Build an OEE dashboard tab with downtime reason buttons and a shift target gauge" - "Add a shift handover screen that shows the last 5 alarms and a notes input field" - "Set up a flow that polls an HTTP endpoint every minute and sends an alert if the response status is not 200" The more specific your prompt, the closer the result will be to what you need. Include node names, topic paths, endpoints, or field names where you have them. Expert will use them directly rather than substituting placeholders. > **Availability:** This feature is available on **FlowFuse Cloud** for all tiers, and from v2.32, to Self-Hosted Enterprise tiers. For Self-Hosted Enterprise tiers it requires a configuration for your instance. [Contact us](https://flowfuse.com/contact-us/?subject=FlowFuse%20Expert%20Application%20Building){rel=""nofollow""} to get access. #### Staying in Control of What Expert Does When Expert builds on your canvas, you stay in control of what it does and when. Three features let you steer it before and during a build: it can ask clarifying questions, propose a plan for your approval, and request permission before running individual actions. > **Availability:** Clarifying questions, plan mode, and tool permissions are available to FlowFuse Cloud and self-hosted users from v2.32. ##### Clarifying Questions Rather than guessing when a request is ambiguous, Expert can ask you a few questions before it starts. It presents up to four questions in a single turn, each as its own option group, either single-select or multi-select depending on the question. You answer all of them together and submit once, and Expert uses your answers to build exactly what you intended. You can edit an answered question and resubmit if you change your mind, up until you send a new message. ![The Expert asking grouped clarifying questions before building, each with selectable options](https://flowfuse.com/docs/user/images/assistant/follow-up-questions.png){dataZoomable=""} You can control how often Expert asks from the **Follow-up questions** setting in the Expert settings dialog, choosing whether it asks all its questions **all at once** or **one at a time**. ##### Plan Mode Plan mode lets you review Expert's approach before it changes anything on your canvas. Turn it on using the **plan mode** toggle in the composer. While it is enabled, Expert responds to your request with a proposed plan instead of acting on it. The plan is shown as its own card with four actions: - **Approve:** Expert exits plan mode and carries out the plan. - **Edit:** the plan text is loaded into the composer so you can adjust it directly and resubmit. - **Request changes:** describe what you'd like changed in your own words, and Expert proposes an updated plan. - **Reject:** the plan is abandoned and nothing is built. ![A plan card in the Expert, showing the proposed plan with Approve, Edit, Request changes, and Reject actions](https://flowfuse.com/docs/user/images/assistant/plan-mode.png){dataZoomable=""} Once you send a newer message, an earlier plan card is disabled so you can't act on a stale plan. ##### Controlling What Expert Can Do You decide which actions Expert is allowed to take on your behalf, and which require your approval first. These cover both the flow-building actions Expert runs on your canvas and the platform actions it can take across FlowFuse outside the editor. Each action, such as reading a flow, writing nodes to the canvas, or creating an instance, carries an action type of **Read**, **Write**, or **Delete**, and each is governed by a permission. **Approving actions in chat.** When an action is set to require approval, Expert pauses and shows an approval card in the chat before running it. The card shows the friendly name of the action, its type (Read / Write / Delete), and the exact parameters of the call as formatted JSON, so you can see precisely what Expert is about to do. You can then choose: - **Allow:** run this action once. - **Always allow:** run this action, and don't ask again for it for the rest of this chat. - **Deny:** skip this action; Expert adapts and explains what it did instead. - **Always deny:** skip it and don't ask again for it for the rest of this chat. ![FlowFuse Expert pausing for approval mid-build, showing the Adding Nodes action awaiting your decision](https://flowfuse.com/docs/user/images/assistant/expert-tool-approval-card.png){dataZoomable=""} Expert waits as long as you need, with no timeout on the decision. Stopping the chat while a card is open cancels the pending action (treated as a denial). "Always allow" and "Always deny" choices apply only to the current conversation and reset when you use **Start Over** or refresh. If you want to keep one, click **Make permanent** to save that choice for future chats. Once you approve an action, the card collapses to show what was decided, and the build continues. ![The chat after actions have been approved, each card showing its action type and that it is allowed for the current chat](https://flowfuse.com/docs/user/images/assistant/expert-tool-approval-chat.png){dataZoomable=""} **Configuring permissions in settings.** You can set your team's permissions ahead of time from the Expert settings dialog. To open it: 1. Open the FlowFuse Expert panel and select **Support** mode. 2. Click the settings (gear) icon at the top-right of the composer. Permissions are saved per user, for the team you currently have active. If you switch to another team, your settings there are kept separately, so you can maintain a different policy per team without losing your choices when you switch back. ![The Expert settings dialog showing follow-up question cadence and the tool permission defaults for each action type](https://flowfuse.com/docs/user/images/assistant/expert-tool-permissions-settings.png){dataZoomable=""} For each action type you set a **default** for all Read actions, all Write actions, and all Delete actions, choosing between **Always allow**, **Ask**, and **Always deny**. You can then override individual actions where you want different behaviour; an override stays in place until you reset it. Each default shows how many actions are "set individually", with a **Reset** control to return those actions to the default. The settings split actions into two groups: **Flow Building Tools**, the actions Expert takes on your canvas inside the editor, and **FlowFuse Platform Tools**, the actions it takes across the wider FlowFuse platform, such as listing your applications and instances, checking an instance's status or logs, and creating a new instance. Which group is shown first depends on where you are: flow-building tools lead inside the editor, and platform tools lead in the app. Flow-building tools only run inside an instance editor, so open one to let the Expert use them, though you can set their permissions here at any time. **Role-based limits.** Permissions respect your team role. Read-only team members cannot enable or trigger actions that write or delete, and will see why they are unavailable. This is enforced by Expert itself, not just hidden in the interface. **Auditability.** Every action Expert takes on the platform is recorded in the [Audit Log](https://flowfuse.com/docs/user/logs#ai-agents-and-api-activity), alongside the tool it called and the user it was acting on behalf of. **Example permission setups.** A few common ways to configure this: - **Balanced (the default):** Read is set to *Always allow*, Write and Delete to *Ask*. Expert reads your flows freely and checks with you before changing or removing anything. - **Fast building session:** set Write to *Always allow* so Expert builds without interrupting you, while leaving Delete on *Ask* so removals still need a nod. You can also grant a single tool *Always allow* from its approval card to skip repeat prompts for just that action for the rest of the chat. - **Review-only / locked down:** set Delete to *Always deny* and Write to *Ask*, so Expert can propose and build step by step but can never remove anything. For example, if you ask Expert to "add three nodes" with Write set to *Ask*, it pauses on the first *Adding Nodes* call and shows you the payload. Choose **Allow** to let just that call through, or **Always allow** so the remaining node additions in this chat don't prompt you again. **Flow-building actions reference.** The flow-building actions available today, grouped by action type. New actions are added over time and some are gated to your instance's Expert version, so your list may differ slightly. | Action type | What it covers | Actions | | ----------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Read** | View only, no changes | Describing Node Type, Describing Property, Getting Flow, Getting Nodes, Getting Palette, Listing Config Nodes, Listing Node Types, Listing Nodes, Listing Subflows, Listing Tabs, Searching Canvas, Selecting Nodes, Showing Workspace | | **Write** | Create or change resources | Adding Nodes, Adding Subflow, Adding Tab, Arranging Nodes, Creating Subroutine, Managing Groups, Opening Palette Manager, Setting Links, Setting Wires, Updating Nodes, Updating Tab | | **Delete** | Remove resources | Removing Nodes, Removing Tab | **Platform actions reference.** The actions in the FlowFuse Platform Tools group, for working with your platform outside the editor. These are read and write only. There are no delete actions. | Action type | What it covers | Actions | | ----------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Read** | View only, no changes | List Applications, Get Application, Get Application Hosted Instances, Get Application Remote Instances, Get Application Instances Status, Get Application Audit Log, List Teams, Get Team, Get Hosted Instance, Get Hosted Instance Status, Get Hosted Instance Logs, Check Hosted Instance Name Availability, List Team Remote Instances, Get Remote Instance, Get Remote Instance Status, List Hosted Instance Snapshots, List Remote Instance Snapshots, List Hosted Instance Types, List Stacks, List Templates, List Blueprints, Open Hosted Instance, Open Hosted Instance Editor | | **Write** | Create or change resources | Create Application, Create Hosted Instance, Create Remote Instance, Assign Remote Instance To Application, Create Hosted Instance Snapshot, Create Remote Instance Snapshot | #### Context: What the Expert Can See Support mode becomes significantly more helpful when the Expert has context about your environment. Context is not automatic, you choose what to share with the Expert depending on what you need help with. ::div --- style: "display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 20px 0;" --- [ Palette Context ](https://flowfuse.com/#palette-context){.assistant-feature}[ Flow Context ](https://flowfuse.com/#flow-context){.assistant-feature}[ Debug Context](https://flowfuse.com/#debug-context){.assistant-feature} :: ##### Palette Context To add Palette Context, click the **Resource Selector** button (paperclip icon) in the chat interface and select **Palette**. Once added, the Expert has access to information about the nodes installed in your Node-RED instance - including installed packages and their versions. This allows you to ask questions like: - "Is my palette up to date?" - "What version of node-red-dashboard is installed?" - "Do I have a node available for reading from a PostgreSQL database?" The Expert can use palette context to tailor its suggestions - for example, recommending nodes you actually have installed rather than suggesting ones that are not available. ![Palette Context discussion with the FlowFuse Expert.](https://flowfuse.com/docs/user/images/assistant/palette-context.gif){dataZoomable=""} ##### Flow Context To add Flow Context, select the flow you want the Expert to reference from the flow tabs in the Node-RED editor. The selected flow is then added as context for the Expert to read and reason about. This makes it possible to ask questions directly about your flows without having to copy and paste JSON or describe your configuration manually. This allows you to ask questions like: - "What does this flow do?" - "Why does this flow output a number instead of a string?" - "Is there something wrong with this flow? I don't seem to get an output from the debug node!" Flow Context is what makes the Expert genuinely useful as a debugging and code review tool - it can see the same flow you're looking at and reason about it directly. ![FlowFuse Context discussion with the FlowFuse Expert.](https://flowfuse.com/docs/user/images/assistant/flow-context.gif){dataZoomable=""} ##### Debug Context To add Debug Context, you have 2 options: 1. Add individual log entries by clicking the ➕ button that appears over your debug message 2. Click the **Resource Selector** button (paperclip icon) in the chat interface and select **Add Debug Logs**. Once added, the Expert has access to the messages and output currently captured in your Node-RED debug panel. This allows the Expert to reason directly about the data or errors you are getting from your nodes at runtime, not just the structure of the flow itself. This allows you to ask questions like: - "Why is this debug output a number instead of a string?" - "What does this error message in the debug panel mean?" - "Is the payload structure here what my downstream node expects?" Debug Context is especially useful in combination with Flow Context - together they give the Expert both the structure of your flow and the actual data it is producing, making debugging significantly more effective. ![Debug Context discussion with the FlowFuse Expert.](https://flowfuse.com/docs/user/images/assistant/expert-debug-context.gif){dataZoomable=""} #### Resetting Context If the Expert starts giving unexpected or inconsistent answers, it may be due to accumulated context from earlier in the conversation influencing its responses. Use the **Start Over** button to clear the conversation and start fresh with a clean context. ### Insights Mode **Insights mode** connects the Expert to your live data via **Model Context Protocol (MCP)**. Use it when you want to query, analyze, or interact with real-world data - not just your Node-RED flows. In Insights mode, you first select an MCP Server that you've built using [FlowFuse MCP Server Nodes](https://flowfuse.com/node-red/flowfuse/mcp/){rel=""nofollow""}. The Expert can then use the tools and resources exposed by that server to answer questions against your live operational data. If you haven't built an MCP Server yet, see the guide on [building an MCP Server using FlowFuse](https://flowfuse.com/blog/2025/10/building-mcp-server-using-flowfuse/){rel=""nofollow""}. As of v2.32, Insights mode also reaches your **remote instances** at the edge, not just hosted instances. Point the Expert at a remote instance and ask about its live machine or operational data in plain language, with no dashboard to build and no query to write. > **Note:** Insights on remote and self-hosted instances relies on a change to how data is routed through the platform in v2.32. Remote instances need Device Agent 4.0.0 or newer, and existing hosted instances on FlowFuse Cloud need to update to Launcher 2.23.0 or newer to keep working. Typical use cases in Insights mode: - You have an MCP Resource named `production_lines_facilities_list`that returns a list of your production lines, their facility names and the facility types (stamping, assembly, packaging etc) - You can ask: "List all stamping facilities on our production lines" - You have added an MCP Tool named `get_production_live_state` -You can ask: "Tell me which of any of my assembly facilities are running and at what speed" - You have added an MCP tool named `get_production_oee`that - You can ask: "Show me the worst 3 OEE results for all production line facilities" > **Note:** Insights mode is currently in Beta. Capabilities are actively being expanded. **To switch to Insights mode:** 1. Open the FlowFuse Expert panel 2. Use the **Mode Selector** to switch from "Chat" to "Insights" 3. Select the MCP Server you want to query 4. Ask your question ## Writing Better Queries The quality of Expert's responses depends heavily on how your prompt is phrased, whether you are asking a question or requesting Expert to build something on the canvas. The more specific and contextual your prompt, the more accurate and actionable the result. Here are some common patterns to improve your prompts: ### Be specific about what you're referring to Vague references like "it", "this", or "that" require the Expert to guess what you mean. Name the thing explicitly. | Less effective | More effective | | ---------------------- | -------------------------------- | | "Is it up to date?" | "Is my palette up to date?" | | "What does this mean?" | "What does this log entry mean?" | | "Why did this happen?" | "Why did this error log occur?" | ### Describe the actual problem, not just a symptom If something isn't behaving as expected, describe what you expected versus what you got. | Less effective | More effective | | -------------------- | -------------------------------------------------------------- | | "This doesn't work" | "My flow should output a string but it is outputting a number" | | "The node is broken" | "The HTTP Request node is returning a 401 status code" | ### Include relevant details upfront The Expert works best when it doesn't have to ask clarifying questions. Include relevant context - the node type, the message property, the protocol, or the error - in your initial query. | Less effective | More effective | | --------------------------------- | --------------------------------------------------------------------------------------- | | "How do I connect to a database?" | "How do I connect to a PostgreSQL database using the node-red-contrib-postgresql node?" | | "How do I format this?" | "How do I format a Unix timestamp as an ISO 8601 string in a Function node?" | ### Ask one question at a time for complex topics If you have multiple questions, consider asking them separately so the Expert can give a focused answer to each rather than a broad response that covers everything superficially. *See also: [Node-RED Embedded AI](https://flowfuse.com/docs/user/expert/node-red-embedded-ai/) for AI features built directly into the Node-RED editor.* ## Keeping Expert Up to Date When a newer version of FlowFuse Expert is available, a banner appears in the chat area to let you know. You can update with a single click directly from the notification, without leaving your current workflow. ![FlowFuse Expert Update Banner](https://flowfuse.com/docs/user/images/assistant/ff-expert-update-banner.gif){dataZoomable=""} # FlowFuse Expert FlowFuse Expert is the AI built into FlowFuse and the Node-RED editor. It is not a generic AI assistant bolted onto the side of your workflow, it understands your flows, your installed nodes, your live data, and your environment in real time. **FlowFuse Expert is automatically installed and available in all hosted and remote instances running within or connected to FlowFuse**, no manual installation or configuration required. For self-hosted Enterprise customers, FlowFuse Expert can be enabled on request. [Contact us](https://flowfuse.com/contact-us/){rel=""nofollow""} to get it set up on your infrastructure. > **Note:** On self-hosted installations, FlowFuse Expert requires an Enterprise license and the platform's EMQX-based MQTT broker with the Team Broker capability enabled in the platform configuration (see [MQTT Broker configuration](https://flowfuse.com/docs/install/configuration#mqtt-broker-configuration)); installations running without EMQX cannot enable Expert. ## Managing AI Features **Team owners** can enable or disable all AI features for their team from the team settings page. When disabled, the Expert chat panel with all AI feature will get removed for that team. Running instances need to be restarted for the change to take full effect. **Self-Hosted Enterprise admins** have two additional controls: - Disable AI across the entire platform via the `ai.enabled` [configuration option](https://flowfuse.com/docs/install/configuration/#ai-configuration). This overrides all team-level settings. - Configure which AI features are available on a per-team-type basis from the Admin Panel. See [Managing Team Types](https://flowfuse.com/docs/admin/introduction/#managing-team-types) for details. ## What FlowFuse Expert Can Do FlowFuse Expert works in two distinct ways inside your environment. ### Chat Interface The Chat Interface is a conversational AI panel built into the FlowFuse Platform and accessible directly within the Node-RED editor. With agentic flow building enabled, you can describe what you want to build and Expert will build it on your canvas for you. It can also answer questions, debug flows, and query live operational data via MCP. The Chat Interface supports two modes: - **Support**: flow-building assistance, including asking questions, debugging, and building flows on the canvas. Expert can ask clarifying questions, propose a plan before it acts, and ask for your approval before running actions, and it can also take actions across the FlowFuse platform such as looking up your instances and creating new ones - **Insights**: query live operational data via MCP tools and resources exposed by your own MCP servers, on both hosted and remote instances [Learn more about the Chat Interface](https://flowfuse.com/docs/user/expert/chat/) ### AI in Node-RED FlowFuse Expert brings AI assistance directly into the Node-RED editor itself. It works where you already are - inside node editors, on the canvas, without requiring you to open a separate panel. AI features within the Node-RED editor include inline code completions, flow autocomplete, function builder, flow explainer, JSON generation, and CSS and HTML generation for FlowFuse Dashboard. > **Note:** FlowFuse Expert's in-editor AI features can also be installed as a plugin into Node-RED instances running outside of FlowFuse, using the `@flowfuse/nr-assistant` package from the Node-RED Palette Manager. This requires a FlowFuse Cloud account but does not require a paid subscription for the current release. The Chat Interface is exclusive to FlowFuse and cannot be installed externally. [Learn more about AI in Node-RED](https://flowfuse.com/docs/user/expert/node-red-embedded-ai/) ## Data Privacy No data from FlowFuse is used by third-party AI service providers for training models. Some features utilize the OpenAI API, and as such some data is sent to OpenAI to process requests. In accordance with the [OpenAI Terms of Service](https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance){rel=""nofollow""} no data is used for training of future models. OpenAI will retain data sent via its APIs for 30 days for abuse monitoring, after which it is permanently deleted. # AI in Node-RED FlowFuse Expert brings AI assistance directly into the Node-RED editor itself. Unlike the [Chat Interface](https://flowfuse.com/docs/user/expert/chat/), which is a conversational panel you open separately, FlowFuse Expert's in-editor AI works where you already are - inside node editors, on the canvas, and in the palette. > **Note:** FlowFuse Expert can also be installed as a plugin into Node-RED instances running outside of FlowFuse, using the `@flowfuse/nr-assistant` package from the Node-RED Palette Manager. This requires a FlowFuse Cloud account but does not require a paid subscription for the current release. To enable the latest features, ensure your instance is running the latest Stack. **For FlowFuse Cloud instances, FlowFuse Expert (`@flowfuse/nr-assistant`) is automatically updated to the latest version.** Team owners can disable all AI features, including the in-editor AI, from the team settings page. When AI is disabled, the `@flowfuse/nr-assistant` plugin will not be loaded on new deployments. Running instances will need to be restarted for the change to take full effect. ## Features ::div --- style: "display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 20px 0;" --- [ Flow Autocomplete ](https://flowfuse.com/#flow-autocomplete){.assistant-feature}[ Inline Code Completions ](https://flowfuse.com/#inline-code-completions){.assistant-feature}[ Flow Explainer ](https://flowfuse.com/#flow-explainer){.assistant-feature}[ Function Builder ](https://flowfuse.com/#function-node-creation){.assistant-feature}[ Function Code Generation ](https://flowfuse.com/#function-code-generation){.assistant-feature}[ JSON Generation ](https://flowfuse.com/#json-generation){.assistant-feature}[ CSS & HTML Generation](https://flowfuse.com/#css-and-html-generation-for-flowfuse-dashboard){.assistant-feature} :: 1. **Flow Autocomplete:** Automated, intelligent suggestions for which node should be added next in your flow 2. **Inline Code Completions:** Inline code completions for Function node, Tables Query node and FlowFuse Dashboard `ui-template` node 3. **Flow Explainer:** Get detailed explanations of the selected nodes in your flow 4. **Function Node Creation:** Create a new function node directly, driven by natural language 5. **Function Code Generation:** Within the scope of an existing function node, ask the assistant to write code for you 6. **JSON Generation:** In-editor JSON generation within the JSON editor for all typed inputs and JSON editors 7. **CSS and HTML Generation:** In-editor CSS and HTML generation for FlowFuse Dashboard `ui-template` nodes ### Flow Autocomplete ![Recording of the flow autocomplete in-action, with up/down keys used to toggle suggestions and tab to move to the next suggestion](https://flowfuse.com/docs/user/images/assistant/node-autocomplete.gif){dataZoomable="" width="700px"}*Recording of the flow autocomplete in-action, with up/down keys used to toggle suggestions and tab to move to the next suggestion* FlowFuse Expert runs a trained, in-browser machine learning model that provides intelligent suggestions for which node should be added next in your flow. You can accept the suggestion by clicking it or by pressing the `Tab` key. You can also toggle through suggestions by pressing the `Up` and `Down` keys. ### Inline Code Completions Mimicking the familiar code assistant in your IDE, FlowFuse Expert provides inline code completions for Function nodes, Tables Query nodes, and FlowFuse Dashboard `ui-template` nodes. ![inline code completions](https://flowfuse.com/docs/user/images/assistant/inline-completion.png){dataZoomable="" width="450px"}*A simple example of inline code completions for a Function node* This feature accelerates the writing of custom code and queries by providing intelligent suggestions without having to leave the editor, lowering the barrier to entry for non-technical users. Using comments is optional - the assistant will do its best to understand the context of the code and provide suggestions based on the surrounding code. However, writing comments does help to frame the request in a way that is more likely to produce accurate results. ### Flow Explainer FlowFuse Expert adds a button to the Assistant menu that will explain what the selected nodes do. To use this feature, select one or more nodes on the canvas and click the "Explain Flows" button in the Assistant menu. ### Function Node Creation ![FlowFuse Expert dialog](https://flowfuse.com/docs/user/images/assistant/dialog-function-node-builder.png)*Screenshot showing the FlowFuse Expert dialog for creating a function node* Use natural language to request a new function node be added to your Node-RED flow. This is useful when you want to quickly add a function node without having to drag it from the palette and write the code yourself. If your instance supports external modules, you can ask for a function node that uses them and they will be added to the function node setup automatically. If your function node requires multiple outputs, FlowFuse Expert will set the correct number of outputs accordingly. ### Function Code Generation FlowFuse Expert adds a code lens to the function node editor that allows you to generate code directly within the editor. ![inline code lens](https://flowfuse.com/docs/user/images/assistant/function-node-inline-code-lens.png){dataZoomable=""} This is useful when you want to quickly add or rewrite code within an existing function node without generating a full new function node from scratch. ### JSON Generation FlowFuse Expert adds a code lens to the JSON editor that allows you to generate JSON directly within the Monaco editor. ![json generation](https://flowfuse.com/docs/user/images/assistant/json-prompt.png){dataZoomable="" width="700px"}*Screenshot showing a FlowFuse Expert prompt for JSON generation* This is useful when you want to quickly generate JSON for a prototype or to test a piece of functionality in your flows. ![json generation](https://flowfuse.com/docs/user/images/assistant/json-results.png){dataZoomable="" width="700px"}*Screenshot showing the result of the above FlowFuse Expert prompt* ### CSS and HTML Generation for FlowFuse Dashboard FlowFuse Expert adds a code lens to the FlowFuse Dashboard `ui-template` node that allows you to generate CSS and HTML directly within the code editor. It is aware of the context of the node and will generate suitable CSS and HTML components for Vuetify and the FlowFuse Dashboard. ## Installing in External Node-RED Instances > **Note:** FlowFuse Cloud instances have FlowFuse Expert automatically installed. Manual installation is only needed for Node-RED instances running outside of FlowFuse. Only the in-editor AI features can be installed externally - the Chat Interface is exclusive to FlowFuse. To install the plugin in your own Node-RED instance: 1. Use the Node-RED Palette Manager to install the package `@flowfuse/nr-assistant` 2. Restart Node-RED 3. Click the FlowFuse Expert icon in the header 4. Follow the prompts to connect to your FlowFuse Cloud account 5. Once connected, you will be able to use all FlowFuse Expert features ### FlowFuse Expert for self-hosted customers If you are self-hosting FlowFuse with an Enterprise license, get in touch with [support](https://flowfuse.com/support) who will be able to help get you set up to use FlowFuse Expert locally. # FlowFuse Tables ::div{.ff-callout.ff-callout--warning} Warning :::div{.ff-callout__content} This feature is currently in [the beta state](https://flowfuse.com/handbook/engineering/releases/#beta-release){rel=""nofollow""}. ::: :: From FlowFuse v2.20.0 Teams (Enterprise teams only) can create a relational database to use to store data. You can create a database by selecting the Tables entry in the left hand menu ![Screenshot of FF Tables create database menu](https://flowfuse.com/docs/user/images/tables/create-database.png){dataZoomable=""}*Screenshot of FF Tables create database menu* Once you have created a database you can use the wizard to create some tables. The wizard offers a subset of the most used column types and has the option to set default values and generate sequences for ids. ![Screenshot of create tables wizard](https://flowfuse.com/docs/user/images/tables/create-table-wizard.png){dataZoomable=""}*Screenshot of create tables wizard* Once the tables have data in them then the first 10 rows will be displayed in the Explorer tab. ![Screenshot of Tables Explorer](https://flowfuse.com/docs/user/images/tables/tables-ui-screenshot.png){dataZoomable=""}*Screenshot of Tables Explorer* ## Query Nodes FlowFuse Node-RED instances come with a Query Node that will be enabled if running in a Team with a FlowFuse Tables database enabled. This node will automatically import the required credentials to connect to the database. ![Screenshot of Node-RED query node](https://flowfuse.com/docs/user/images/tables/tables-query-node.png){dataZoomable=""}*Screenshot of Node-RED query node* ## Postgres Clients You can connect any Postgres Client to the FlowFuse Tables database using the information on the Credentials tab ![Screenshot of credentials](https://flowfuse.com/docs/user/images/tables/credentials.png){dataZoomable=""}*Screenshot of credentials* # FlowFuse File Nodes Node-RED instances running within FlowFuse Cloud with Launcher version before 2.7.0 include a modified set of nodes that make it possible to store files safely regardless of the environment. Cloud based instances can read and write to persistent storage using these nodes. Edge devices will store files on its local filesystem. There are two nodes in the File Node collection: - `file` - A file node for writing to persistent storage - `file in` - A file node for reading from persistent storage ## FlowFuse 2.7.0 and later With the release of FlowFuse v2.7.0 a new Persistent Storage feature was enabled. This allows the default Node-RED File Nodes to work safely by mounting a volume to ensure files are persisted across all restarts of the Instance. On Docker, Kubernetes self hosted instances and FlowFuse Cloud the volume is mounted on `/data/storage`. The Current Working Directory for the Node-RED process is set to this directory, this means that if you do not specify a path in a node it will created or read from this directory. For LocalFS builds a `storage` directory is created in the Instance User Directory, this means that if FlowFuse is installed in `/opt/flowfuse` the directory will be at `/opt/flowfuse/var/projects//storage` ## Templates On FlowFuse Cloud before v2.7.0 the Default Template had an explicit exclusion for `10-file.js` to ensure that the replacement FlowFuse File Nodes were loaded. This Template was renamed to "Default v1" to differentiate it. If your instance is using this version of the template then it will need modifying to allow access to the default Node-RED File Nodes to make use of the new Persistent Storage. Please contact FlowFuse support to arrange this, they will also be able to migrate any files stored in the old service. ## Usage Simply drop the file nodes into your flows as you would with the regular file nodes in Node-RED. **Example:** Write string to a file, then read from the file :iframe{allow="clipboard-read; clipboard-write" height="100%" src="https://flows.nodered.org/flow/7f93fbbf67f9dc4e81bfbeb2b921881e/share" style="border: none;" width="100%"} There are more helpful built-in examples on the **Import Examples** dialog in Node-RED. ## Deployment Considerations When a snapshot is deployed to a device, the original Node-RED file nodes are used and any files will be stored on the device's local filesystem. # High Availability mode High Availability mode allows you to run multiple copies of your Node-RED instance, with incoming work distributed between them. The following requirements apply: - FlowFuse 1.8+ running with an EE license and the kubernetes driver - FlowFuse Cloud Within FlowFuse Cloud it is currently free to use for all teams, but will become a chargeable feature in a future release. ## Restrictions - Enabling or disabling HA mode requires a restart of the Instance. - When in HA mode, two copies of the flows are run. - Flows cannot be directly modified in an HA Instance; the editor is disabled. A [DevOps Pipeline](https://flowfuse.com/docs/user/devops-pipelines) should be created to deploy new flows to the instance. - Any internal state of the flows is not shared between the HA copies. - The [FlowFuse Persistent Context](https://flowfuse.com/docs/user/persistent-context) is not synchronised between the HA copies - The logs view show combined logs of all copies. An identifier indicates which replica the log message originates from. You have the possibility to filter the messages. More details of these restrictions are available below. ## Setting up High Availability mode To make full use of the HA mode you will need two separate Node-RED instances. The first will be your development instance. This is where you can build and test your flows. The second will be your HA 'production' instance. This instance will run multiple copies of the flows and get updated from the development instance using a DevOps Pipeline between the two instances. To enable HA mode on your production instance, open the [settings page](https://flowfuse.com/docs/user/instance-settings) for your instance and go to the High Availability section. Click the 'Enable HA mode' button and confirm the choice in the dialog. Once enabled the instance will be restarted to apply the settings. Once enabled, the editor will no longer be available. ## Editing an HA instance As the editor is disabled for an HA instance the flows cannot be directly modified. There are two options for updating the flows on an HA instance. ### Disabling HA mode If you disable HA mode via the setting page, after the instance has been restarted the editor will be available and changes can be made. However, this will mean a period of downtime whilst the instance is brought in and out of HA mode. ### DevOps Pipeline Create a [DevOps pipeline](https://flowfuse.com/docs/user/devops-pipelines) from a 'development' Instance to push updates to your HA instance. This allows you to free develop your flows in one instance and when you are happy with the results, use the pipeline to push the changes over to the HA instance. ## Building HA-ready flows Whilst FlowFuse can help run multiple copies of your flows, and provide the necessary load balancing between those copies, it still requires the flows to have been created with HA in mind. This guide provides some things to consider when building HA-ready flows. We'll continue to expand on this as we gather more feedback from our users. There are two things to consider - how state is managed and how work is distributed between the copies. ### Managing state The most well-suited flows for HA are those that do not depend on local state being maintain between individual messages coming into the flow. This is because, in an HA instance, the messages are distributed between the copies - none of them get to see every message. When state is required, it needs to be stored somewhere that all instances can access it - for example an external database service. FlowFuse provides a [persistent context](https://flowfuse.com/docs/user/persistent-context) option - however this includes a local caching layer that means it doesn't fully synchronize between the instances in real time. This is something we'll be working on for a future iteration of the feature, as it will require some changes in Node-RED to unlock its full potential. There is also the state that is implicitly maintained within the nodes of a flow. For example, the Smooth node can be used to calculate a running average value of messages passing through it. The node does that by keeping in memory the recent values so it can recalculate the average with each update. In an HA instance, the node will only be calculating the average for the messages it sees. ### Routing work When HA is enabled, all HTTP traffic directed at the instance will be load-balanced between the copies automatically. It gets a little more complicated where flows connect out to external systems to pull in work - for example, when using MQTT. MQTT includes a feature called [Shared Subscriptions](https://www.hivemq.com/blog/mqtt5-essentials-part7-shared-subscriptions/){rel=""nofollow""} that allows a broker to distribute messages between a group of subscribers. This provides the load balancing needed for an HA instance - but the flow must be configured to use the appropriate shared subscription topics, as well as to not set a ClientID on the connection to avoid conflicts between the connections. The [Project Nodes](https://flowfuse.com/docs/user/projectnodes) have been updated to support shared subscriptions automatically when running in HA mode. There are lots of other nodes that can be used to trigger flows, whether by listening for events on an API, connecting to locally attached hardware and many things in between. Typically, those that are more cloud-aligned, such as messaging systems like Kafka and AMQP will have very well established ways of doing load balancing. # HTTP Access Tokens When a FlowFuse instance is secured with [FlowFuse User Authentication](https://flowfuse.com/docs/user/instance-settings#flowfuse-user-authentication), only users who are in the same FlowFuse team can access the dashboard and HTTP endpoints created within Node-RED. In some cases, it is also necessary to provide secure access to the endpoints for other applications to use. This is where the HTTP Access Tokens can be used. With the FlowFuse User Authentication option enabled on the Instance's Security settings tab, the table of access tokens is shown. ### Creating an access token To create a token, click the Add Token button. Given your token a name and an optional expiry date, then click create. You will be shown the token value - this is the only time it will be shown so make a note of it before closing the dialog. ![](https://flowfuse.com/docs/user/images/bearer-token-dialog.png) ### Using an access token The token can be used when making an HTTP request to the Node-RED instance by providing it in the `Authorization` http header as a bearer token. For example, if a Node-RED instance has a flow deployed that includes an `HTTP In` node listening on `/token-demo`, the following `curl` command can be used to access it: ```shell curl -H "Authorization: Bearer ffhttp_FKc_S4qlTBV1H411hmhneHcSJ6F_FGNQLPYbnoD3-B0" \ https://example.flowfuse.cloud/token-demo ``` To access it from another Node-RED instance, you can use the `HTTP Request` node by enabling the 'Use authentication' option, selecting 'bearer authentication' and providing the token: ![](https://flowfuse.com/docs/user/images/bearer-token-nr-request.png) ### Remote Instances HTTP Access Tokens can also be used to secure HTTP endpoints served by Node-RED flows running on Remote Instances (devices). This requires [Device Agent v4](https://flowfuse.com/docs/device-agent/introduction) or later. On self-hosted FlowFuse, it also requires FlowFuse v2.32 or later. # Instance Settings The Instance Settings allow you to customize many aspects of your Node-RED runtime. To access them, click on an instance from the FlowFuse platform and select the **Settings** tab. The instance view also includes other tabs such as Overview, Devices, Version History, Assets, and Node-RED Logs. You can also access all the same settings from within the immersive Node-RED editor. The settings drawer sits alongside the canvas without overlapping it, so you can adjust environment variables, manage snapshots, update the palette, and make other changes without leaving your flow. You can customize the drawer layout using the eye icon at the top of the drawer, which lets you move it from right to left, pin or unpin it, and toggle fullscreen mode. ![Instance settings drawer](https://flowfuse.com/docs/user/images/instance-settings.png) Instance Settings are split into a number of sections: - [Instance Settings](https://flowfuse.com/#instance-settings) - [General](https://flowfuse.com/#general) - [Change Stack](https://flowfuse.com/#change-stack) - [Copy Instance](https://flowfuse.com/#copy-instance) - [Import Instance](https://flowfuse.com/#import-instance) - [Suspend Instance](https://flowfuse.com/#suspend-instance) - [Delete Instance](https://flowfuse.com/#delete-instance) - [Environment](https://flowfuse.com/#environment) - [High Availability](https://flowfuse.com/#high-availability) - [Editor](https://flowfuse.com/#editor) - [Security](https://flowfuse.com/#security) - [None](https://flowfuse.com/#none) - [Basic Authentication](https://flowfuse.com/#basic-authentication) - [FlowFuse User Authentication](https://flowfuse.com/#flowfuse-user-authentication) - [Palette](https://flowfuse.com/#palette) - [Alerts](https://flowfuse.com/#alerts) ## General This section includes several actions you can take on the instance: ### Change Stack The Stack determines the version of Node-RED being used. If a new stack is available, you can use this option to update your stack. ### Copy Instance This allows you to create a copy of the instance in your team. ### Import Instance This allows you to take existing Node-RED flow and credential files and import them into your instance. ### Suspend Instance This stops the instance entirely. ### Delete Instance If you're sure you don't want the instance anymore, this allows you to delete it. You cannot undo deleting an instance. Devices assigned to the instance will be unassigned from the instance and available to be reassigned to a new instance. ## Environment This allows you to manage the environment variables. More information on working with environment variables is available [here](https://flowfuse.com/docs/user/envvar). ## High Availability This allows you to manage the HA settings of the instance. High Availability is a Preview Feature. More information is available [here](https://flowfuse.com/docs/user/high-availability). ## Editor This covers many options to customize the Node-RED editor. This includes: - Disabling the editor entirely - Modifying the paths the editor and dashboard are served on - Choosing which code editor to use in your Node-RED nodes - Setting a custom title for the editor - Choosing a light or dark theme for the editor - Controlling the runtime timezone - Controlling the use of node modules in function nodes ## Security This allows you to modify the security settings of the runtime. In particular this covers the security applied to any HTTP routes served by the runtime. ### None The default option is not to apply any security - so any HTTP In nodes, or Node-RED Dashboard can be accessed by anyone. ### Basic Authentication You can optionally enable Basic Authentication, with a single hardcoded username and password. ### FlowFuse User Authentication Alternatively, with a licensed instance of FlowFuse, you can require anyone accessing those routes to be logged into FlowFuse. The hosted pages or API endpoints are only available for FlowFuse users who have access to the team on FlowFuse and the cloud instance. If using FlowFuse user Authentication you can also generate HTTP Bearer tokens that can be used to access APIs hosted in Instances with HTTP-in/HTTP-response nodes. More information on using HTTP Access Tokens can be found [here](https://flowfuse.com/docs/user/http-access-tokens). ## Palette This allows you to manage what extra nodes are installed inside Node-RED, as well as any restrictions you want to apply to the Palette Manager within Node-RED. It includes the option to add extra Node-RED Catalogue URLs and a `.npmrc` file that will be deployed to the instance. Details of the `.npmrc` format can be found [here](https://docs.npmjs.com/cli/v9/configuring-npm/npmrc){rel=""nofollow""} ## Alerts Alerts are a feature designed to provide email notifications based on specific Auditlog events. This functionality ensures prompt awareness and response to critical events. Users can configure alerts for the following Auditlog events: - Node-RED has crashed - Node-RED has been placed in Safe Mode When configuring alerts, you can choose the recipients of these notifications: - Team Owners - Team Members - Both Owners and Members # FlowFuse Node-RED Instance States The following list describes the possible states a hosted or remote Node-RED instance can find itself in. ## Stable States - **running**: Instance is fully operational. Flows are deployed and executing normally. - **suspended**: Instance resources are freed and flows are paused. Can be resumed without redeploying. - **stopped**: Instance is not executing any flows and is intentionally shut down. - **error**: Instance cannot operate normally due to a critical configuration or runtime issue. Requires user intervention. - **crashed**: Instance terminated unexpectedly due to repeated runtime errors or failures. - **rollback**: A previous working version of the instance has been restored due to deployment failure or manual revert. - **warning**: Instance is running but has non-critical issues (e.g., node failures, resource limits close to threshold). - **safe**: Running in safe mode after multiple crashes. Editor works, flows are not started until a deploy action is triggered. - **protected**: Editor is disabled. Deployment can only occur via pipeline or controlled automation. - **connected**: Instance has established a successful connection to its runtime environment and is reachable. ## Transitional States - **loading**: Instance UI or resources are being prepared (initial startup or page load). - **installing**: Required packages, dependencies, or container layers are being installed. - **starting**: Flows and runtime services are initializing. - **stopping**: Flows and runtime services are shutting down gracefully. - **restarting**: Instance is stopping and then immediately starting again, typically after a deployment or configuration update. - **suspending**: Instance flows are being paused and resources deallocated before entering suspended state. - **importing**: An external project or configuration is being applied to the instance. - **pushing**: Changes are being uploaded to the runtime or container registry. - **pulling**: Artifacts or project content are being fetched from a registry or pipeline source. # Getting Started with FlowFuse This guide will help you learn how to use the FlowFuse platform to quickly create new Node-RED applications after a successful [installation](https://flowfuse.com/docs/install/introduction) or [sign-up](https://app.flowforge.com/account/create){rel=""nofollow""} for FlowFuse Cloud. ## Creating a Node-RED Instance ### Your First Hosted Instance Your first Node-RED instance should be automatically created upon your initial login to FlowFuse. ![Instance created initial login](https://flowfuse.com/docs/user/images/getting-started/ff-home-initial-login.png){dataZoomable=""} **Accessing the Node-RED Editor:** To access the Node-RED Editor, simply click on the option shown in the image below marked with a red box: ![Open Editor Shortcut](https://flowfuse.com/docs/user/images/getting-started/open-editor-shortcut.png){dataZoomable=""} Alternatively, you can click on that instance and then you will find the "Open Editor" button at the top right: ![Open Editor](https://flowfuse.com/docs/user/images/getting-started/Open-Editor.png){dataZoomable=""} ### Creating Additional Instances For utilizing various other FlowFuse features (e.g., DevOps Pipelines), it's highly beneficial to create a second Node-RED instance. A second Node-RED instance is included in the Trial Phase of FlowFuse Cloud. **From the Home Page:** 1. Click on the "Add Instance" button shown in the image below 2. Enter the name you want for your instance 3. Select your application in which you want to create it 4. Select the instance type 5. Select the Node-RED version from the dropdown 6. Click "Next" and [select the blueprint](https://flowfuse.com/#selecting-a-blueprint) you want to use ![Add Instance](https://flowfuse.com/docs/user/images/getting-started/ff-home-after-initial-login-add-instance.png){dataZoomable=""} **From the Applications Page:** Alternatively, you can go to your applications from the left sidebar by clicking "Applications," select your application—in our example, "Demo's Application"—and click "Add Instance," then follow the same process. [Learn more about Instances](https://flowfuse.com/#working-with-instances) ### Your First Remote Instance A remote instance allows you to run Node-RED on your own hardware while managing it through FlowFuse. **Adding a Remote Instance:** 1. Click on "Remote Instances" from the left sidebar 2. Click "Add Remote Instance" 3. Enter a name for your instance and select the device type 4. Select your application and click "Add" ![Add Remote Instance](https://flowfuse.com/docs/user/images/getting-started/add-remote-instance.png){dataZoomable=""} ![Add Remote Instance Form](https://flowfuse.com/docs/user/images/getting-started/instance-add-form.png){dataZoomable=""} **Installing the Device Agent:** FlowFuse will show you a device configuration window with installation options: - **One-Line Install** (Recommended): Automatically installs Node.js (if needed), the device agent, and registers your device - **NPM Installation**: Manual installation instructions for Windows, Mac, or Linux ![Device Configuration Window](https://flowfuse.com/docs/user/images/getting-started/device-configuration-window-2.gif){dataZoomable=""} Follow the steps in the window to connect your device—it takes less than a minute. [Learn more about Device Agent](https://flowfuse.com/docs/device-agent/introduction) **Accessing Your Remote Instance:** Once registered, you can manage your remote Node-RED instance through FlowFuse: To start building flows: 1. Enable "Developer Mode" from the top right 2. Click "Open Editor" ![Developer Mode](https://flowfuse.com/docs/user/images/getting-started/developer-mode.png){dataZoomable=""} ![Open Editor](https://flowfuse.com/docs/user/images/getting-started/open-editor-remote-instance.png){dataZoomable=""} ### Selecting a Blueprint When creating a new Node-RED instance, you have the option to choose a blueprint tailored for specific use cases. For example, our "ANDON Operator Terminal" blueprint can be selected, and it will automatically configure the Node-RED instance, install necessary nodes, sparing you the need to start from scratch. Click "Create Instance" from the top-right to complete the process. While these templates are powerful out-of-the-box, they're also fully customizable, allowing you to tweak them to suit your unique requirements. Ultimately, blueprints speed up the learning curve for new users and expedite the solution-building process for experienced ones. ![Blueprint selection](https://flowfuse.com/docs/user/images/getting-started/blueprint-selection.png){dataZoomable=""} **NOTE**: *Some blueprints may only be available on certain tiers* ## Creating Your First Flow FlowFuse published an [eBook on Node-RED development](https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/){rel=""nofollow""}, which is a great resources when you're new to Node-RED. You can also read our [blog post on creating your first flow](https://flowfuse.com/blog/2023/01/getting-started-with-node-red/){rel=""nofollow""}. ## Creating Your First DevOps Pipeline DevOps Pipelines enable you to link multiple Node-RED instances together in a deployment pipeline. 1. **Add a Pipeline**: Select your application and click `Add Pipeline`. :br![Add Pipeline](https://flowfuse.com/docs/user/images/getting-started/Add-Pipeline.png) 2. **Name Your Pipeline**: Enter a suitable name. 3. **Add Stages**: You can now add stages to your pipeline. In our example, we add a Development Stage and a Production Stage. 4. **Execute the Pipeline**: It is now easy to execute the pipeline with one click, promoting your recently created flow to your Production Node-RED instance. :br![Execute Pipeline](https://flowfuse.com/docs/user/images/getting-started/devops-pipeline-w-stage.png) [Learn more about DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines/) ## Working with Devices FlowFuse supports managing Node-RED on your own hardware. - [Getting started with Devices](https://flowfuse.com/docs/device-agent/introduction) ## Working with Teams - [Team management](https://flowfuse.com/docs/user/team/) - How to add and remove users from a team. - [Role based access control](https://flowfuse.com/docs/user/team/#role-based-access-control) - Which privileges are granted to different roles. ## Working with Files and Context FlowFuse supports reading and writing persistent files and persistent context. - [Working with Files](https://flowfuse.com/docs/user/filenodes) - [Working with the Static Asset Service](https://flowfuse.com/docs/user/static-asset-service) - [Working with Context](https://flowfuse.com/docs/user/persistent-context) ## Working with Instances - [Dashboards](https://flowfuse.com/docs/user/dashboards) - View and switch between your Node-RED Dashboards from one place. - [Instance States](https://flowfuse.com/docs/user/instance-states) - List of states an instance can be in. - [Snapshots](https://flowfuse.com/docs/user/snapshots) - Create point-in-time backups of your Node-RED instances. - [Environment Variables](https://flowfuse.com/docs/user/envvar) - How to manage Environment Variables in your Node-RED instances. - [Change Project Stack](https://flowfuse.com/docs/user/changestack) - How to change an instance stack, for example to upgrade Node-RED. - [Logs](https://flowfuse.com/docs/user/logs) - The Logs available in the FlowFuse application. - [Project Link Nodes](https://flowfuse.com/docs/user/projectnodes) - Custom nodes for sending messages between Node-RED instances and devices. - [MQTT Nodes](https://flowfuse.com/docs/user/mqtt-nodes) - Custom nodes for zero config MQTT integration with the team broker. - [Instance Settings](https://flowfuse.com/docs/user/instance-settings) - Settings available for Node-RED instances. - [Shared Team Library](https://flowfuse.com/docs/user/shared-library) - Share flows easily between different Node-RED instances in your team. - [Node-RED Tools Plugin](https://flowfuse.com/docs/migration/node-red-tools) - A plugin for Node-RED that lets you work with your flows outside of FlowFuse. - [High Availability mode](https://flowfuse.com/docs/user/high-availability) - Run multiple copies of your instance for scaling and availability. - [FlowFuse Expert](https://flowfuse.com/docs/user/expert/) - A Node-RED plugin powered by AI, trained on FlowFuse content, that helps you code faster, build flows, and debug with context-aware guidance. ## Working with MQTT - [Team Broker](https://flowfuse.com/docs/user/teambroker) - Working with the FlowFuse bundled MQTT Broker ## Working with Custom Nodes - [Custom Nodes](https://flowfuse.com/docs/user/custom-npm-packages) - Publishing Custom Node-RED Nodes ## Working with FF Tables - [FF Tables](https://flowfuse.com/docs/user/ff-tables) - Databases # Logs FlowFuse presents log information in several different places depending on what you are interested in. ## Node-RED Logs The Node-RED logs are available for all instances running within the platform. They will contain information such as nodes being added and errors relating to your flows. The log information is kept back to the last time the instance container was restarted, you can view older information on the `Load earlier...` link at the top of the log. ![](https://flowfuse.com/docs/user/images/projectlog.png){width="500"} Node-RED logs can also be output from the Containers/Pods that run Instances on Docker or Kubernetes. This is enabled by the `forge.logPassthrough` option. More details can be found in the [Configuration](https://flowfuse.com/docs/install/configuration) documentation. ## Audit Log The Audit Log tab on the application and instance views shows key events that have happened. The events include: - User logging into the editor - Flows being updated - Nodes installed - Snapshots being created - Resource utilization warnings This log contains all events since the instance was created. You can view older data using the `Load More...` link at the bottom of the log. ![](https://flowfuse.com/docs/user/images/projectactivity.png){width="500"} ### Resource utilization warnings If the CPU or memory usage exceeds 75% for more than 5 minutes, a warning will be displayed in the Audit Log, indicating that measures such as upgrading the instance are recommended. ## Team Audit Log From the Team page the Audit Log shows events relating to the management of the team. This includes: - Applications/Instances being created or deleted - Users being added/removed from the team This log contains all events since the team was created. Tou can view older data using the `Load More...` link at the bottom of the log. ![](https://flowfuse.com/docs/user/images/teamauditlog.png){width="500"} ### AI Agents and API Activity For actions performed through FlowFuse Expert or the FlowFuse platform API, both this log and the instance/application-level [Audit Log](https://flowfuse.com/#audit-log) show an icon indicating how the action was performed: - A sparkle icon means the action was performed by FlowFuse Expert on the user's behalf. Hovering over the icon shows the name of the tool that was called. - A terminal icon means the action was performed through the FlowFuse API using a [Personal Access Token](https://flowfuse.com/docs/user/user-settings#personal-access-tokens). Actions performed directly through the FlowFuse UI do not show an icon. These logs provide visibility into supported FlowFuse Expert and API actions only. # FlowFuse MQTT Nodes Node-RED instances running within FlowFuse include a set of nodes that make it simple to securely connect and send or receive messages between MQTT clients in your team or externally connected clients via the [Team Broker](https://flowfuse.com/docs/user/teambroker/#getting-started-with-team-broker). The nodes are very similar to the Node-RED MQTT nodes, but without the need to configure any settings for your broker, making integration seamless. ### Nodes There are two nodes in this collection: - `MQTT In` - subscribes to fixed or dynamic topics - `MQTT Out` - publishes messages to fixed or dynamic topics The `MQTT In` node receives messages from the topic defined in the node's configuration or the `msg.topic` property if it is set. The `MQTT Out` node sends the `msg.payload` value to the topic defined in the node's configuration or the `msg.topic` property if it is set. See the built-in help on the Node-RED sidebar for more information about using these nodes. ### GitHub The nodes are published under an Apache-2.0 license and available on [GitHub](https://github.com/FlowFuse/nr-mqtt-nodes){rel=""nofollow""}. # FlowFuse Persistent Context Some Node-RED flows require the ability to persist context values between restarts and FlowFuse stack updates. By default, context data in Node-RED is ephemeral, meaning it does not survive restarts or stack updates. With FlowFuse's paid plans, however, you can enable **persistent context storage**, ensuring that your context values persist across restarts, upgrades, and more. ## Usage In Node-RED with FlowFuse, you now have two context store options: 1. **Memory Context**: This is the default ephemeral context. Values stored in memory are not persistent and will be lost when Node-RED restarts or the Node-RED stack is updated. 2. **Persistent Context**: This allows context values to persist even when Node-RED restarts, updates. The amount of persistent storage available to you depends on the FlowFuse plan you're subscribed to. FlowFuse offers different storage sizes for each plan, allowing you to select the appropriate level of storage for your needs. For detailed information on storage options, please refer to the [pricing page](https://flowfuse.com/pricing/){rel=""nofollow""}. ### How to Use FlowFuse Persistent Context Using persistent context in Node-RED is similar to using memory context, with the key difference being that you specify the storage type for your context data. Here’s how to use persistent context it: #### Using Persistent Context in Nodes When configuring persistent context in different nodes (e.g., Change, Inject, or Switch nodes), you can select the type of context storage to use. By default, the context type is set to **Memory**, but you can change it to **Persistent** to store values across restarts. - **Change Node, Inject Node, Switch Node**: :br When you configure these nodes to store or access context data, you’ll notice a storage option at the right corner. By default, it will be set to **Memory**. To make the context **persistent**, simply switch the selection to **Persistent**. ![Persistent Store Option in Change Node](https://flowfuse.com/docs/user/images/variables-in-node-red-change-node-persistent-store-option.gif) #### Using Persistent Context in Function Nodes In **Function nodes**, you interact with context data using the `set` and `get` methods. These methods allow you to specify where the context data should be stored or accessed from. - **Setting Context**: To set a persistent context value, use the `set` method with three arguments. The third argument specifies the context store, which should be set to **persistent**. For example: ```javascript context.set('myKey', 'myValue', 'persistent'); ``` This argument is optional and defaults to memory. This means that if you leave it empty, it will use memory as the context store. - **Getting Context**: When retrieving persistent context, use the `get` method with two arguments. The second argument is optional and specifies the context store (either memory or persistent). Example: ```javascript var value = context.get('myKey', 'persistent'); ``` If you don't specify the store, it defaults to memory. For more detailed information, refer to the article [Understanding Node, Flow, Global, and Environment Variables in Node-RED](https://flowfuse.com/blog/2024/05/understanding-node-flow-global-environment-variables-in-node-red/){rel=""nofollow""}. # FlowFuse Project Nodes Node-RED instances running within FlowFuse include a set of nodes that make it very quick and easy to securely send and receive messages between different instances in a team. The nodes act in a similar way to the Node-RED Link nodes, but by allowing the links to extend between different instances, they open up a wide range of possibilities. Remote instances can take part too, with the limitations described below. For example, a single Node-RED instance may contain a set of utility flows that you want to reuse in other instances. Rather than copy the flows around, the Project Nodes allow you to easily call those flows and get the result back. The flows stay in one place, so a change to them applies to every caller at once, with the trade-offs covered in [Limitations](https://flowfuse.com/#limitations). The project nodes are only available in the Enterprise tier of FlowFuse. ### Nodes There are three nodes in this collection: - `Project In` - listens for messages being broadcast by other Node-RED instances, or for messages being sent just to this instance - `Project Out` - sends messages to other Node-RED instances - `Project Call` - sends messages to other Node-RED instances and waits for a response The nodes send the whole `msg` object. Due to the way the nodes encode messages, there are some data types that do not get sent. For example, the `msg.req`/`msg.res` properties used by the core HTTP nodes will not be sent. Instead, they are temporarily removed from the message and re-attached when the message is received back. Each node is configured with a topic on which it either sends or receives messages on. This is similar in concept to MQTT topics - although the nodes do not currently support using MQTT wildcards in their topics. The Project Out nodes can either broadcast messages on a topic to anyone listening, or they can send messages on a topic to a specific other instance. The Project In nodes do the opposite - they can either listen for messages being broadcast, or for messages sent directly to them. The Project Call node can be used to send a message to another Project In node and then wait for a response, with a built-in timeout if it doesn't arrive. The response is sent back using a Project Out node configured to respond to the call node. ### Limitations **Only hosted instances can be addressed directly.** The list of targets and sources offered by the nodes is the hosted instances in your team. This applies to the `Project Out` node when sending to a specific instance, the `Project In` node when listening to a specific source, and the `Project Call` node's target. **A remote instance can send, but cannot be sent to.** Flows on a remote instance can use `Project Out` and `Project Call` to reach a hosted instance, and can receive broadcasts. They cannot be named as a target, so shared logic being called by others has to live on a hosted instance. A remote instance assigned to an application cannot receive direct messages at all, and can only listen for broadcasts. **Calls need the target to be up.** The `Project Call` node waits for the default timeout of 30 seconds, then logs an error that can be caught with a Catch node. The target instance must be running and have a `Project In` node listening on the same topic. Because every call travels through the platform's broker, logic called this way is unavailable to a caller whose connection to the platform is down. **Load is shared only in HA mode.** The instance holding the called flows serves every caller from a single Node-RED runtime. In [High Availability mode](https://flowfuse.com/docs/user/high-availability) the nodes automatically switch to MQTT shared subscriptions, so calls are distributed between the copies. ### GitHub The nodes are published under an Apache-2.0 license and available on [GitHub](https://github.com/FlowFuse/nr-project-nodes){rel=""nofollow""}. # Role-Based Access Control Role-based access control (RBAC) determines what actions users can perform within FlowFuse. By assigning roles to team members, you control who can create, modify, view, or delete resources. ## RBAC Levels FlowFuse provides role-based access control at two levels: 1. **Team-Level RBAC** - Defines default permissions across all team resources 2. **Application-Level RBAC** - Overrides team-level permissions for specific applications ## Team-Level RBAC Team-level roles establish baseline permissions for all resources within a team. Every team member is assigned one of four roles. ### Roles **Owner**:br Full administrative control over the team, including managing settings, members, and all resources. **Member**:br Can develop and manage flows, create snapshots, and modify environment variables. Cannot manage team settings or create/delete applications and instances. **Viewer**:br Read-only access to view flows, instance details, and snapshots. Cannot make any modifications. **Dashboard Only**:br Restricted access limited to viewing dashboards and HTTP endpoints only. ### Permissions The table below shows which actions each role can perform. | Action | Owner | Member | Viewer | Dashboard Only | | --------------------------------- | ----- | ------ | ------ | -------------- | | **Team Management** | | | | | | Manage Team Settings | ✓ | - | - | - | | View Team Audit Log | ✓ | - | - | - | | Invite User | ✓ | - | - | - | | Change User Role | ✓ | - | - | - | | Remove User from Team | ✓ | §1 | §1 | §1 | | **Applications** | | | | | | Create Application | ✓ | - | - | - | | Delete Application | ✓ | - | - | - | | Modify Application Settings | ✓ | - | - | - | | View Application Logs | ✓ | ✓ | ✓ | - | | **Instances** | | | | | | Create Instance | ✓ | - | - | - | | Delete Instance | ✓ | - | - | - | | Copy Instance | ✓ | - | - | - | | View Instance Details | ✓ | ✓ | ✓ | - | | Start, Stop, Suspend Instance | ✓ | - | - | - | | Modify Instance Settings | ✓ | - | - | - | | Modify Environment Variables | ✓ | ✓ | - | - | | Manage Assets | ✓ | ✓ | - | - | | View Node-RED Logs | ✓ | ✓ | ✓ | - | | Access Dashboard or HTTP Endpoint | ✓ | ✓ | ✓ | ✓ | | **Flows** | | | | | | Access Flow Editor | ✓ | ✓ | ✓ | - | | Modify Flows | ✓ | ✓ | - | - | | **Snapshots** | | | | | | Create Snapshot | ✓ | ✓ | - | - | | Restore Snapshot | ✓ | ✓ | - | - | | Set as Device Target | ✓ | ✓ | - | - | | View Snapshots | ✓ | ✓ | ✓ | - | | Download Snapshot | ✓ | ✓ | - | - | | Upload Snapshot | ✓ | - | - | - | | Delete Snapshot | ✓ | - | - | - | | **Devices** | | | | | | View Devices | ✓ | ✓ | ✓ | - | | Modify Device Settings | ✓ | - | - | - | | Modify Environment Variables | ✓ | ✓ | - | - | | Assign to/Remove from Application | ✓ | - | - | - | | Assign to/Remove from Instance | ✓ | - | - | - | | Delete Device | ✓ | - | - | - | | Bulk Move Devices | ✓ | - | - | - | | Bulk Delete Devices | ✓ | - | - | - | | **Team Library** | | | | | | Add an Item | ✓ | ✓ | - | - | | Modify an Item | ✓ | ✓ | - | - | | Delete an Item | ✓ | ✓ | - | - | | **Team Broker** | | | | | | Create Client | ✓ | ✓ | - | - | | Delete Client | ✓ | ✓ | - | - | | List Clients | ✓ | ✓ | - | - | **Notes:** - §1 Users in any role can remove themselves from a team - Platform Administrators have owner-level access to all teams but cannot access the Flow Editor ### Managing Team-Level Roles Team Owners can manage member roles from the **Team Members** page. #### Setting Roles When Inviting Members When inviting a new team member: 1. Navigate to the Team Members 2. Click **Invite Member** ![Invite team member popup](https://flowfuse.com/docs/user/images/invite-members.png){dataZoomable=""} 3. Enter the user's username or email address 4. Select the initial role (Owner, Member, Viewer, or Dashboard Only) ![Select role while inviting](https://flowfuse.com/docs/user/images/invite-popup.png){dataZoomable=""} 5. Send the invitation The invited user will have the assigned role once they accept the invitation. #### Changing Existing Member Roles To change a team member's role: 1. Navigate to the Team Members page 2. Locate the user whose role you want to change 3. Click the three-dot icon next to their username 4. Select **Change Role** ![Change member role](https://flowfuse.com/docs/user/images/change-role.png){dataZoomable=""} 5. Choose the new role from the popup (similar to the invitation process) 6. Confirm the change **Note:** An Owner can only change their own role if at least one other Owner exists on the team. ## Application-Level RBAC Application-Level RBAC enables you to control permissions at the individual application level within a team. This allows different team members to have different permission levels for different applications without creating multiple teams. This is an Enterprise lisenced feature for Self Hosted Users and requires an Entprise Team on FlowFuse Cloud. ### Overview Team-level roles define default permissions across all resources. :br Application-level roles override these defaults for specific applications. When you assign an application-level role to a team member, it takes precedence over their team-level role **only** for that application. Their team-level role applies to all other applications. ### Available Roles Application-level roles follow the same structure: - **Owner** – Full control over the application - **Member** – Can develop and manage flows, create snapshots, modify environment variables - **Viewer** – Read-only access - **Dashboard Only** – Can only view dashboards and HTTP endpoints ### Permission Hierarchy 1. If a user has an application-level role, that determines their permissions for the application. 2. If not, their team-level role applies. 3. Team Owners always have full access to all applications. **Example:**:br A team-level *Member* is assigned *Viewer* permissions for one production application. They can only view flows in that application, but retain normal Member permissions for all others. ### Configuring Application-Level Roles Team Owners can configure application-level roles: 1. Navigate to the application 2. Open **Application Settings** → **User Access** 3. Click the three-dot icon next to the user and select **Edit Permission** ![Application user access settings](https://flowfuse.com/docs/user/images/application-rbac.png){dataZoomable=""} 4. In the popup, assign the desired application-level role ![Application RBAC popup](https://flowfuse.com/docs/user/images/application-rbac-popup.png){dataZoomable=""} 5. Changes apply immediately To remove an application-level assignment, simply clear the role. The user will fall back to their team-level role for that application. # Shared Team Library Node-RED allows you to import and export **flows** and **functions** to a local library. This is helpful for saving pieces of flow that you want to reuse. With FlowFuse Premium, each Node-RED instance has access to a Team Library that makes it very easy to share flows and functions without having to manually copy them around. For example, you may have a standard set of flows that you want each Node-RED instance to include. By exporting them to the Team Library, you can quickly import them wherever you want to use them. A short video is available on [how to use this feature](https://www.youtube.com/watch?v=B7XK3TUklUU){rel=""nofollow""}. ### Exporting flows To export flows to the library within the Node-RED editor: 1. Select the nodes to be included 2. Open the Export Dialog (`Main Menu -> Export`, or `Ctrl/Cmd-E`) 3. Select the `Team Library` tab 4. Enter a name for the library entry 5. Click Export ### Importing flows To import flows within the Node-RED editor: 1. Open the Import Dialog (`Main Menu -> Import`, or `Ctrl/Cmd-I`) 2. Select the 'Team Library' tab 3. Select the flow to import 4. Click Import 5. Place the imported nodes within your flows ![](https://flowfuse.com/docs/user/images/shared-lib-import.png) ### Viewing Team Library It is possible to explore your Team Library within FlowFuse by clicking "Library" in your Team options. You can inspect the contents of any `.json` flow file, or `.js` function file here too. # Snapshots ## Introduction A Snapshot is a point-in-time backup of a Node-RED instance. It captures: 1. The flows 2. Credentials 3. Environment variables 4. NPM packages, with locked versions 5. Runtime settings. ## In this document - [Application Snapshots Overview](https://flowfuse.com/#application-snapshots-overview) - An overview of all snapshots belonging to an application and the available actions - [Instance Snapshots Overview](https://flowfuse.com/#instance-snapshots-overview)- An overview of all snapshots belonging to an instance - [Snapshots](https://flowfuse.com/#snapshot-list) - A list of all snapshots belonging to an instance and the available actions - [Timeline](https://flowfuse.com/#timeline-view) - A visual timeline of instance changes and snapshots interleaved - [Device Snapshots Overview](https://flowfuse.com/#device-snapshots-overview) - An overview of all snapshots belonging to a device and the available actions - [Create a snapshot](https://flowfuse.com/#create-a-snapshot) - Create a snapshot of a device or an instance - [Restore Snapshot](https://flowfuse.com/#restore-a-snapshot) - Apply a snapshot to the runtime of a device or an instance - [Edit a snapshot](https://flowfuse.com/#edit-a-snapshot) - Edit the name and description of a snapshot - [Upload a snapshot](https://flowfuse.com/#upload-a-snapshot) - Upload a snapshot to a device or an instance - [Download a snapshot](https://flowfuse.com/#download-a-snapshot) - Download a snapshot to your local machine - [Delete a snapshot](https://flowfuse.com/#delete-a-snapshot) - Delete a snapshot - [Set Device Target (instance)](https://flowfuse.com/#instance-owned-devices) - Set a snapshot as the target for all devices belonging to an instance - [Creating a Snapshot from a device](https://flowfuse.com/#creating-a-snapshot-from-a-device) - Create a snapshot from the device overview page - [Creating a Snapshot from within a device](https://flowfuse.com/#creating-a-snapshot-locally) - Create a snapshot from within Node-RED - [Auto Snapshots](https://flowfuse.com/#auto-snapshots) - Automatically create snapshots when flows are deployed - [Previewing Snapshots](https://flowfuse.com/#previewing-snapshots) - Preview the flows of a snapshot ## Snapshot Views: ### Application Snapshots Overview All snapshots belonging to the instances and devices of an application are gathered and presented in a single list where you can perform the following actions: - [Edit a snapshot](https://flowfuse.com/#edit-a-snapshot) - Edit the name and description of a snapshot - [View Snapshot](https://flowfuse.com/#previewing-snapshots) - Preview the flows of a snapshot - [Compare Snapshot](https://flowfuse.com/#comparing-snapshots) - Compare the snapshot with another snapshot - [Download Snapshot](https://flowfuse.com/#download-a-snapshot) - Download the snapshot to your local machine - [Delete Snapshot](https://flowfuse.com/#delete-a-snapshot) - Delete the snapshot ![Application Snapshots](https://flowfuse.com/docs/user/images/snapshots/application-snapshots.png)*Screenshot showing Applications Snapshot list* ### Instance Snapshots Overview Snapshots belonging to an instance are presented as a list or a visual timeline: #### Snapshot List Snapshots belonging to an instance are gathered and presented in a single list where you can perform the following actions: - [Upload Snapshot](https://flowfuse.com/#upload-a-snapshot) - Upload a snapshot to the instance - [Create Snapshot](https://flowfuse.com/#create-a-snapshot) - Create a snapshot of the instance - [Restore Snapshot](https://flowfuse.com/#setting-a-device-target-snapshot) - Restore the snapshot to the instance - [Edit Snapshot](https://flowfuse.com/#edit-a-snapshot) - Edit the snapshot name and description - [View Snapshot](https://flowfuse.com/#previewing-snapshots) - Preview the snapshot flows - [Compare Snapshot](https://flowfuse.com/#comparing-snapshots) - Compare the snapshot with another snapshot - [Download Snapshot](https://flowfuse.com/#download-a-snapshot) - Download the snapshot to your local machine - [Set as Device Target](https://flowfuse.com/#setting-a-device-target-snapshot) - Set the snapshot as the device target snapshot - [Delete Snapshot](https://flowfuse.com/#delete-a-snapshot) - Delete the snapshot ![Instance Snapshots](https://flowfuse.com/docs/user/images/snapshots/instance-snapshots.png)*Screenshot showing Instance Snapshot list* #### Timeline View The timeline view shows a visual representation of changes made to an instance with available snapshots interleaved to signify when they were created. Any snapshots displayed inline on the timeline will have the same actions available as in the Snapshot List view above. ![Visual Timeline](https://flowfuse.com/docs/user/images/snapshots/instance-timeline.png)*Screenshot showing Instance Visual Timeline* ### Device Snapshots Overview Snapshots belonging to a device are presented in a single list where you can perform the following actions: - [Upload Snapshot](https://flowfuse.com/#upload-a-snapshot) - Upload a snapshot to the instance - [Create Snapshot](https://flowfuse.com/#create-a-snapshot) - Create a snapshot of the instance - [Restore Snapshot](https://flowfuse.com/#setting-a-device-target-snapshot) - Set the snapshot as the devices target snapshot - [Edit Snapshot](https://flowfuse.com/#edit-a-snapshot) - Edit the snapshot name and description - [View Snapshot](https://flowfuse.com/#previewing-snapshots) - Preview the snapshot flows - [Compare Snapshot](https://flowfuse.com/#comparing-snapshots) - Compare the snapshot with another snapshot - [Download Snapshot](https://flowfuse.com/#download-a-snapshot) - Download the snapshot to your local machine - [Delete Snapshot](https://flowfuse.com/#delete-a-snapshot) - Delete the snapshot ![Device Snapshots](https://flowfuse.com/docs/user/images/snapshots/device-snapshots.png)*Screenshot showing Device Snapshot list* ## Snapshot Actions ### Create a snapshot To create a snapshot: 1. Go to the device or instance's page and select the **Snapshots** tab. 2. Click the **Create Snapshot** button. 3. You will be prompted to give the snapshot a **name** and optional **description**. 4. Click **Create** The list of snapshots will update with the newly created entry at the top. ### Restore a snapshot To restore a snapshot: 1. Go to the desired device or instance page and select the **Snapshots** tab. 2. Open the dropdown menu to the right of the snapshot you want to restore and select the **Restore Snapshot** option. 3. You will be asked to confirm - click **Confirm** to continue. ### Edit a snapshot To edit a snapshot: 1. Go to the instance's page and select the **Snapshots** tab. 2. Open the dropdown menu to the right of the snapshot you want to edit and select the **Edit Snapshot** option. 3. Update the name and description as required. 4. Click **Update** NOTE: Changes made to a snapshot will not be immediately reflected in the Node-RED runtime already running this snapshot. ### Upload a snapshot A snapshot can be uploaded to an device or instance from your local machine. To upload a snapshot: 1. Go to the desired instance or device overview page and select the **Snapshots** tab. 2. Click the **Upload Snapshot** button. 3. Select the snapshot file from your local machine. 4. Update the name and description if required. 5. Select the components to upload: - **Flows**: Include the snapshots flows - **Credentials**: Include the snapshots flows credentials (visible only if the snapshot contains credentials) - **Environment Variables**: Include environment variables in the snapshot - **Keys and Values**: Include the keys and values of the environment variables - **Keys Only**: Include only the keys of the environment variables 6. If the snapshot contains credentials and the `Credentials` component is checked, you will be asked to enter a Secret. This will be used to later decrypt any credentials in the snapshots flows. 7. Click **Upload** ### Download a snapshot A snapshot can be downloaded to your local machine for backup or sharing. To download a snapshot: 1. Go to the desired application, instance or device overview page and select the **Snapshots** tab. 2. Open the dropdown menu to the right of the snapshot you want to download and click the **Download Snapshot** option to open the download dialog. 3. Select the required components to download. - **Flows**: Include the snapshot flows - **Credentials**: Include the snapshot flows credentials - **Environment Variables**: Include environment variables in the snapshot - **Keys and Values**: Include the keys and values of the environment variables - **Keys Only**: Include only the keys of the environment variables 4. Enter a secret to encrypt any credentials in the snapshot (optional, depends on components selected). 5. Click **Download** ### Delete a snapshot To delete a snapshot: 1. Go to the instance's page and select the **Snapshots** tab. 2. Open the dropdown menu to the right of the snapshot you want to delete and select the **Delete snapshot** option. 3. You will be asked to confirm - click **Delete** to continue. *Note:* If the snapshot is the current **Device Target** snapshot, this will cause any connected devices to stop running the snapshot when they next check in. ### Setting a Device Target snapshot Snapshots are used to identify a version of the Node-RED instance that should be pushed out to any connected devices. This allows you to develop your flows in FlowFuse and only push out to the devices when it is ready. #### Instance owned devices To set the **Device Target** of an instance owned device: 1. Go to the instance's page and select the **Snapshots** tab. 2. Open the dropdown menu to the right of the snapshot you want to set as the device target and select the **Set as Device Target** option. 3. You will be asked to confirm - click **Set Target** to continue. This will cause the snapshot to be pushed out to any connected devices the next time it checks in. #### Application owned devices To set the **Device Target** of an application owned device: 1. Go to the devices's page and select the **Snapshots** tab. 2. In the list of snapshots available, a "Restore Snapshot" button will be displayed for each snapshot as you hover over it. 3. You will be asked to confirm - click the **Confirm** button to set it as the target snapshot. This will cause the snapshot to be pushed out to the device the next time it checks in. ### Creating a Snapshot from a device It is possible to create a Snapshot from a device that is connected to the platform. The device must be set to Developer Mode for this to work. See [Working with Devices](https://flowfuse.com/docs/device-agent/deploy) for more information. ### Creating a Snapshot locally Using the [Node-RED Tools Plugin](https://flowfuse.com/docs/migration/node-red-tools) it is also possible to create Snapshots in a local copy of Node-RED and push them back into your FlowFuse managed Node-RED instances. For more information, see the [Node-RED Tools Plugin guide](https://flowfuse.com/docs/migration/node-red-tools). ### Auto Snapshots FlowFuse can automatically create snapshots whenever flows are deployed to the instance. This is useful for tracking changes, and rolling back. FlowFuse will label these snapshots as "Auto snapshot - yyyy-mm-dd hh\:mm\:ss". A limit of 10 auto snapshots will be kept, with the oldest being deleted when a new one is created. Devices can optionally disable auto snapshots, in the developer mode tab. This can be helpful to avoid excessive data usage when a device is in the field or on a cellular connection. NOTE: This feature is only available to Enterprise tier teams ### Previewing Snapshots From any Snapshots tab, you can preview the flows of a snapshot by selecting the Snapshot's actions, and selecting "View Snapshot". ![Screenshot to show the available "Actions" for a given Snapshot](https://flowfuse.com/docs/user/images/snapshots-actions.png)*Screenshot to show the available "Actions" for a given Snapshot* ![Screenshot to an example flow preview for a Snapshot in FlowFuse](https://flowfuse.com/docs/user/images/snapshots-preview.png)*Screenshot to an example flow preview for a Snapshot in FlowFuse* ### Comparing Snapshots From any Snapshots tab, you can compare two snapshots by selecting the Snapshot's action, then selecting "Compare Snapshots". This will open a dialog where you choose a second snapshot to compare with. The comparison view has three panels: - **Left sidebar**— lists every node that differs between the two snapshots. Each entry shows a node-type badge (config node, tab, or regular node) and one of three statuses: - **Added** — the node exists in the newer snapshot but not the older one - **Deleted** — the node existed in the older snapshot but has been removed - **Changed** — the node exists in both snapshots but one or more properties differ - **Flow canvas** — highlights the selected node and scrolls to it automatically, giving a visual indication of where the change is in your flow - **Right panel** — shows the property and code changes for the selected node. Use the **Prev / Next** buttons to step through changes one at a time Selecting an entry in the sidebar highlights the corresponding node on the canvas. The highlight clears automatically when you select a different entry. There are two types of diff shown in the right panel: - **Property diffs** — each changed property is shown with the old and new value side by side, with red `-` for removed and green `+` for added ![Screenshot showing property-level diff with old and new values displayed side by side](https://flowfuse.com/docs/user/images/snapshots/snapshot-diff-prop-change.png)*Screenshot showing property-level diff with old and new values displayed side by side* - **Code diffs** — for multiline properties such as function and template nodes, changes appear as a line-level diff with red `-` for removed lines and green `+` for added, in the same format as a git diff. Use the **Prettify** and **Wrap** toggles to make large or nested values easier to read ![Screenshot showing a code diff for a function node with red and green line-level changes](https://flowfuse.com/docs/user/images/snapshots/snapshot-diff-code-change.png)*Screenshot showing a code diff for a function node with red and green line-level changes* Not every difference between two snapshots is meaningful. Computed layout properties such as group node `w` and `h` values are automatically excluded because they are recalculated by Node-RED and do not reflect intentional edits. Position-only changes (nodes that moved on the canvas but had no property edits) can be hidden when you want to focus on substantive changes. # Static asset service Our platform now includes a Static Asset Service, enabling you to manage files seamlessly within your hosted Node-RED instances. ## What is Static asset service? The Static Asset Service allows you to store files permanently within your FlowFuse Instances. Files that you upload, generate, or modify will remain accessible even after your session ends or the application restarts. ## Prerequisites ### FlowFuse Cloud - A Instance Stack with a launcher version of 2.8.0 or greater. - Enterprise Team Type. ### Self-Hosted This feature is available only on self-hosted Enterprise licensed versions of FlowFuse. ### Limitations - Uploaded file sizes must not exceed 5MB. - Unsupported characters (dependent on the operating system): - empty paths (eg: `/`, `//` or `\`, `\\`) - special chars: `*`, `:` - Team permissions required: owner / member ## Getting Started If the prerequisites are met, you will be able to use the Static Asset Service capabilities of FlowFuse Instances from two locations: - Instance Assets tab within the **Instance Details** page. ![Instance Details Page](https://flowfuse.com/docs/user/images/assets-tab-instance.png){dataZoomable=""} - Instance Assets tab within the **Immersive Editor**. ![Immersive Editor](https://flowfuse.com/docs/user/images/assets-tab-editor.png){dataZoomable=""} The following steps assume that you have navigated to one of these locations and have the assets tab opened. ### Files #### Uploading a File To upload a file, click the 'Upload' button, select your desired file, and click confirm. This will upload the file to the current storage folder. *Note: Uploading a file with the same name as an existing one will overwrite the existing file.* #### Deleting a File Navigate to the file you want to delete, click on the kebab menu (three vertical dots) associated with the file, select 'Delete File,' and confirm when prompted. #### Renaming a File Currently, this feature is not supported. To rename a file, upload the file with the desired name and delete the old one from the instance's Persistent Storage. *Caution: Renaming a file will also affect any linked files or relative paths to that file in the Node-RED instance.* ### Folders #### Creating a Folder To create a folder, click the 'New Folder' button, enter your desired folder name, and click confirm. This will create the folder inside the current directory. #### Deleting a Folder Navigate to the folder you want to delete, click on the kebab menu associated with the folder, select 'Delete Folder,' and confirm when prompted. *Caution: Deleting a folder that contains files or other folders will permanently delete all nested files and folders.* #### Renaming a Folder Navigate to the folder you want to rename, click on the kebab menu, select 'Edit Folder,' and confirm the new name when prompted. *Caution: Renaming a folder will also affect any linked files or relative paths of nested files or directories in the Node-RED instance.* #### Folder Navigation You can navigate through folder structures by clicking on any folder and return by using the Working Directory Breadcrumbs located at the top of the Search Files input. #### Folder Visibility Following the 2.9.0 release, you can set the folder's visibility using the Visibility selector found in the Navigation section. This means that users can set the visibility of their uploaded files to public and make them accessible outside the node-red instance itself. When setting a folder's visibility to public you are required to set a static file path on which the files will be served by your instance. ![static-assets-visibility-selector.png](https://flowfuse.com/docs/user/images/static-assets-visibility-selector.png){dataZoomable=""} ![static-assets-select-static-path.png](https://flowfuse.com/docs/user/images/static-assets-select-static-path.png){dataZoomable=""} ![static-assets-public-visibility.png](https://flowfuse.com/docs/user/images/static-assets-public-visibility.png){dataZoomable=""} Considerations: - Visibility and static path maps can be set on folders only. - Any change in visibility settings require an instance restart in order for the changes to take effect. ### How to use The following video is a quick demonstration on how to use assets inside a FlowFuse Node RED Instance: :video[ Your browser does not support the video tag.]{controls="true" width="800"} # Teams Teams are groups of users that collaborate on their Node-RED applications. ## Managing Teams ### Creating a New Team Users can create new teams. Creating a team requires a name and a unique url. The user who creates the team is added as its "owner". **Note:** Administrators might not [allow all users to create teams](https://flowfuse.com/docs/admin/introduction#admin-settings). FlowFuse Cloud does allow any user to create a new team. ### Adding Team Members Each user with "owner" permissions on a team can invite new members on the "Members" tab of the team page. Invitations can be sent to existing users on the platform by their username, or via email. Users have up to 7 days to accept, or decline, the invitation before it will expire. When inviting a member, you can assign them an initial role (Owner, Member, Viewer, or Dashboard Only). This role can be changed later by team owners. For more details on managing roles, see [Role-Based Access Control](https://flowfuse.com/docs/user/role-based-access-control). ### Removing Team Members Owners can remove a member from a team by clicking the dropdown menu next to the username and selecting `Remove from team`. The team member will no longer have access to any team data. ## Role-Based Access Control FlowFuse uses role-based access control to manage what users can do within teams and applications. For detailed information about roles, permissions, and access control at both team and application levels, see the [Role-Based Access Control documentation](https://flowfuse.com/docs/user/role-based-access-control). # Getting Started with Team Broker When FlowFuse is deployed with an Enterprise license from v2.11.0 onwards comes with the option to enable a MQTT broker for each Team. This is a single shared MQTT broker, but each team has their own separate topic space and the ability to provision credentials for clients. It removes the need to install and manage a broker of your own. ## Foreword FlowFuse offers zero config MQTT integration with the Team Broker via the [FlowFuse MQTT Nodes](https://flowfuse.com/docs/user/mqtt-nodes/) that greatly simplifies the whole process by removing the need for manual configuration. If you wish to continue using traditional MQTT clients, the below sections will guide you through the process of creating clients and connecting to the broker. ## Creating Clients When creating clients you can specify a username, it will prepended to the the Team's id e.g. `alice` will become `alice@32E4NEO5pY`. This username should also be used as the MQTT Client ID in order to connect to the broker. Examples of how to do this are in the [next section](https://flowfuse.com/#connecting-to-the-broker). ![Create Broker Client](https://flowfuse.com/docs/cloud/images/create-broker-client.png) ## Connecting to the Broker The broker for FlowFuse Cloud is available on `broker.flowfuse.cloud` and supports the following connection types: - MQTT on port `1883` - MQTT over TLS on port `8883` - MQTT over secure WebSockets on port `443` For Self Hosted instances, please ask your Administrator for hostname and ports. You can connect to the broker using any MQTT client, for example `mosquitto_sub` ```text mosquitto_sub -u "alice@32E4NEO5pY" -i "alice@32E4NEO5pY" -P "password" -h broker.flowfuse.cloud -t "#" ``` Please note that username **must** also be used for the client id to connect to the team broker. This does mean that each username/password can only be used with a single MQTT client at a time. Or in Node-RED as follows ![Node-RED MQTT Client Connection](https://flowfuse.com/docs/cloud/images/node-red-mqtt-connection.png) ![Node-RED MQTT Client Security](https://flowfuse.com/docs/cloud/images/node-red-mqtt-security.png) # User Settings Access the FlowFuse User settings by clicking on your username in the top right corner. ![User Settings](https://flowfuse.com/docs/user/images/user-settings.png) ## Settings In the Settings section, you can modify various characteristics of your personal user account: - **Username** - **Name** - **E-Mail** (modifiable only if SSO is disabled) - **Theme** - choose **Light**, **Dark**, or **System** for the FlowFuse interface. **System** follows your operating system setting and updates live when it changes. Your choice is saved per browser. This applies to the FlowFuse application only; the Node-RED editor and Dashboard manage their own themes separately. Additionally, you can select a default Team, especially useful if your account is associated with multiple Teams. ## Teams The Teams tab provides an overview of all teams associated with your User account. Here, you can also view and respond to open invitations. ## Security ### Change Password Under the Security section, you have the option to change your password. For this, you need to enter your current and new password. ### Two-Factor Authentication Two-factor authentication adds an extra layer of security to your account. It requires a second form of identification when logging in. Note that when signing in via your SSO provider, you will not be prompted for a two-factor authentication code. To set up Two-factor authentication, click on `Enable two factor authentication` and follow the instructions. ### Personal Access Tokens Personal Access Tokens are useful for interacting with [FlowFuse APIs](https://flowfuse.com/docs/api/){rel=""nofollow""}. You can set these tokens to have a limited or unlimited lifespan. Tokens can be revoked at any time by removing them from your account. Remember, tokens with an expiry date will automatically delete upon reaching that date. It's important to note that the token value is only displayed once, at the time of creation. There is no way to retrieve the token value after this point. ### Creating a Token 1. Click **Add Token**. 2. Enter a name for the token. 3. Optionally, tick **Add Expiry Date** and choose a date. 4. Configure the token's scope (see below). 5. Click **Create**. ![Creating a new scoped Personal Access Token](https://flowfuse.com/docs/user/images/scoped-pat.png){dataZoomable=""} ### Scoping a Token - **Read Only** - restricts the token to read-only operations. Write operations, such as creating, updating, or deleting resources, are denied. - **Team Scope** - limits the token to specific Teams. If no Teams are selected, the token can access every Team you belong to, including Teams you join in the future. If specific Teams are selected, the token does not automatically gain access to any new Team you join later - you need to edit the token to add it. - **Admin Access** - only shown if your account has admin privileges. A token does not carry admin privileges by default, even if your account does; this must be enabled explicitly for that token. These restrictions can only narrow a token's access - a token can never do more than your own account is permitted to do. # Introducing FlowFuse Inc. When Dave and I first created [Node-RED](https://nodered.org){rel=""nofollow""}, it was a tool to solve a problem - allowing us to do our day job more effectively when building IoT solutions for clients. That gave us the means and purpose to create a truly useful platform. When it became an open source project, it quickly found an enthusiastic audience that has seen the community grow beyond our imagination. From both individual users, to a wide range of companies integrating it into their own products. But with that growth, the question in my mind has always been how to take it further and secure its long term future. I wrote on the [project blog](https://nodered.org/blog/2020/10/13/future-plans){rel=""nofollow""} last year about the future plans of the project. A key piece of that is its sustainability - how we can increase the commercial adoption of Node-RED and how we can get more people contributing back. An opportunity presented itself earlier this year that I believe will bring a step-change to what the Node-RED project is able to achieve. ### Introducing FlowFuse Inc. So today, I'm launching FlowFuse Inc - a new company whose mission is to build a low-coding development platform fit for the enterprise with Node-RED at its heart. Backed by [Sid Sijbrandij](https://www.linkedin.com/in/sijbrandij/){rel=""nofollow""}, we have funding in place to create a fully remote team dedicated to an open core model. In the short term, this means helping to accelerate the plans already in place for the Open Source project. Getting the 2.0 release done in the next few weeks, working on long-standing features such as the Test framework and Flow Debugger. Alongside that we'll also be building a platform around Node-RED that will make it easier to adopt at scale and integrate into existing enterprise environments. Node-RED remains a fully open source project, with its home at the OpenJS Foundation and an open governance model that allows anyone to have a say in its development. Our goal is to incorporate as much of our work directly into the core project as possible. Where we do create closed-source components, we will work with the community to ensure the right APIs and extension points are in the core for all to benefit from. We will only be successful if the whole Node-RED community is successful. For me personally, this is a really exciting next step. I never expected to turn my little side project into a full-time job and now into a company. ### Hiring soon! We'll be hiring soon - so keep an eye out if you're interesting in getting involved. # Welcome Ben I'm excited to share the news that Ben Hardill ([@hardillb](https://twitter.com/hardillb){rel=""nofollow""}) is joining FlowFuse as a Senior Engineer. I've known Ben for many years, having worked together at IBM. He's been an active member of the Node-RED community since the start of the project and he published one of the very first [3rd party nodes](https://flows.nodered.org/node/node-red-node-geofence){rel=""nofollow""}. He is ever-present on Stack Overflow, to the point where I've long since stopped rushing to respond to questions tagged with [`node-red`](https://stackoverflow.com/questions/tagged/node-red){rel=""nofollow""} in the full knowledge that he has usually beaten me to it. More recently he's been doing some really interesting work exploring [multi-tenant Node-RED systems](https://www.hardill.me.uk/wordpress/2020/10/01/multi-tenant-node-red/){rel=""nofollow""} - something we'll be continuing at FlowFuse. Welcome aboard Ben! # Community News January 2022 Welcome to the first FlowFuse newsletter, we’re going to publish this as a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community, if you want to receive it via email, sign up for updates at the bottom of the page. If you’ve got something that you’d like us to share please email . [FlowFuse 0.1 Released](https://flowfuse.com/blog/2022/01/flowforge-01-released/):br First up we are really pleased to ship the first version of our platform, this is a very early release but hopefully will give you an idea of the direction we’re going in. [Node-RED 2.2](https://nodered.org/blog/2022/01/27/version-2-2-released){rel=""nofollow""}:br The next version of Node-RED has been released with new editor features, predefined environment variables and improvements to some of the core nodes. Checkout the blog post and change log for more details. [Make your IoT data beautiful](https://blog.golioth.io/building-iot-dashboards-with-golioth-grafana-and-node-red){rel=""nofollow""}:br Ben Mawby wrote a guide on connecting Golioth's WebSocket endpoints to Grafana using Node-RED and InfluxDB [Alexa Voice Service on Node-RED](https://www.sammachin.com/posts/alexaweb-reborn){rel=""nofollow""}:br Sam Machin rebuilt an app on Node-RED allowing you to talk to Alexa through the browser. And he has published a new [node](https://flows.nodered.org/node/@sammachin/node-red-alexa-voice-service){rel=""nofollow""} to use the Alexa Voice Service within your own flows. [New Team Members](https://flowfuse.com/blog):br We welcomed 2 new members of the FlowFuse team this month [ZJ](https://flowfuse.com/blog/2022/01/welcome-zj/) joins as our CEO and [Steve](https://flowfuse.com/blog/2022/01/welcome-steve/) has come onboard to work on the Node-RED project [We are Hiring](https://boards.greenhouse.io/flowfuse/jobs/4312861004){rel=""nofollow""}:br We're looking for the next member of our team, If you're a Node.JS developer and want to work with us take a look at the link. # FlowFuse 0.1 released For an open core company, we haven't been very open with what we're doing. That all changes today with the release of FlowFuse 0.1 and making all of our repositories public. This is a significant step for the company as we look to build a platform around Node-RED. The main question we get asked is: 'What is FlowFuse?" - which is a very reasonable question to ask. FlowFuse is a platform for managing Node-RED instances at scale. It lets you have multiple users on the platform, organised into teams to provide proper access control to individual Node-RED instances, or Projects as we call them. With the 0.1 release, we have the basic building blocks of the platform in place. - Add multiple users to the platform - Create teams for those users - Create Node-RED projects quickly and easily through the platform UI For a more complete walk-through of the platform in this early release, you can watch this video. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: YYZDx8n17Ys --- :: ### Getting started with FlowFuse The documentation provides a guide for [installing FlowFuse on a local server](https://github.com/FlowFuse/flowfuse/tree/main/docs){rel=""nofollow""}. We also have drivers for deploying to Docker Compose and Kubernetes based environments to enable a larger scale of deployment. We'll have more documentation on those options in the near future. ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. ### What's next? With so much in the plan and lots of exciting features to come, we will have a regular cycle of releases every four weeks. So you can expect the next release, 0.2, on Thursday 17th February. As we're only at 0.1 today, there is a lot still to do. We will be following the principles of [Semantic Versioning](https://semver.org/){rel=""nofollow""} with our releases, but until we reach 1.0, there may be some disruptive changes along the way. Sign up to the mailing list below if you want to hear more about the work we're doing. # Welcome Steve We're continuing to grow the FlowFuse team with our latest hire; Steve McLaughlin who is joining our development team. Steve is a well known face in the Node-RED community. He is a regular contributor to the community forum, always happy to help users with their questions. He has published a number of very popular nodes, include [buffer-parser](https://flows.nodered.org/node/node-red-contrib-buffer-parser){rel=""nofollow""}, [cron-plus](https://flows.nodered.org/node/node-red-contrib-cron-plus){rel=""nofollow""} and [image-tools](https://flows.nodered.org/node/node-red-contrib-image-tools){rel=""nofollow""}. He has also made some significant contributions to the core of Node-RED, such as delivering MQTTv5 support and introducing the Monaco code editor into the heart of the editor. Steve joins us from a background in Industrial IoT in the automative manufacturing space - experience and knowledge that will be invaluable as we look to growing the FlowFuse platform. Steve will be focussed on the Node-RED side of our activities - helping to continue the ongoing development and growth of the open source project at the heart of what FlowFuse is about. Welcome aboard Steve! # Welcome ZJ This year the FlowFuse team will grow further, and I'm excited to announce our newest addition to the team; Zeger-Jan van de Weg ([@ZJvandeWeg](https://twitter.com/ZJvandeWeg){rel=""nofollow""}). Zeger-Jan, also known as ZJ, is joining FlowFuse as CEO. ZJ previously worked at GitLab where he helped build a vibrant open source community and we're thrilled to have him to continue this with the Node-RED community. Under his supervision the involvement of GitLab in the Git project grew significantly, allowing Git and GitLab to be successful together. This aligns well with our vision for FlowFuse: > We will only be successful if the whole Node-RED community is successful. As our first non-engineering hire, ZJ will be focussed on growing the business side of FlowFuse, working on marketing and ongoing business development. Welcome aboard ZJ! # Announcing FlowFuse Cloud As an open core company, anyone is free to [download and install](https://github.com/FlowFuse/flowfuse/tree/9219e81399eaf52fb0ee5573707a52f5520fbfdd/docs/install){rel=""nofollow""} our platform. In many cases this is a great solution, it allows for custom setups in your own environment. We know this isn't for everyone though, some people just want to start building with Node-RED without having to manage their servers. We are excited to announce FlowFuse Cloud, a hosted Node-RED as a service offering and today we are opening the waitlist. Our waitlist captures your email, and we'll reach out to you on that address once your account is created. ### Starting operations After having released v0.2 recently, we're now working on v0.3 that will include a user flow for [billing](https://github.com/FlowFuse/flowfuse/issues/224){rel=""nofollow""}. When that work has been done and deployed people on the waitlist will slowly be invited to the platform. Currently that's scheduled for April 1st, no joke, although it could happen either sooner or later. More details on the exact pricing will be availble nearer the time. Once you are invited you will be able to; - Create multiple Node-RED projects hosted on flowforge.cloud, - Invite team members to collaborate on those projects, - And many more features will automatically become availble with each new release. # FlowFuse 0.2 released Four weeks have passed since our initial release of FlowFuse, and we're happy to release v0.2 today as we continue moving forward and evolve the platform. There aren't lots of headline features in this release to tell you about as a lot of the work has been on the internals, as well as responding to some of the early feedback from the community. Features like improving the test framework, and building a database migration framework may not sound too exciting to the end user, but they are critical pieces when build a platform that needs to be stable and easy to upgrade. We've also been doing work to get our own instance of the platform running in the Cloud - and figuring out how to automate as much of that as possible. Aside from being a key way to test the platform, it helps validate the work we're doing for when others come to run it in that way. It also lays the ground work for our own cloud service we'll be sharing more about in the coming days. The full change-log for the core of the platform is available [on GitHub](https://github.com/FlowFuse/flowfuse/blob/v0.2.0/CHANGELOG.md){rel=""nofollow""}. But with a further 15 repositories containing different components, each with its own change-log, we're still thinking about how best to share a single view of the updates. ### Getting started with FlowFuse The documentation provides a guide for [installing FlowFuse on a local server](https://github.com/FlowFuse/flowfuse/tree/main/docs){rel=""nofollow""}. If you haven't played with FlowFuse 0.1 yet, here's a more complete walk-through of the platform: ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: YYZDx8n17Ys --- :: ### Upgrading FlowFuse If you installed FlowFuse 0.1 and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. ### What's next? Our regular release cycle puts the next release on Thursday 17th March. We've got some key features planned in this release around [Project Templates](https://github.com/FlowFuse/flowfuse/issues/141){rel=""nofollow""} and [Stacks](https://github.com/FlowFuse/flowfuse/issues/285){rel=""nofollow""} - which will underpin how you can customise Node-RED within FlowFuse. We'll also have some exciting news to share about our own hosted service you'll be able to sign-up for. Sign up to the mailing list below if you want to hear more about the work we're doing. # Using Node-RED to keep Solar PV afloat [Krisnan Ravichandran](https://www.linkedin.com/in/krishnanravichandran/){rel=""nofollow""} works for [spb sonne](https://www.sbp.solar){rel=""nofollow""}, a engineering consultancy for renewable energy. Within the company he is part of the engineering effort on the [Gömbhal](https://www.sbp.de/en/news/goembhal-sbp-sonnes-pioneering-floating-pv-system/){rel=""nofollow""} project, creating a floatation device for solar panels. In this post, he shares his experiences of using Node-RED. Currently there’s a prototype deployed in Hungary, while the company is located in Stuttgart: “We’re remotely monitoring the installation. There are over 40 sensors that all connect to an ADAM-6717, Compact Intelligent Gateway. When the data is acquired we leverage Node-RED flows that maintain the structure, monitor performance, and provide reporting back to our offices. Node-RED is embedded in the ADAM 6717, it was very new to me. I was already experienced in programming, mainly Python, and within a month I felt very comfortable and productive in Node-RED. Understanding programming is useful, though not a necessity. Building and improving flows I did on my own through trial-and-error, as well as a lot of times through help on the [Node-RED Forum](https://discourse.nodered.org/){rel=""nofollow""}. The community is helpful and welcoming to new users. Now I maintain multiple flows with very different purposes. Some track temperature, irradiation wind-speed, direction, tilt and wave height; to ensure the floating PV installation remains floating. Other sensors are connected to actuators through flows that control pressure. We do have some challenges; the ADAM 6717 contained an older version of Node-RED. This raised questions around security and maintenance, as our Node-RED version isn’t updated to a newer version in an easy manner. It also hampers training a bit because documentation might reference an API or node for a flow that’s just not the same on older versions. However, I’d choose Node-RED again, it’s well known as well as easy to learn. Furthermore I found it very versatile.” --- Thanks to Krisnan for sharing his story. If you have a Node-RED story for us to share, please get in touch via . # Welcome Joe Today we welcome Joe Pavitt ([@joepavitt3d](https://twitter.com/joepavitt3d){rel=""nofollow""}) as our new Head of UX & Design. This is a key role that will help deliver the awesome user experience of the FlowFuse platform. Joe has a passion for user experience, data visualisation and creativity in technology. He joins us having been at IBM for 9 years where he specialised in building bespoke, first of a kind experiences in IBM's Emerging Technology and Research teams. We worked together at IBM and I saw first-hand the range and quality of what he can do. I was super pleased when he agreed to join us and I look forward to seeing the real impact he'll have on what we're building. Welcome aboard Joe! # Community News February 2022 Welcome to the FlowFuse newsletter, a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [Announcing FlowFuse Cloud](https://flowfuse.com/blog/2022/02/announcing-flowforge-cloud/):br We are excited to announce FlowFuse Cloud, a hosted Node-RED as a service offering and today we are opening the waitlist. [FlowFuse 0.2](https://flowfuse.com/blog/2022/02/flowforge-02-released/):br We continue to iterate with our 4 weekly releases of the FlowFuse platform. [Node-RED 2.2.2](https://discourse.nodered.org/t/node-red-2-2-2-released/58606){rel=""nofollow""}:br The 2.2 release of Node-RED (last month) has received 2 maintenance releases to fix bugs with duplicate wires in the editor and MQTT. [New Team Members](https://flowfuse.com/blog/2022/02/welcome-joe/):br FlowFuse is now up to 6 people, Joe Pavitt has joined the team [We still are Hiring](https://boards.greenhouse.io/flowfuse/jobs/4312861004){rel=""nofollow""}:br We're looking for the next member of our team, If you're a Node.JS developer and want to work with us take a look at the link. # FlowFuse 0.3 released The FlowFuse 0.3 release brings us closer to the launch of FlowFuse Cloud. Find out more about what's in this new release. This release of the FlowFuse platform brings some significant new features that will underpin more of what is to come. ### Project Stacks & Templates When we think about what a makes a Project inside FlowFuse, the simple answer is Node-RED. The more complete answer is: a version of Node-RED, a version of Node.js, some memory, some CPU and a bunch of Node-RED settings to customise the instance. In a platform like FlowFuse, it's important to have the tools to manage all of these things. This is where Project Stacks and Template come in. A Project Stack defines the underlying characteristics of the Node-RED process - or the container it is running in. For example, with our Local deployment model, it defines the version of Node-RED to use and how much memory the process should try to use. In our container based deployment models, the stack identifies the container to use for the project, along with memory and CPU limits. In a future release, this will be the way we will support upgrading the version of Node-RED a project is using - and doing so in a well managed way. An Administrator will be able to create a new Stack containing the new version of Node-RED. Project owners will then be able to update their projects to use the new Stack - at a time that is convenient to them. A Project Template is more about how the Node-RED instance is configured - exposing the options a user would traditional modify in their Node-RED settings file. With this release, we're not exposing a lot of settings as the focus has been more on the underlying Template concept. But it will be the basis for gradually exposing more options for customisation in the future. - [Epic #285 - Project Stacks](https://github.com/FlowFuse/flowfuse/issues/285){rel=""nofollow""} - [Epic #141 - Project Templates](https://github.com/FlowFuse/flowfuse/issues/141){rel=""nofollow""} ### Billing Integration With our open core philosophy, the heart of the FlowFuse platform is open source and available under the Apache 2 license for anyone to use. But the plan was always to have certain features that were licensed separately. This release brings the first of those features - Stripe Billing Integration. This feature brings the ability to require a Team to have a Stripe Billing agreement in place and to be able to charge on a per-project basis within that Team. Being able to charge is an important feature for any commercial platform, and with our own FlowFuse Cloud launching soon, we needed to get this feature in place today. We've structured the code in the repository and updated the LICENSE file to make it very clear what parts of the code base are *not* covered by the Apache 2 license. - [Epic #224 - Billing](https://github.com/FlowFuse/flowfuse/issues/224){rel=""nofollow""} ### Getting started with FlowFuse The documentation provides a guide for [installing FlowFuse on a local server](https://github.com/FlowFuse/flowfuse/tree/main/docs){rel=""nofollow""}. If you haven't played with FlowFuse yet, here's a more complete walk-through of the platform: ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: YYZDx8n17Ys --- :: ### Upgrading FlowFuse If you installed FlowFuse 0.1 or 0.2 and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. ### What's next? Our regular release cycle puts the next release on Thursday 14th April. We're still in planning stage for the release, but we'll also be beginning to invite people from the waiting list to sign-up to FlowFuse Cloud. For more information, check out the [annoucement blog post](https://flowfuse.com/blog/2022/02/announcing-flowforge-cloud/). You can also sign up to our general mailing list below if you want to hear more about the work we're doing. # Community News March 2022 Welcome to the FlowFuse newsletter, a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [New Website](https://flowfuse.com):br Some of you may have noticed the new design on our website, this is the first step in our more refined corporate identity, the same look will be coming to the FlowFuse application [soon.](https://github.com/FlowFuse/flowfuse/issues/430){rel=""nofollow""}. [FlowFuse 0.3](https://flowfuse.com/blog/2022/03/flowforge-03-released/):br The latest release introduced 2 new concepts, Templates and Stacks, with these we are starting to show the value of the FlowFuse platform allowing you to easily manage the Node-RED settings and versions used by your projects. [Intro to JSON for Node-RED](https://www.opto22.com/support/resources-tools/videos/video-introduction-to-json-for-node-red/){rel=""nofollow""}:br Our friends over at Opto 22 published a handy guide to JSON and how it works in Node-RED, great introduction for new builders and a good refresher for seasoned pros. # FlowFuse 0.4 released This release of the FlowFuse adds a seemingly small, but significant new feature. With [Node-RED 3.0 fast approaching](https://nodered.org/about/releases/){rel=""nofollow""} we've been making sure we are ready to support this. ### Upgrading Node-RED The goal of FlowFuse is to be the best way to run Node-RED at any scale, whether that's many users or many instances. Node-RED is a constantly developing as a platform and therefore part of running Node-RED is also upgrading the version you are running. With the 0.4 release today we've made that super simple in FlowFuse. Last month we introduced the concept of [Project Stacks](https://flowfuse.com/docs/user/concepts/#stack). One of the key elements of a Stack was the version of Node-RED in use. Initially this may have seemed fairly basic, when you create a new project you usually want to use the latest version of Node-RED. However what happens when a new version is released and you have an existing project? Now you can change the stack that a project is running on, which in turn will change the version of Node-RED. This is a simple process from the project settings, it only requires a short period of downtime while the project restarts on the new stack, typically around 10-15 seconds. Our driver to get this feature into the 0.4 release is the approaching release of Node-RED 3.0, now we know that we can be ready to offer our users Node-RED 3.0 as soon as it is released. We will also be making available the Beta's of Node-RED 3.0 within FlowFuse Cloud, this becomes a great way to test out the new features without having to touch your own environments. - [Story #288 - Change Stacks](https://github.com/FlowFuse/flowfuse/issues/288){rel=""nofollow""} - [Docs](https://flowfuse.com/docs/user/changestack/) ### Environment Variables Another key new feature we are introducing is the ability to set and manage environment variables within your projects. Environment Variables are a key tool when building applications as they allow you to to separate the configuration of your system from the logic in the code. Even in Low-Code platforms this is an important design pattern. Environment variables are fully integrated into [Templates](https://flowfuse.com/docs/user/concepts/#template) that we introduced last month so they can be set both at the platform level or on an individual project. Our plans for the next release will make these even more useful as we introduce the ability to [duplicate a project](https://github.com/FlowFuse/flowfuse/issues/271){rel=""nofollow""} and then modify those variables for the new project. - [Story #225 - Project Environment Variables](https://github.com/FlowFuse/flowfuse/issues/225){rel=""nofollow""} - [Docs](https://flowfuse.com/docs/user/envvar/) ### There's more There are many more improvements in this release, such as the ability to [Set the timezone](https://github.com/FlowFuse/flowfuse/issues/239){rel=""nofollow""} your project is running in, we've also been iterating on our billing experience as we've welcomed the first paying customers to FlowFuse Cloud. Finally we're very happy that we've had our first external contribution to the code base, as an Open Core company we believe strongly that Open Source lives at the heart of everything we do. We would like to say a big thank-you to [Fakorede Damilola Idris](https://fakocodes.netlify.app/){rel=""nofollow""} for his work on fixing a [bug](https://github.com/FlowFuse/flowfuse/issues/424){rel=""nofollow""} in the UI. ### Getting started with FlowFuse The documentation provides a guide for [installing FlowFuse on a local server](https://flowfuse.com/docs/install/). If you haven't played with FlowFuse yet, here's a more complete walk-through of the platform: ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: YYZDx8n17Ys --- :: ### Upgrading FlowFuse If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. ### What's next? Our regular release cycle puts the next release on Thursday 12th May. We will be building on features in the last few releases around managing your projects and using templates, we're also setting the foundations of our work to [manage Node-RED on your own devices running at the Edge](https://github.com/FlowFuse/flowfuse/issues/446){rel=""nofollow""}. For more information, check out the [announcement blog post](https://flowfuse.com/blog/2022/02/announcing-flowforge-cloud/). You can also sign up to our general mailing list below if you want to hear more about the work we're doing. # FlowFuse is accepting customers now A year ago our CTO, Nick O'Leary, [introduced FlowFuse](https://flowfuse.com/blog/2021/04/first-deploy/). Since then major milestones have been achieved. As we grow as a company, important steps are taken. Today we make another very important step; we're accepting our first customers. A few weeks ago our product was nearly in a state where it could support customers, when that was the case, we [opened up our waitlist](https://flowfuse.com/blog/2022/02/announcing-flowforge-cloud/). The waitlist has been growing daily and now we're ready to start inviting those users onto the platform. The first lucky few users are being added today, and each working day for the foreseeable future we'll continue onboarding users. New users will receive an email from our team with their login details and can start creating new workflows with Node-RED minutes after. While the source code of FlowFuse is [available](https://github.com/FlowFuse/flowfuse){rel=""nofollow""}, there's a chance you're unfamiliar with what has been built around Node-RED. With FlowFuse, our intent is to build a platform to aid with colaboration of flows in Node-RED. The first steps to our vision include a managed Node-RED instance to connect virtually any online service. Multiple users will have access to the same flows and can collaborate. Further, once Node-RED 3.0 has been released, the platform will provide a pain-free way to upgrade and keep your projects up to date. There's many more exciting features right around the corner on our [roadmap](https://flowfuse.com/changelog/) # Community News April 2022 Welcome to the FlowFuse newsletter, a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [FlowFuse 0.4](https://flowfuse.com/blog/2022/04/flowforge-04-released/):br The next release has enabled us to offer multiple versions of Node-RED along with adding more features to help you configure your Node-RED projects. [Node-RED 3.0.0-beta-1](https://discourse.nodered.org/t/node-red-3-0-0-beta-1-released/62124){rel=""nofollow""}:br The first beta for Node-RED 3.0.0 has been published, there's some exciting improvements on UI in the editor to make designing your flows even easier. [Node-RED Beta on FlowFuse](https://flowfuse.com/blog/2022/05/node-red-3-beta-stack/):br As promised in out 0.4 announcement, we've made the beta available to our users on FlowFuse as a separate stack, this is just one way that we're able to demonstrate the flexibility you get from running Node-RED on FlowFuse. [Cisco & Node-RED](https://developer.cisco.com/meraki/build/exploring-meraki-and-spark-apis-with-node-red/){rel=""nofollow""}:br The team at Cisco DevNet published some great guides on using Node-RED to manage both your Meraki Access points and to integrate with WebEx Teams, they've also produced a handy [starter guide](https://blogs.cisco.com/developer/helloworldlowcodenodered01){rel=""nofollow""} for those that are new to Node-RED [Node-RED Con 2022](https://nrcon.nodered.org){rel=""nofollow""}:br Our friends in the Node-RED Japan User Group have run a number of successful Node-RED conferences over the last few years. This year, we're joining forces with them to bring the event to a wider audience. The [Call for Papers](https://www.papercall.io/nrcon2022){rel=""nofollow""} is open now and we'd love to see your submissions. # FlowFuse 0.5 released The cycle continues with our next regularly scheduled release, bringing a fresh new look to the platform. Since joining the team, Joe has been hard at work bringing a more consistent design language to what we're doing. This release brings a lot of his hardwork to the platform itself. There's more to be done on the individual pages of the platform, but this gives us a solid framework to build on. ![](https://flowfuse.com/blog/2022/05/images/ff-05-dashboard.png) - [Epic #430 - Rebrand Forage App](https://github.com/FlowFuse/flowfuse/issues/430){rel=""nofollow""} ### Copying Projects One of the usage scenarios we want to support is having an easy way to have separate test and production environments. The previous release added the ability to configure environment variables on individual projects. This release unlocks the next piece of the puzzle - making it easy to copy flows between projects. The Project settings page has two new options: - Copy Project lets you create a complete copy of the project. - Export to existing project lets you copy over selected aspects of the project over to another project. In both cases, you get to pick what parts of the project should be copied. - [Epic #268 - Export Project](https://github.com/FlowFuse/flowfuse/issues/268){rel=""nofollow""} - [Story #271 - Duplicate Project](https://github.com/FlowFuse/flowfuse/issues/271){rel=""nofollow""} - [Story #272 - Export to Existing Project](https://github.com/FlowFuse/flowfuse/issues/272){rel=""nofollow""} ### Improve Billing Information We've been getting some great feedback from the users of FlowFuse Cloud. One of the areas we identified as needing some more clarity was around the point users are asked to setup their billing information. - [Story #563 - Improve Information on billing](https://github.com/FlowFuse/flowfuse/issues/563){rel=""nofollow""} ### Edge Devices Whilst we always want to deliver new functionality to end users in each release, sometimes bits of work don't fit naturally into a single four week iteration. That's the case here with some of the preliminary work we've done to introduce the idea of Edge Devices to the platform. The goal here is to provide a way to easily deploy and manage Node-RED projects on remote devices. This release introduces a bunch of work to the core app and front-end to begin introducing the concept of a Device. It includes the basic workflows for registering a device on the platform and being able to assign it to a team. The whole feature is hidden behind a feature flag so users on FlowFuse Cloud won't see any of this quite yet. The next release will introduce the Edge Agent piece of this - the bit that runs on devices. - [Epic #446 - Devices](https://github.com/FlowFuse/flowfuse/issues/446){rel=""nofollow""} ### Upgrading FlowFuse If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # Node-RED 3.0 Beta Stack The first beta of Node-RED 3.0 is here and FlowFuse is ready for you to try it out. When we released [FlowFuse 0.4](https://flowfuse.com/blog/2022/04/flowforge-04-released/) last month we talked about allowing users to select the stack their project runs on. Until now we've only offered one stack which has been the latest Node-RED release (2.2.2). Yesterday the first beta of Node-RED 3.0 was [released](https://discourse.nodered.org/t/node-red-3-0-0-beta-1-released/62124){rel=""nofollow""}, so as of today we have added a choice of stacks to FlowFuse Cloud. You can stick with the *Default* and use Node-RED 2.2.2 or if you want to try out the beta you can select *Node-RED-3.0.0-beta-1*. :video{ariaLabel="Selecting the beta Stack" autoPlay="true" height="730" loop="true" muted="true" playsInline="true" preload="none" width="954"} FlowFuse is the best way to run multiple Node-RED instances at different versions. Beta releases are exciting to try out, but you don't want to risk your production applications with an early upgrade. FlowFuse makes it easy to create a new project to try things out. We'll continue to update the stack choice with each beta when they are released. # FlowFuse open for everybody FlowFuse wants to enable everyone to build workflows in Node-RED. Since announcing [FlowFuse Cloud](https://flowforge.com/blog/2022/02/announcing-flowforge-cloud/){rel=""nofollow""} two months ago we've had a waiting list for users to sign up to. That allowed us to control the pace we were bringing new users onto the platform, learning what is needed to scale up our platform and continue to improve our first user experience. Today we have removed the waiting list. Anyone can sign up to FlowFuse and start a new Node-RED project in under a minute! ::div{.max-w-md.m-auto} [Sign up](https://app.flowfuse.com/account/create){.ff-btn.ff-btn--primary} :: ## What we offer Besides the sub minute time to start a new Node-RED project, there's many more features our offering includes. Two we'd like to highlight. To start our blog highlight reel: Collaboration. FlowFuse allows you to work with a team on your flows. There's the ability to create multiple users, each with their own credentials that can alter the flows on Node-RED, and it's execution environment like for example the [environments variables](https://flowforge.com/docs/user/envvar/){rel=""nofollow""}. Furthermore, [stacks](https://flowforge.com/docs/user/changestack){rel=""nofollow""}. These allow a user to select the execution environment for their Node-RED project. For example; the Node-RED version being used. Combined with the ability for one to copy a project to a new stack, this allows FlowFuse users to copy their project to the Node-RED 3.0-beta stack to validate their solutions will continue to work on the new release without disrupting their main project. ## Our roadmap Currently we're working towards our 0.6 release. The main feature of this release will be support for Devices. This will allow you to send a snapshot of a project to a Node-RED instance running outside of the FlowFuse platform and update the flows remotely. Remote devices will run our [agent](https://github.com/FlowFuse/device-agent){rel=""nofollow""} to communicate with the FlowFuse Cloud project. While the first iteration will be considered an Alpha release, by shipping early and often, it lets us get welcome feedback from our users and the wider community - helping to shape the future direction. It also allows users to start validating the feature for their own proof of concept projects. Over the next few months we're continuing to drive development of the platform across a number of areas - including further improvements to the Device feature. But also looking at new Enterprise-ready features, such as Single-Sign On integration and more tools to make collaboration even easy. We intend to grow our offering so that FlowFuse remains the best way to run Node-RED. Stay informed by registering for our newsletter! # Community News May 2022 Welcome to the FlowFuse newsletter, a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [FlowFuse 0.5 AND 0.6](https://flowfuse.com/blog/2022/06/flowforge-06-released/):br We've had two releases since our last newsletter, there are a lot of related features between them. We introduced a new design for the forge application which aligns with the branding on our website. We've added [Devices](https://flowfuse.com/docs/user/concepts/#device), allowing you to run and manage your Node-RED projects on your own hardware, this is ideal for applications that need to connect to either sensor data or specialist hardware deployed outside the cloud. We added a new concept as part of this work, [Snapshots](https://flowfuse.com/docs/user/concepts/#snapshot) allow you to take a point in time copy of your project, today that can then be deployed to one or more devices but we have plans to expand this concept for things like [rolling back](https://github.com/FlowFuse/flowfuse/issues/587){rel=""nofollow""} a project to a previous point in time. [0.5](https://flowfuse.com/blog/2022/05/flowforge-05-released/) Introduced the capabilities of copying a project or certain parts of it, allowing for scenarios like having multiple environments for Development and Production. [Node-RED 3.0.0-beta-3](https://discourse.nodered.org/t/node-red-3-0-0-beta-3-released/64027){rel=""nofollow""}:br The Node-RED 3.0 beta releases continue as the project is getting very close to the full release in the coming weeks, We'll be making it availble on FlowFuse cloud very soon. [Creating custom node from subflow in Node-RED](https://kazuhitoyokoi.medium.com/creating-custom-node-from-subflow-in-node-red-ce52cc42bbba){rel=""nofollow""}:br One of the contributors to Node-RED, Kazuhito Yokoi wrote a nice tutorial on how to turn a subflow into a custom node. [YouTube Channel](https://www.youtube.com/channel/UCbBzP8NZbv3WDtlt4UouA-g){rel=""nofollow""}:br Joe has been busy creating short videos to present FlowFuse and our key concepts, these will start to appear on our YouTube channel, so please like and subscribe! [Visa Direct in Node-RED](https://www.42flows.tech/blog/why-have-we-decided-to-implement-visa-direct-api-for-node-red/){rel=""nofollow""}:br The folks over at 42flows use Node-RED in a financial and banking context, they've published an article about integrating with the Visa payments APIs [Node-RED Con CFP](https://www.papercall.io/nrcon2022){rel=""nofollow""}:br A reminder about Node-RED Con and the call for papers, submissions close at the end of July but don't wait until the last minute to submit your proposal. # FlowFuse 0.6 released Node-RED is well known for its role in IoT solutions - which often means running flows on devices. This was something we always wanted to support in FlowFuse and with this release we're taking the next steps in that direction. ### Devices This release includes the first alpha release of the [FlowFuse Device Agent](https://github.com/FlowFuse/device-agent){rel=""nofollow""}. This is a small piece of node.js software that can be installed on a device, such as a Raspberry Pi. It connects back to the FlowFuse platform to get the Node-RED flows it should be running. This builds on the work we added in 0.5 that lets you register the device, generate credentials for it and pick which Project in your team it should be assigned to. It makes it super simple to start developing your flows in FlowFuse and push them out to a group of devices with a couple clicks. There's plenty of work still to come on the Devices feature. Under the covers it uses an HTTP polling approach to check for updates. That was a pragmatic choice to get something working - but it isn't our long term strategy. We'll be working towards a more IoT-native MQTT/WebSocket appoach in the coming releases. - [Devices documentation](https://flowfuse.com/docs/device-agent/introduction/) - [Epic #446 - Devices](https://github.com/FlowFuse/flowfuse/issues/446){rel=""nofollow""} ### Snapshots This release adds the ability to create Snapshots of your projects. These are point-in-time backups of your project's flows, credentials and settings. With this release we support *creating* snapshots and pushing them to devices. We don't have the ability to revert a project back to a previous snapshot, but that will come soon. - [Snapshot documentation](https://flowfuse.com/docs/user/snapshots/) - [Story #587 - Snapshot/Rollback](https://github.com/FlowFuse/flowfuse/issues/587){rel=""nofollow""} ### Other updates Beyond these headline features, there are a number of smaller, but just as useful items in this release. We've continued with the rebranding work started in 0.5 with some more improvements to the overall UX of the platform. Little touches like placeholder loading graphics give the UI a more responsive feel. When you log out of the platform we now also automatically log you out of any Node-RED editor sessions you have open. ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 0.6 - ready for you to start creating snapshots and adding devices right now. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # Community News June 2022 Welcome to the FlowFuse newsletter, a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [FlowFuse 0.7 ](https://flowfuse.com/blog/2022/07/flowforge-07-released):br We've shipped the next version of FlowFuse, features in this release include the ability to Rollback a project to a previous snapshot, setting environment variables for a specific device and we've begun to unify the experience between the Forge app and the Node-RED editor with our own Node-RED theme. [Node-RED 3.0 Released ](https://nodered.org/blog/2022/07/14/version-3-0-released){rel=""nofollow""}:br Node-RED 3.0 Has been officially released, there are a lot of improvements in the user experience of the editor, with new menus and junctions. There's also the ability to stop your flows while you can continue to edit and deploy. Take a look at the blog post for all the details. This is now the default stack on FlowFuse Cloud. [Node-RED for Microcontrollers](https://discourse.nodered.org/t/node-red-flows-on-esp8266-and-esp32/64345){rel=""nofollow""}:br[Peter Hoddie](https://twitter.com/phoddie){rel=""nofollow""} of [Moddable](https://moddable.com){rel=""nofollow""} Has released some early [work](https://github.com/phoddie/node-red-mcu){rel=""nofollow""} on getting the Node-RED runtime to execute a flow on a microcontroller like the ESP32. It's still very early days so don't expect to be building flows directly on the MCU, and the number of nodes that are supported is limited, but this is an interesting development for Node-RED in the IoT space. [Node-RED Con CFP](https://www.papercall.io/nrcon2022){rel=""nofollow""}:br A reminder about Node-RED Con and the call for papers, submissions close at the end of July so get your proposals in now. # FlowFuse 0.7 released Rollback projects to a previous snapshot, improvements in using Devices, and more. Keep reading for the details of whats in this release our you can watch our 1 min roundup video of the new release above. We're pleased to announce version 0.7 is now available. the next release of the FlowFuse application. ## Features [Rollback](https://github.com/FlowFuse/flowfuse/issues/587){rel=""nofollow""} FlowFuse is about running Node-RED at any scale, part of that scale is having multiple users collaborate on the same project. When you are collaborating with people it's important to be able to go back in time to a known working state. As part of that we are introducing rollbacks, this means that you can now take a snapshot of your project at a point in time and then make changes safe in the knowledge that you can rollback to that previous snapshot if you need to. [Device Environment Variables](https://github.com/FlowFuse/flowfuse/issues/680){rel=""nofollow""} In the last release we introduced the concept of devices. We're already learning from how these are used and one feature we've added in 0.7 is Device Environment Variables. You have been able to set Environment Variables at the project level but when deploying a snapshot to multiple devices you may want to override these values for each device, for example to set a site ID. With device specific variable users are enabled to differentiate based on the context in their flows. [FlowFuse Theme](https://github.com/FlowFuse/flowforge-nr-theme/){rel=""nofollow""} Now that we have a stronger visual identity in the Forge application we have continued that work through to the Node-RED editor. If you create or upgrade a project with a Node-RED 3.0 stack you will see a different theme in the editor. It's still very much Node-RED but just has some subtle hints to tie it back to the FlowFuse application. We will continue to iterate on this to further integrate the experience between FlowFuse and Node-RED in both directions. !["Screenshot showing the FlowFuse theme when the Node-RED 3.0 stack is selected"](https://flowfuse.com/blog/2022/07/images/ff-07-theme.png "Screenshot showing the FlowFuse theme when the Node-RED 3.0 stack is selected") [ProjectTypes](https://github.com/FlowFuse/flowfuse/issues/380){rel=""nofollow""} The introduction of ProjectTypes is a way to group Stacks together that share common characteristics - such as memory/cpu limits, or the availability of particular features. In platforms with billing enabled, such as our own FlowFuse Cloud, the ProjectTypes can have different price points set on them. Within FlowFuse Cloud, you'll see we've introduced the Small ProjectType - which applies to all existing projects on the platform. [Stack Versions](https://github.com/FlowFuse/flowfuse/issues/694){rel=""nofollow""} This allows an admin to link different stacks together in their lineage. This allows administrators to nudge users to new Node-RED versions or upgrade pre-installed dependencies when running in a container environment. Any users with projects on an old version will be prompted that there is an update available, making it even easier to stay up to date with Node-RED versions when you build your flows on FlowFuse. ## Improvements We've made a number of improvements to the overall experience of running FlowFuse. - The Team Switch menu has been moved to a more prominent position in the interface, this also makes it easier to see how to create a new team. [#616](https://github.com/FlowFuse/flowfuse/issues/616){rel=""nofollow""} - Notifications have had an overhaul, you will now see waiting invites on all pages. [#515](https://github.com/FlowFuse/flowfuse/issues/515){rel=""nofollow""} - If you are running your own copy of FlowFuse you can now see the version details in the admin pages [#655](https://github.com/FlowFuse/flowfuse/issues/655){rel=""nofollow""} - Device polling is no longer an INFO level message filling the log on your devices [#10](https://github.com/FlowFuse/device-agent/issues/10){rel=""nofollow""} ## Bug Fixes We've fixed the following bugs in this release. - [Devices now listen on all Interfaces allowing you to run local http servers](https://github.com/FlowFuse/device-agent/issues/7){rel=""nofollow""}:br - [Solved an issue where a device gets an error unknown device](https://github.com/FlowFuse/device-agent/issues/7){rel=""nofollow""}:br - [The Audit Log in the Forge app displays the correct IP when a user logs in to Node-RED](https://github.com/FlowFuse/flowfuse/issues/507){rel=""nofollow""}:br - [Resolved an issue with devices downloading snaphots from legacy stacks](https://github.com/FlowFuse/flowfuse/issues/507){rel=""nofollow""}:br - [Fixed an error where objects in the Node-RED log would hang the log page](https://github.com/FlowFuse/flowfuse/issues/735){rel=""nofollow""}:br - [Next Billing Date is now shown correctly](https://github.com/FlowFuse/flowfuse/issues/745){rel=""nofollow""}:br - [Fixed a bug where the loading page would flash during polling](https://github.com/FlowFuse/flowfuse/issues/689){rel=""nofollow""}:br ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 0.7 - ready for you to try out rollbacks and the new theme. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Customers of FlowFuse Cloud can raise a ticket by emailing We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # Introducing Medium Projects on FlowFuse Cloud We've added a second size of project to FlowFuse Cloud. A bigger project type with more resources available to it. Our [0.7 release](https://flowforge.com/blog/2022/07/flowforge-07-released/){rel=""nofollow""} introduced the concept of Project Types. This allows platforms to provide different sizes of projects, varying the memory/cpu or features available within a given type. Today we've put this feature to work on FlowFuse Cloud by introducing the new **Medium Project** type. !["Screenshot showing the new stack selection feature"](https://flowfuse.com/blog/2022/07/images/project-type.png "Screenshot showing the new stack selection feature") Medium projects have 3 times the resources of the existing Small type, allowing for more complex flows and larger message objects. This will be useful to business users looking to process complex sets of data. Our Medium project is priced at $50 a month and we'll be adding new features to this project type in the coming months to further enhance the value of this new tier. We don't currently support directly upgrading a project between types, but that is in the [plan for the future](https://github.com/FlowFuse/flowfuse/issues/595){rel=""nofollow""}. In the meantime, you can use the 'Export Project' feature on a project's settings tab to copy it over into a new Medium type project. # Community News July 2022 Welcome to the FlowFuse newsletter, a regular roundup of what\`s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [FlowFuse 0.8 ](https://flowfuse.com/blog/2022/08/flowforge-08-released/):br Version 0.8 was released, notable features include the new Project Link nodes for sharing data between projects, and the ability to stop and start flows within Node-RED. We've updated the format of our release posts as well to detail all the user facing changes, from new Features through to small improvements and bugs. [Raspberry Pi Pico with Node-RED](https://www.tomshardware.com/how-to/raspberry-pi-pico-w-node-red){rel=""nofollow""}:br[Les Pounder](https://twitter.com/biglesp){rel=""nofollow""} Has published a great tutorial on communicating with the new [Raspberry Pi Pico W](https://www.raspberrypi.com/products/raspberry-pi-pico/){rel=""nofollow""} and Node-RED. He shows you how to capture data from an environmental sensor then display this on a dashboard. [Medium Projects](https://flowfuse.com/blog/2022/07/new-projecttype/):br We've added a second size of project to FlowFuse Cloud. A bigger project type with more resources available to it. [Using Node-RED to control IoT Devices on Golioth](https://blog.golioth.io/how-to-use-node-red-to-control-iot-devices-on-golioth/){rel=""nofollow""}:br Our friends at [Golioth](https://golioth.io/){rel=""nofollow""} have published an article on how to use Node-RED to control and process data from IoT Devices connected to their platform. [Node-RED 3.0.2 and 2.2.3](https://discourse.nodered.org/t/node-red-2-2-3-and-3-0-2-released/66018){rel=""nofollow""}:br Node-RED 3.0.2 has been released fixing some bugs in the 3.0.0 release, The Node-RED 2.x stream has also had a maintenance release with many of the fixes in 3.0 back-ported. As usual these are already available as stacks on FlowFuse Cloud. # FlowFuse 0.8 released Easily pass messages between your projects on the cloud or devices, UX improvements, and more. Keep reading for the details of whats in this release our you can watch our 1 minute roundup video of the new release above. We're pleased to announce version 0.8 is now available. The next release of the FlowFuse application containing new features, a number of improvements, and bug fixes. ## Features [Project Link Nodes](https://github.com/FlowFuse/flowfuse/issues/662){rel=""nofollow""} We've introduced our first custom FlowFuse nodes to the palette of new projects. The Project Link nodes allow you to easily pass data between different projects within the same team. These projects can be running in the cloud or on devices, with the communication powered by our own internal MQTT broker. Try these out today on FlowFuse Cloud by creating a new project or updating your existing project's stack. There's more information in the [README](https://github.com/FlowFuse/nr-project-nodes/blob/main/README.md){rel=""nofollow""} for the nodes. For local installs of FlowFuse, the nodes are only available with an Enterprise Edition license. :video{ariaLabel="Video showing the message being sent from one project to another using project link nodes" autoPlay="true" height="678" loop="true" muted="true" playsInline="true" preload="none" width="1864"} [Start & Stop Flows](https://github.com/FlowFuse/flowfuse/issues/839){rel=""nofollow""} Node-RED 3.0 [introduced a new feature](https://nodered.org/blog/2022/07/14/version-3-0-released#editing-stopped-flows){rel=""nofollow""} that allows you to stop your flows from processing requests while still being able to work in the editor and deploy changes. We've now enabled this feature within FlowFuse for projects running a Node-RED 3.x stack. [Default Team](https://github.com/FlowFuse/flowfuse/issues/298){rel=""nofollow""} If you are a member of multiple teams you can now set your preferred default saving you from having to change teams each time you log in. ## Improvements We've made a number of improvements to the overall experience of running FlowFuse. - Devices now communicate to the Forge application over MQTT instead of polling [#754](https://github.com/FlowFuse/flowfuse/issues/754){rel=""nofollow""}. You'll need to update your Device Agent to the latest version to take advantage of this. - The table views have had a major overhaul allowing you to sort and search items [#28](https://github.com/FlowFuse/forge-ui-components/issues/28){rel=""nofollow""} - If the application receives an error you now see a notification in the UI. [#771](https://github.com/FlowFuse/flowfuse/issues/771){rel=""nofollow""} - The Verification email page has been cleaned up [#718](https://github.com/FlowFuse/flowfuse/issues/718){rel=""nofollow""} - The initial Thank-you page has been cleaned up [#648](https://github.com/FlowFuse/flowfuse/issues/648){rel=""nofollow""} ## Bug Fixes We've fixed the following bugs in this release. - [Logo Distorted in Safari](https://github.com/FlowFuse/flowfuse/issues/793){rel=""nofollow""}:br - [LocalFS Install doesn't check for Build Tools](https://github.com/FlowFuse/flowfuse/issues/729){rel=""nofollow""}:br - [Users with Expired passwords can create teams](https://github.com/FlowFuse/flowfuse/pull/842){rel=""nofollow""}:br - [Click-jacking Vulnerability](https://github.com/FlowFuse/flowfuse/pull/790){rel=""nofollow""} - [Users with can create teams without verifying email](https://github.com/FlowFuse/flowfuse/pull/824){rel=""nofollow""}:br - [Occasional Timeout when deploying flows](https://github.com/FlowFuse/flowforge-nr-storage/issues/17){rel=""nofollow""}:br - [Notification of member deletion contains internal ID](https://github.com/FlowFuse/flowfuse/issues/833){rel=""nofollow""}:br - [Pressing Enter in the Team Delete modal triggers cancel](https://github.com/FlowFuse/flowfuse/issues/334){rel=""nofollow""}:br - [Node-RED Isn't ready when Forge app says it is running (Docker)](https://github.com/FlowFuse/flowfuse/issues/751){rel=""nofollow""}:br ## Contributors We'd like the thank the following for their contributions to this release: - [HaroldPetersInskipp](https://github.com/HaroldPetersInskipp){rel=""nofollow""} helped [updating our documentation](https://github.com/FlowFuse/flowfuse/pull/812){rel=""nofollow""} - [Steveorevo](https://github.com/Steveorevo){rel=""nofollow""} also [updated our documentation](https://github.com/FlowFuse/flowfuse/pull/818){rel=""nofollow""} As an open-source project, we welcome the community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 0.8 and the stacks updated. Upgrade your project stacks to the latest version and start using the Project Link nodes now. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Customers of FlowFuse Cloud can raise a ticket by emailing We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # Community News August 2022 Welcome to the FlowFuse newsletter, a regular roundup of what’s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [**FlowFuse 0.9**](https://flowfuse.com/blog/2022/09/flowforge-09-released/):br Version 0.9 was released on 1st September. Our latest release includes some great new features, quality of life improvements and bug fixes. Notable additions included the ability to [suspend your projects](https://github.com/FlowFuse/flowfuse/pull/893){rel=""nofollow""}, [login with your email](https://github.com/FlowFuse/flowfuse/pull/856){rel=""nofollow""} and [define custom paths for your dashboards](https://github.com/FlowFuse/flowfuse/issues/774){rel=""nofollow""}. If you’d like to learn more about what else was included in 0.9 you can do so on our [blog post](https://flowfuse.com/blog/2022/09/flowforge-09-released/), on our [GitHub release page](https://github.com/FlowFuse/flowfuse/releases/tag/v0.9.0){rel=""nofollow""} and on our [Youtube channel](https://www.youtube.com/watch?v=d23Pmyc0k7I){rel=""nofollow""}. We’d also love for more of you to get involved in the development of FlowFuse, [contributions to the code](https://github.com/FlowFuse/flowfuse/blob/main/CONTRIBUTING.md){rel=""nofollow""} and [bug reports](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""} are really appreciated. [**Node-RED Con 2022**](https://nrcon.nodered.org/){rel=""nofollow""}:br We are happy to again be involved in Node-RED con. The event is being held online on 7th October, with content for both English and Japanese speakers. You can find out more on the [Node-RED Con website](https://nrcon.nodered.org/){rel=""nofollow""}. [**FlowFuse Team News**](https://flowfuse.com/about/):br We’d like to welcome Rob Marcer to the FlowFuse team. Rob has joined as our Developer Educator, he's going to work to help you get the best value from FlowFuse by developing our documentation and community support. We are also recruiting for [NodeJS Developers](https://boards.greenhouse.io/flowfuse/jobs/4463977004){rel=""nofollow""}, if you’re interested in joining our team please [apply here](https://boards.greenhouse.io/flowfuse/jobs/4463977004#app){rel=""nofollow""}. [**Official Node-RED Docker Image Passes Milestone**](https://twitter.com/Docker/status/1559919666721693699?t=QBzGGzY2kJ12Z5aoi1QPTA){rel=""nofollow""}:br Docker has [announced](https://twitter.com/Docker/status/1559919666721693699?t=QBzGGzY2kJ12Z5aoi1QPTA){rel=""nofollow""} that the Node-RED Docker image has now been downloaded over 100 million times. They have also created a [guide to using Node-RED](https://www.docker.com/blog/build-retail-store-items-detection-system-no-code-ai/?utm_campaign=2022-08-17-brnd-nocode&utm_medium=social&utm_source=twitter){rel=""nofollow""} to Build and Deploy a Retail Store Items Detection System Using No-Code AI Vision at the Edge. We think it’s worth a read. [**Simulating IOT Projects in Your Browser**](https://wokwi.com/){rel=""nofollow""}:br While not directly related to FlowFuse we’ve enjoyed wasting a little too much time looking at the simulated IOT projects on [Wokwi](https://wokwi.com/){rel=""nofollow""}. The [Simon Game with Score](https://wokwi.com/projects/328451800839488084){rel=""nofollow""} project is a little too addictive. [**Try FlowFuse for Free**](https://app.flowfuse.com/account/create){rel=""nofollow""}:br As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on FlowFuse when you create a new team. To get this discount please use the code RELEASE09 when on the payment page after creating a new team. # FlowFuse 0.10 released Secure your HTTP endpoints, create read-only users in your teams and use our static IP address for outbound traffic Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above. We're pleased to announce version 0.10 is now available. The next release of the FlowFuse application containing new features, a number of improvements, and bug fixes. Keep reading for a promotion code to get your first month free on FlowFuse Cloud. ## Features [Secure HTTP Endpoints](https://github.com/FlowFuse/flowfuse/issues/578){rel=""nofollow""} We've added the ability for you to secure your HTTP endpoints. You can now control who can access Dashboards or API endpoints you create in FlowFuse. [Read-only Users](https://github.com/FlowFuse/flowfuse/issues/657){rel=""nofollow""} We've added a new user role for Read-only access. This will allow users to login to your FlowFuse project and view the Node-RED flows without them being able to edit anything. [Static Outbound IP Addresses](https://flowfuse.com/docs/cloud/introduction/#ip-addresses) We've updated FlowFuse Cloud so that all outbound traffic from your projects now comes from a single IP address. When trying to access a remote resource such as a database it is often a requirement for the IP address the traffic comes from to be fixed. ## Improvements We've made a number of improvements to the overall experience of running FlowFuse. - Allow both key and component in a ff-data-table column definition [#43](https://github.com/FlowFuse/forge-ui-components/issues/43){rel=""nofollow""} - Default Stack and Templates [#989](https://github.com/FlowFuse/flowfuse/issues/989){rel=""nofollow""} - Provide platform containers and base stack container for administrators [#917](https://github.com/FlowFuse/flowfuse/issues/917){rel=""nofollow""} ## Bug Fixes We've fixed the following bugs in this release. - [Provide platform containers and base stack container for administrators](https://github.com/FlowFuse/flowfuse/issues/917){rel=""nofollow""} - [User names can be same (but different case)](https://github.com/FlowFuse/flowfuse/issues/983){rel=""nofollow""} - [User list not refreshing after changing user details](https://github.com/FlowFuse/flowfuse/issues/463){rel=""nofollow""} - [Navigating directly to a device page gets the wrong team selected](https://github.com/FlowFuse/flowfuse/issues/986){rel=""nofollow""} - [Node-RED Isn't ready when FlowFuse app says it is running following a project restart](https://github.com/FlowFuse/flowfuse/issues/941){rel=""nofollow""} - [Invitations left for deleted teams](https://github.com/FlowFuse/flowfuse/issues/923){rel=""nofollow""} - [Following email verification link twice throws error](https://github.com/FlowFuse/flowfuse/issues/1024){rel=""nofollow""} - [Agent does not log stderr from the Node-RED process](https://github.com/FlowFuse/device-agent/issues/21){rel=""nofollow""} - [On Kubernetes project names can not start with a number](https://github.com/FlowFuse/flowfuse/issues/948){rel=""nofollow""} - [When creating projects stack options do not wrap](https://github.com/FlowFuse/flowfuse/issues/930){rel=""nofollow""} - [Save button in admin user-edit dialog doesn't close dialog](https://github.com/FlowFuse/flowfuse/issues/979){rel=""nofollow""} - [Setting UI doesn't allow me to update settings](https://github.com/FlowFuse/flowfuse/issues/911){rel=""nofollow""} ## Contributors We'd like the thank the following for their contributions to this release: [Pezmc](https://github.com/Pezmc){rel=""nofollow""} for their work on [Add device count and project counts by type to admin](https://github.com/FlowFuse/flowfuse/pull/949){rel=""nofollow""} [ArshErgon](https://github.com/ArshErgon){rel=""nofollow""} for their work on [Update vue component name for NoVerifiedEmail.vue](https://github.com/FlowFuse/flowfuse/pull/977){rel=""nofollow""} As an open-source project, we welcome the community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ### Try it out [Sign up for FlowFuse Cloud](https://app.flowfuse.com/account/create?code=RELEASE010){rel=""nofollow""} with this link or at the checkout enter the code **RELEASE010** to get your first project free for a month. ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 0.10 and the stacks updated. Upgrade your project stacks to the latest version to make sure you get all the latest changes. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Customers of FlowFuse Cloud can raise a ticket by emailing We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # FlowFuse 0.9 released Suspend your projects when you don't need them, login with either your username or email, and introducing Team Types Keep reading for the details of what's in this release our you can watch our 1 minute roundup video of the new release above. We're pleased to announce version 0.9 is now available. The next release of the FlowFuse application containing new features, a number of improvements, and bug fixes. Keep reading for a promotion code to get your first month free on FlowFuse Cloud. ## Features [Suspend Projects](https://github.com/FlowFuse/flowfuse/issues/893){rel=""nofollow""} Sometimes you want to put a project to one side for a while, maybe your development has stalled or you're waiting on something external to be ready. Perhaps you don't need it to be running all the time. With the 0.9 release we've added the ability to suspend a project. Once suspended, your flows are safely stored in the platform database, but Node-RED isn't running and the project doesn't consume any resources. In FlowFuse Cloud we do not charge you for suspended projects - you only pay when the project is running. Your project will be there ready to start back up when you need it with just one click. Remember that any context data or anything written to the filesystem will not persist through a restart or a suspend of a project. Alongside this change, we've removed the option to 'stop' a project. That option would only stop Node-RED, but the underlying container would still be running, consuming resources. With Node-RED 3.0 adding the ability to stop the flows, but still be able to edit them, that provides a much better user experience. You can still restart the Node-RED process from the Forge app as before for example when you have updated a package in your flows. [Team Types](https://github.com/FlowFuse/flowfuse/issues/733){rel=""nofollow""} We've introduced another concept into the platform with this release. Team Types will allow us to offer more advanced features to teams on FlowFuse Cloud. You won't see much difference in this release but it allows us to build on in future releases. [PostHog Analytics](https://github.com/FlowFuse/flowfuse/issues/695){rel=""nofollow""} We've changed the analytics tooling integrated into the platform. With this release, we've deprecated the use of Plausible Analytics as it didn't quite provide the sort of insight we wanted. We now integrate with [PostHog](https://posthog.com/){rel=""nofollow""}. They share our ethos and approach to open source and self hosting - something you can take advantage of if you're running your own FlowFuse platform. For FlowFuse Cloud, the data is sent to our PostHog account so we can better understand how the platform is being used. If you're running your own instance, the information is only captured if you configure it with your own PostHog instance details - it does not send any data back to us. [Login with email](https://github.com/FlowFuse/flowfuse/issues/856){rel=""nofollow""} A common problem that we've seen from users is trying to login with their email address instead of their username. As of 0.9 you can now enter either at the login screen. [Custom Dashboard Path](https://github.com/FlowFuse/flowfuse/issues/774){rel=""nofollow""} If you are using the Node-RED Dashboard set of nodes, you can now change the path where the dashboard will be served from. The default is still `/ui` but you can now move that onto `/` or anything else. This is helpful when migrating existing projects over to FlowFuse. ## Improvements We've made a number of improvements to the overall experience of running FlowFuse. - Improvements to the FlowFuse Theme [#883](https://github.com/FlowFuse/flowfuse/pull/883){rel=""nofollow""}. - Upper-case characters in Project Names [#546](https://github.com/FlowFuse/flowfuse/issues/546){rel=""nofollow""} - Password reset requests are logged[#773](https://github.com/FlowFuse/flowfuse/issues/773){rel=""nofollow""} - Admin can manually verify users email [#902](https://github.com/FlowFuse/flowfuse/issues/692){rel=""nofollow""} ## Bug Fixes We've fixed the following bugs in this release. - [Cannot edit template settings](https://github.com/FlowFuse/flowfuse/issues/875){rel=""nofollow""}:br - [Project Link Nodes Appear in CE Install](https://github.com/FlowFuse/nr-project-nodes/issues/10){rel=""nofollow""} - [Project Link Nodes MQTT Connection](https://github.com/FlowFuse/nr-project-nodes/issues/14){rel=""nofollow""} - [Theme shows white characters on white background](https://github.com/FlowFuse/flowforge-nr-theme/issues/19){rel=""nofollow""} - [Changing Project on device doesn't remove old modules](https://github.com/FlowFuse/device-agent/issues/27){rel=""nofollow""} - [Device Agent and Node-RED use different time in logs](https://github.com/FlowFuse/device-agent/issues/30){rel=""nofollow""} ## Contributors We'd like the thank the following for their contributions to this release: [Bonantech](https://github.com/bonanitech){rel=""nofollow""} for his work [cleaning up the theme CSS](https://github.com/FlowFuse/flowforge-nr-theme/commit/30e21a3777dc3438ef206157ee9110728011f59c){rel=""nofollow""} As an open-source project, we welcome the community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowforge.com/docs/contribute/){rel=""nofollow""}. ### Try it out [Sign up for FlowFuse Cloud](https://app.flowfuse.com/account/create){rel=""nofollow""} and at the checkout enter the code **RELEASE09** to get your first project free for a month. ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 0.9 and the stacks updated. Upgrade your project stacks to the latest version and start using the Project Link nodes now. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please do raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Customers of FlowFuse Cloud can raise a ticket by emailing We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # Static Outbound IP Addresses On Friday last week we updated FlowFuse Cloud to use a static IP address for outbound traffic. This will allow you to predict which IP address your traffic will come from for example when traversing a firewall or accessing a remote database. You will need to manually suspend then start each of your projects (a restart will not move your projects to the fixed IP address). Once that action is completed all outbound connections will come from one of our static IP address. Any inbound traffic should still use the hostname assigned to each of your projects, you cannot use our IP address to route http traffic to your projects. You can view our IP address in the [Docs](https://flowfuse.com/docs/cloud/introduction/#ip-addresses) section of our website. If you’d like to stay up to date with our latest releases you can do so on [our blog](https://flowfuse.com/blog). # Community News September 2022 Welcome to the FlowFuse newsletter, a regular roundup of what’s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [**FlowFuse 0.10**](https://flowfuse.com/blog/2022/09/flowforge-010-released/) Version 0.10 was released on 30th September. Our latest release includes some great new features, quality of life improvements and bug fixes. Notable additions included the ability to [Secure your HTTP endpoints](https://github.com/FlowFuse/flowfuse/pull/893){rel=""nofollow""}, [Add read-only users to your projects](https://github.com/FlowFuse/flowfuse/issues/657){rel=""nofollow""} and [use our static IP address for outbound connections](https://flowfuse.com/docs/cloud/introduction/#ip-addresses) If you’d like to learn more about what else was included in 0.10 you can do so on our [blog post](https://flowfuse.com/blog/2022/09/flowforge-010-released/), on our [GitHub release page](https://github.com/FlowFuse/flowfuse/releases/tag/v0.10.0){rel=""nofollow""} and on our [Youtube channel](https://youtube.com/watch?v=mjR1iiEFiBg){rel=""nofollow""}. We’d also love for more of you to get involved in the development of FlowFuse, [contributions to the code](https://github.com/FlowFuse/flowfuse/blob/main/CONTRIBUTING.md){rel=""nofollow""} and [bug reports](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""} are really appreciated. [**Node-RED Con 2022**](https://nrcon.nodered.org/){rel=""nofollow""}:br We are happy to again be involved in Node-RED con. The event is being held online on 7th October, with content for both English and Japanese speakers including our colleagues Nick O'Leary and Sam Machin. You can find out more on the [Node-RED Con website](https://nrcon.nodered.org/){rel=""nofollow""}. [**FlowFuse Team News**](https://flowforge.com/team/){rel=""nofollow""}:br We are currently recruiting [NodeJS Developers](https://boards.greenhouse.io/flowfuse/jobs/4463977004){rel=""nofollow""}, if you’re interested in joining our team please [apply here](https://boards.greenhouse.io/flowfuse/jobs/4463977004#app){rel=""nofollow""}. We are also looking for a [PeopleOps Manager](https://boards.greenhouse.io/flowfuse/jobs/4687876004){rel=""nofollow""} to help us grow our team. You can [apply here](https://boards.greenhouse.io/flowfuse/jobs/4687876004#app){rel=""nofollow""} for that position. [**Forest Fire Alerts Using ML, IOT and Node-RED**](https://hackster.io/user102774/fight-fire-wild-fire-prediction-using-tinyml-df7572){rel=""nofollow""}:br This fascinating project came up a few days ago and we wanted to share it with you all. The concept is to use a mesh network of IoT devices to monitor various indicators of potential and current wildfires and report data back to the relevant authorities. The system will use ML to predict wildfire risk levels and hopefully send warnings before a fire actually starts. The two developers [Muhammed](https://linkedin.com/in/zainmfd/){rel=""nofollow""} and [Salman](https://linkedin.com/in/salmanfarisvp/){rel=""nofollow""} are planning to use Node-RED to manage the reporting of fires to the authorities. [**Try FlowFuse for Free**](https://app.flowfuse.com/account/create){rel=""nofollow""}:br As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on FlowFuse when you create a new team. To get this discount please follow [this link](https://app.flowfuse.com/account/create?code=RELEASE010){rel=""nofollow""} or use the code RELEASE010 when on the payment page after creating a new team. # Scheduled maintenance: Database encryption October 2022 As part of an on-going security [review](https://flowfuse.com/platform/security/#data-at-rest) of the FlowFuse Cloud offering we discovered that the backend database was not using encrypted storage. In keeping with industry best practices we plan to migrate the database to a new instance using encrypted at rest storage. ## Impact Customers' Node-RED instances will remain running, though any features that depend on FlowFuse will not operate as expected during the migration. This includes user sessions, project and team management, as well as the project nodes for inter-project communication. Self-hosted installations are unaffected by this change. ## When The migration will be on 26 October 2022 at 22:00 UTC and is expected to take under 2 hours. The platform will be available as soon as the migration is complete. We will post updates during the migration period to our Twitter account [@FlowFuseInc](https://twitter.com/flowforgeinc){rel=""nofollow""}. # Install FlowFuse Docker on Google Cloud As part of our preparations for FlowFuse 1.0 we have been testing various real world scenarios to see where we can add to our documentation and where we might be able to improve our releases to make the install process easier for users. As a benefit of that testing we have been able to hone these installation processes and we wanted to share one of those with you today. In this first of three articles, we are going to run through the process for installing FlowFuse on Google Cloud Platform (GCP) within a virtual machine (VM) using Docker. We have set ourselves the goal of delivering a production environment. We want this installation benefit from: - Email alerts (emails to users when they are added to teams etc) - HTTPS access to the install - FlowFuse [Device](https://flowfuse.com/docs/user/concepts/#device) deployment via the included MQTT server that comes in our Docker build We will follow up with a second article covering the process of getting HTTPS running then we will close out the series by covering how to use key features of FlowFuse including [Devices](https://flowfuse.com/docs/user/concepts/#device). # Prerequisites - A domain name - We've registered flowforge-demo.com to demonstrate these steps - A DNS provider - Our Domain registrar provides a basic DNS service for free - A GCP account - Google will often give you free service credits on sign up so setting up FlowFuse on GCP should not cost you anything for at least a few weeks - An email provider which will allow SMTP connections to send email - To manage users on your FlowFuse platform you will need to be able to send emails to them. We have used a Google Workspace account for this purpose # GCP VM Creation Create a GCP account, once logged in navigate to Compute Engine then VM Instances. Select Create Instance you should now be [here](https://console.cloud.google.com/compute/instancesAdd?project){rel=""nofollow""}. Give your instance a name, select a Region and Zone. I have found that the default machine configuration works fine but depending on your project you may wish to change the resources. !["Screenshot showing the interface for creating GCP VM"](https://flowfuse.com/blog/2022/10/images/1.png "Screenshot showing the interface for creating GCP VM") You now need to allow access to your FlowFuse installation from the internet. In the Firewall section tick Allow HTTP traffic and Allow HTTPS traffic. !["Screenshot showing the firewall section in the interface for creating a GCP VM"](https://flowfuse.com/blog/2022/10/images/2.png "Screenshot showing the firewall section in the interface for creating a GCP VM") Next up, assign a static IP address to the VM. Click Advanced options, then Networking. Now scroll down until you see Network interfaces and click on default to expand that section. In External IPv4 address select Create IP Address, give it a name than press Reserve. !["Screenshot showing the network section in the interface for creating a GCP VM"](https://flowfuse.com/blog/2022/10/images/3.png "Screenshot showing the network section in the interface for creating a GCP VM") Once you have reserved your IP it will be shown in the External IPv4 address field, write it down as we will need it later to create the DNS records. Our IP address was 34.125.156.130. !["Screenshot showing your reserved IP in the External IPv4 address field"](https://flowfuse.com/blog/2022/10/images/4.png "Screenshot showing your reserved IP in the External IPv4 address field") You are now ready to create and boot your VM, scroll to the bottom of the page and press Create. It can take a minute or two for the VM to be ready to use. # DNS Set Up So that you can run FlowFuse on your newly created GCP VM you will need to set up 2 DNS records. These records are slightly different to what is suggested in the FlowFuse install docs. We were keen to be able to run other services on this domain so we set up the following records. ![Screenshot showing interface for setting DNS](https://flowfuse.com/blog/2022/10/images/5.png "Screenshot showing interface for setting DNS") DNS changes need to propagate, and depending on your DNS provider, ISP, and other factors, this can take anywhere between a few seconds to 4 hours. Our’s were in place very quickly. To validate the DNS records you can use `dig` on either a Mac or Linux. ![Screenshot showing output of the dig command](https://flowfuse.com/blog/2022/10/images/6.png "Screenshot showing output of the dig command") The DNS records are set to the IP record we noted down earlier, so we're good to continue. # FlowFuse Docker Installation The next step is to install Docker on our GCP VM. If you return to GCP you should see that your VM is now up and running, you can now click on SSH to connect to your VM. This will open up a browser based SSH session to your VM. ![Screenshot showing access to SSH in GCP](https://flowfuse.com/blog/2022/10/images/7.png "Screenshot showing access to SSH in GCP") Once you have a Secure Shell (SSH) session open, the first step is to install Docker using the following commands. `sudo apt-get update` ```text sudo apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release ``` `sudo mkdir -p /etc/apt/keyrings` `curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg` ```text echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ``` `sudo apt-get update` `sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin` You can read a lot more detail about what each these commands actually do [here](https://docs.docker.com/engine/install/debian/){rel=""nofollow""}. # Download FlowFuse’s latest Docker build The next step is to get the codebase for FlowFuse onto your VM, to do so you will need to run the following commands. Please note that we are working with our 0.10.0 build, you will need to update the version number in the commands below if you are working with a newer build. Use curl to download the files we need. `sudo curl -L https://github.com/FlowFuse/docker-compose/archive/refs/tags/v0.10.1.tar.gz -o v0.10.1.tar.gz` Make the directory where we will store FlowFuse. `sudo mkdir /opt/flowforge` Uncompress FlowFuse and save it to the directory. `sudo tar zxf v0.10.1.tar.gz --directory /opt/flowforge` You should now have all the code you need for FlowFuse in the directory `/opt/flowforge/docker-compose-0.10.1`, it should look something like this. !["Screenshot showing directory listing for FlowFuse"](https://flowfuse.com/blog/2022/10/images/8.png "Screenshot showing directory listing for FlowFuse") # Configure FlowFuse We can now configure FlowFuse on your VM. We are going to need to edit two files. Firstly we need to switch into the directory where we just placed FlowFuse. `cd /opt/flowforge/docker-compose-0.10.1` Then we need to edit the flowforge.yml file, we're using Nano to do that. `sudo nano /opt/flowforge/docker-compose-0.10.1/etc/flowforge.yml` At the top of the file you need to update the domain and base\_url to match your domain !["Screenshot showing domain configuration in flowforge.yml"](https://flowfuse.com/blog/2022/10/images/9.png "Screenshot showing domain configuration in flowforge.yml") Next we will need to edit the Email Configuration section to match your SMTP provider. Set enabled to true then add in the details provider by your email provider. For example in this case I am using our Google Workspace account. !["Screenshot showing email configuration in flowforge.yml"](https://flowfuse.com/blog/2022/10/images/10.png "Screenshot showing email configuration in flowforge.yml") Finally, you need to update the `public_url` for your mqtt broker to match your DNS record. !["Screenshot showing MQTT configuration in flowforge.yml"](https://flowfuse.com/blog/2022/10/images/11.png "Screenshot showing MQTT configuration in flowforge.yml") You can now save and close that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key. Now we need to edit the `docker-compose.yml` file. We will use Nano again to do that. `sudo nano /opt/flowforge/docker-compose-0.10.1/docker-compose.yml` We need to edit the file to add in to the domain as follows. !["Screenshot showing virtual hosts configuration in docker-compose.yml"](https://flowfuse.com/blog/2022/10/images/12.png "Screenshot showing virtual hosts configuration in docker-compose.yml") Save and exit from that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key. # Start FlowFuse We are now ready to start up FlowFuse for the first time, to do so we will use the following command. `sudo docker compose -p flowforge up -d` The build process will take a few minutes, once it’s completed let’s make sure all the docker containers are running. `sudo docker ps` ![Docker PS output](https://flowfuse.com/blog/2022/10/images/13.png) You should see 4 running Docker containers. If everything went well you should now be able to access your FlowFuse server via the DNS record you created. ![FF Login page](https://flowfuse.com/blog/2022/10/images/14.png) Nice, you now have a working instance of FlowFuse running on GCP but remember that all traffic is currently running on HTTP so we still have some work to do. In the next article we will cover how to add HTTPS support to this FlowFuse installation. # FlowFuse 1.0 released Predefined environment variables for your Instances and Devices, manage your Project's modules and import your existing flows (and credentials) into your FlowFuse [Projects](https://flowfuse.com/docs/user/concepts/#instance). We're pleased to announce version 1.0 FlowFuse is now available. Keep reading for a promotion code to get your first month free on FlowFuse. Version 1.0 represents our vision of the base set of features needed for you to get great value from using FlowFuse in a production environment. That's not to say we are done, we will continue to add features, improve our interfaces and fix bugs with the same enthusiasm as we've worked towards 1.0. We'd like to hear your feedback on what we will be including in [1.1 and beyond](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""}. ## Features [Standard Environment Variables set for both Projects and Devices](https://github.com/FlowFuse/flowfuse/issues/841){rel=""nofollow""} Projects now get a set of predefined environment variables that can be used by their flows. These give your flows access to the projects unique id and name. When the flows are deployed to [devices](https://flowfuse.com/docs/user/concepts/#device), they also get the device's id and name. That makes is easier to deploy flows across multiple devices and have each able to identify itself. [Add additional node modules to your projects](https://github.com/FlowFuse/flowfuse/issues/405){rel=""nofollow""} This feature allows you to pre-define additional Node-RED nodes and node modules you may want to be installed in your FlowFuse project, making it easier to manage. [Import existing projects into FlowFuse](https://github.com/FlowFuse/flowfuse/issues/835){rel=""nofollow""} You can now import your existing flow and credentials files straight into your FlowFuse project - making it really easy to move your existing projects into the platform. ## Improvements We've made a number of improvements to the overall experience of running FlowFuse. - Editable Stack labels [#915](https://github.com/FlowFuse/flowfuse/issues/915){rel=""nofollow""} - Check for suitable version of Node on Devices [#37](https://github.com/FlowFuse/device-agent/issues/37){rel=""nofollow""} - Realtime Project status details in Project overview [#990](https://github.com/FlowFuse/flowfuse/issues/990){rel=""nofollow""} - Improve Template creation & Edit Project Settings UX [#1041](https://github.com/FlowFuse/flowfuse/issues/1041){rel=""nofollow""} ## Bug Fixes We've fixed the following bugs in this release. - [Pressing return in search box reloads page](https://github.com/FlowFuse/flowfuse/issues/1143){rel=""nofollow""} - [Vue Router Warn](https://github.com/FlowFuse/flowfuse/issues/1126){rel=""nofollow""} - [Kebab menu in Settings breaks](https://github.com/FlowFuse/forge-ui-components/issues/58){rel=""nofollow""} - [flowforge-nr-launcher missing try/catch on http request](https://github.com/FlowFuse/flowfuse/issues/1096){rel=""nofollow""} - [Invite with + in email address is incorrectly sanitised](https://github.com/FlowFuse/flowfuse/issues/1145){rel=""nofollow""} - [Table does not sort correctly when empty fields are present](https://github.com/FlowFuse/forge-ui-components/issues/59){rel=""nofollow""} - [4xx Errors not shown in App](https://github.com/FlowFuse/flowfuse/issues/929){rel=""nofollow""} - [Inconsistent errors returned from the API](https://github.com/FlowFuse/flowfuse/issues/1076){rel=""nofollow""} - [Module install not working on windows](https://github.com/FlowFuse/flowforge-nr-launcher/issues/77){rel=""nofollow""} - [Avatar lettering is mis-allinged when only rendering 1 character](https://github.com/FlowFuse/flowfuse/issues/1038){rel=""nofollow""} - [it.only is not prohibited](https://github.com/FlowFuse/flowfuse/issues/968){rel=""nofollow""} - [No feedback when an API error occurs editing user](https://github.com/FlowFuse/flowfuse/issues/966){rel=""nofollow""} - [Start action is available on a running project](https://github.com/FlowFuse/flowfuse/issues/1040){rel=""nofollow""} ## Contributors We'd like the thank the following for their contributions to this release: [Jozefik](https://github.com/Jozefik){rel=""nofollow""} for their work on [Adding limits to admin panel](https://github.com/FlowFuse/flowfuse/pull/1082){rel=""nofollow""}. As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ### Try it out [Sign up for FlowFuse-Managed Premium](https://app.flowfuse.com/account/create?code=RELEASE1){rel=""nofollow""} with this link or at the checkout enter the code **RELEASE1** to get your first project free for a month. As an open source project you can also use [FlowFuse-Community](https://flowfuse.com/docs/install/) for free, forever. ### Upgrading FlowFuse Our managed \[FlowFuse]\({{ site.appURL }}) is already running 1.0. Upgrade your project Stacks to the latest version to make sure you get all the latest changes. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading FlowFuse on a local server](https://flowfuse.com/docs/upgrade/#upgrading-flowfuse). ### Getting help If you hit any problems with the platform, or have questions to ask, please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Customers of FlowFuse Cloud can raise a ticket by emailing We also have a `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. # FlowFuse raises $7.25M Seed Round to bring Node-RED to the Enterprise Since the [launch of FlowFuse](https://flowfuse.com/blog/2021/04/first-deploy/) in April 2021 our goal has been to build a low-code, enterprise-ready development platform based on Node-RED. We wanted to make it easier for enterprises to adopt, integrate, and scale Node-RED into their existing environments. We published our [first version](https://flowfuse.com/blog/2022/01/flowforge-01-released/) at the start of this year and have continued the journey to the [launch of FlowFuse v1.0](https://flowfuse.com/blog/2022/10/flowforge-1-released/) just last week. Each of these releases has moved us towards our goal of creating a platform for integrating the IT and OT for all organizations, built around an open core. ### FlowFuse is Node-RED for the enterprise Node-RED is an incredible tool that has allowed many organizations to quickly and easily integrate both their IT and OT. The sheer range and variety of solutions created with Node-RED today go far beyond what we imagined. But we’ve also seen the challenges faced by companies wanting to take their Node-RED solutions into production and beyond. This is where FlowFuse comes in. It addresses those challenges by adding security, collaboration and deployment capabilities. ### Our $7.25M Seed Round Today, we’re super excited to announce a $7.25M seed round led by Cota Capital, joined by Westwave Capital, Uncorrelated Ventures, and Open Core Ventures. This brings a huge amount of extensive knowledge and experience in IoT, open source, enterprise-ready software solutions. This investment will enable us to continue growing the team and platform to realize our vision of FlowFuse being the best way to achieve enterprise-ready Node-RED. It will enable us to invest back into the core Node-RED project to further its development. One of our core company principles is around Open Source Stewardship - the success of FlowFuse relies on a strong and successful Node-RED community. We’ll be bringing more collaboration features to the FlowFuse platform to enable true collaborative, team-based working. We’ll expand our [Devices offering](https://flowfuse.com/docs/user/concepts/#device) to make it far easier to run Node-RED wherever it suits your use-case, from data center to remote edge locations. Above all, we’ll be building a company that is open, sustainable, and fun to work at. We already have some job openings available - check out our \[jobs board]\({{ site.jobBoard }}) if you’re interested in joining our team. If you’re interested in learning more about what we’re doing, or have any questions, please do [get in touch](https://flowfuse.com/contact-us/)! # Community News October 2022 Welcome to the FlowFuse newsletter for October 2022, a monthly roundup of what’s happening with both FlowFuse and the wider Node-RED community. If you've got something that you'd like us to share please email . [**FlowFuse 1.0 Released**](https://flowfuse.com/blog/2022/10/flowforge-1-released/):br Version 1.0 was released on 27th October. Our latest release represents our vision of the base set of features needed for you to get great value from using FlowFuse in a production environment. That's not to say we are done, we will continue to add features, improve our interfaces and fix bugs with the same enthusiasm as we've worked towards 1.0. We'd like to hear your feedback on what we will be including in [1.1 and beyond](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""}. If you’d like to learn more about what else was included in 1.0 you can do so on our [blog post](https://flowfuse.com/blog/2022/10/flowforge-1-released), on our [GitHub release page](https://github.com/FlowFuse/flowfuse/releases/tag/v1.0.0){rel=""nofollow""} and on our [Youtube channel](https://www.youtube.com/watch?v=5TLT7CQR7iI){rel=""nofollow""}. We’d also love for more of you to get involved in the development of FlowFuse, [contributions to the code](https://github.com/FlowFuse/flowfuse/blob/main/CONTRIBUTING.md){rel=""nofollow""} and [bug reports](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""} are really appreciated. [**Node-RED Nears 3.1 Release**](https://github.com/node-red/node-red/milestone/19){rel=""nofollow""} The next release of Node-RED has some great new features including support for [locking flows in the editor](https://github.com/node-red/node-red/pull/3938){rel=""nofollow""} and [improving the user experience around hiding flows](https://github.com/node-red/node-red/pull/3930){rel=""nofollow""}. We'd expect the first 3.1 beta to be available in November with a full release following shortly afterwards. \[\*\*FlowFuse raises [[]{.katex-mathml}[[[]{.strut style="height:0.8889em;vertical-align:-0.1944em;"}[7.25]{.mord}[M]{.mord.mathnormal style="margin-right:0.109em;"}[t]{.mord.mathnormal}[o]{.mord.mathnormal}[b]{.mord.mathnormal}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[in]{.mord.mathnormal}[g]{.mord.mathnormal style="margin-right:0.0359em;"}[N]{.mord.mathnormal style="margin-right:0.109em;"}[o]{.mord.mathnormal}[d]{.mord.mathnormal}[e]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:0.8889em;vertical-align:-0.1944em;"}[R]{.mord.mathnormal style="margin-right:0.0077em;"}[E]{.mord.mathnormal style="margin-right:0.0576em;"}[D]{.mord.mathnormal style="margin-right:0.0278em;"}[t]{.mord.mathnormal}[o]{.mord.mathnormal}[t]{.mord.mathnormal}[h]{.mord.mathnormal}[e]{.mord.mathnormal}[E]{.mord.mathnormal style="margin-right:0.0576em;"}[n]{.mord.mathnormal}[t]{.mord.mathnormal}[er]{.mord.mathnormal style="margin-right:0.0278em;"}[p]{.mord.mathnormal}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[i]{.mord.mathnormal}[se]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[∗]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:1em;vertical-align:-0.25em;"}[∗]{.mord}[]]{.mclose}[(]{.mopen}[/]{.mord}[b]{.mord.mathnormal}[l]{.mord.mathnormal style="margin-right:0.0197em;"}[o]{.mord.mathnormal}[g]{.mord.mathnormal style="margin-right:0.0359em;"}[/2022/10/]{.mord}[see]{.mord.mathnormal}[d]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:0.7778em;vertical-align:-0.0833em;"}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[o]{.mord.mathnormal}[u]{.mord.mathnormal}[n]{.mord.mathnormal}[d]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:0.8889em;vertical-align:-0.1944em;"}[b]{.mord.mathnormal}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[in]{.mord.mathnormal}[g]{.mord.mathnormal style="margin-right:0.0359em;"}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:0.7778em;vertical-align:-0.0833em;"}[n]{.mord.mathnormal}[o]{.mord.mathnormal}[d]{.mord.mathnormal}[e]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:0.7778em;vertical-align:-0.0833em;"}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[e]{.mord.mathnormal}[d]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:0.6984em;vertical-align:-0.0833em;"}[t]{.mord.mathnormal}[o]{.mord.mathnormal}[]{.mspace style="margin-right:0.2222em;"}[−]{.mbin}[]{.mspace style="margin-right:0.2222em;"}]{.base}[[]{.strut style="height:1em;vertical-align:-0.25em;"}[e]{.mord.mathnormal}[n]{.mord.mathnormal}[t]{.mord.mathnormal}[er]{.mord.mathnormal style="margin-right:0.0278em;"}[p]{.mord.mathnormal}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[i]{.mord.mathnormal}[se]{.mord.mathnormal}[/]{.mord}[)]{.mclose}[E]{.mord.mathnormal style="margin-right:0.0576em;"}[a]{.mord.mathnormal}[r]{.mord.mathnormal style="margin-right:0.0278em;"}[l]{.mord.mathnormal style="margin-right:0.0197em;"}[i]{.mord.mathnormal}[er]{.mord.mathnormal style="margin-right:0.0278em;"}[t]{.mord.mathnormal}[hi]{.mord.mathnormal}[s]{.mord.mathnormal}[w]{.mord.mathnormal style="margin-right:0.0269em;"}[ee]{.mord.mathnormal}[k]{.mord.mathnormal style="margin-right:0.0315em;"}[,]{.mpunct}[]{.mspace style="margin-right:0.1667em;"}[w]{.mord.mathnormal style="margin-right:0.0269em;"}[e]{.mord.mathnormal}[ann]{.mord.mathnormal}[o]{.mord.mathnormal}[u]{.mord.mathnormal}[n]{.mord.mathnormal}[ce]{.mord.mathnormal}[d]{.mord.mathnormal}[a]{.mord.mathnormal}]{.base}]{.katex-html ariaHidden="true"}]{.katex} 7.25M seed round led by [Cota Capital](https://www.cotacapital.com/knowledgecapital/flowforge-closes-the-gap-between-it-and-ot){rel=""nofollow""}, joined by Westwave Capital, Uncorrelated Ventures, and Open Core Ventures. This brings a huge amount of extensive knowledge and experience in IoT, open source, enterprise-ready software solutions. You can read more about what this investment means for FlowFuse in this [TechCrunch article](https://techcrunch.com/2022/11/03/flowforge-nabs-7-2m-to-help-companies-integrate-iot-using-node-red){rel=""nofollow""}. [**Node-RED Dashboard - Beginners Guide**](https://stevesnoderedguide.com/node-red-dashboard){rel=""nofollow""}:br It's great to see members of the Node-RED community taking their personal time to help us all build better projects. This write up takes you through the basics of creating your first Dashboard through to more advanced techniques to help your interfaces look professional and provide great user experiences. [**FlowFuse Team News**](https://flowfuse.com/about/):br We are currently recruiting [NodeJS Developers](https://boards.greenhouse.io/flowfuse/jobs/4463977004){rel=""nofollow""}, a [Product Manager](https://boards.greenhouse.io/flowfuse/jobs/4717778004){rel=""nofollow""}, a [Recruiter / PeopleOps Manager](https://boards.greenhouse.io/flowfuse/jobs/4687876004){rel=""nofollow""} and a [Senior Community Manager](https://boards.greenhouse.io/flowfuse/jobs/4700809004){rel=""nofollow""}. You can view any of the roles we currently have open and apply on our [Jobs page](https://boards.greenhouse.io/flowfuse){rel=""nofollow""}. We'd also like to welcome [Pez Cuckow](https://github.com/Pezmc){rel=""nofollow""} who joined FlowFuse as a Senior Software Engineer in October. [**Try FlowFuse for Free**](https://app.flowfuse.com/account/create?code=RELEASE1){rel=""nofollow""}:br As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on our managed FlowFuse platform when you create a new team. To get this discount please follow [this link](https://app.flowfuse.com/account/create?code=RELEASE010){rel=""nofollow""} or use the code RELEASE1 when on the payment page after creating a new team. As an open source project you can also use [FlowFuse](https://flowfuse.com/docs/install/) for free, forever. # FlowFuse 1.1 released with persistent file storage Persist files on your FlowFuse Projects, publish locally developed flows to dozens of Devices in a few clicks, and use our new interface for managing Project Deployments. We're pleased to announce version 1.1 is now available! The latest release of the FlowFuse application contains new features, many improvements, and bug fixes. Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above. ## Features [Persistent File Storage](https://github.com/FlowFuse/flowfuse/issues/998){rel=""nofollow""} We've had a great deal of feedback from our customers that being able to persist files in a project is a vital feature in Node-RED. In FlowFuse 1.1 flows can now create and persist files within your Projects. We know those files are used in many creative ways and we're looking forward to seeing how users improve their Projects using this new feature. [Import Snapshots from Outside FlowFuse](https://flowfuse.com/docs/migration/node-red-tools/) Developers may wish to work on Node-RED in a local environment but want an easy path to share that with their team. You can now link your Node-RED instances running outside of FlowFuse and push Snapshots directly into your FlowFuse Projects to leverage FlowFuse fully. With this new feature we've made it effortless to push a local build of a project to FlowFuse for deployment to your staging and production FlowFuse instances. ## Improvements [Project Deployments UX](https://github.com/FlowFuse/flowfuse/issues/1046){rel=""nofollow""} We've reworked the interface for managing your FlowFuse Deployments of Node-RED. We are seeing FlowFuse users deploying their Projects to edge devices at scale. This is another step towards making it easier for users to manage a large quantity of devices in their Projects. When users change their username, email address, or password they'll now be notified through email of changes to ensure they were made by the user. In this release a lot of effort went into the install process, specifically the local install method. First and foremost; a default Stack and Template will be installed automatically. That will ensure users get up and running with Node-RED more quickly. Administrators of each platform can still change Stacks and Templates when needed. Secondly, the installer now auto generates the configuration file for Mosquitto, the MQTT broker FlowFuse uses. This again should save administrators time when installing FlowFuse. ## Bug Fixes The v1.0.1 release included a bug fix where [snapshot rollbacks](https://github.com/FlowFuse/flowfuse/issues/1186){rel=""nofollow""} didn't work, which has also been included in v1.1 onwards. We've fixed the following bugs in this release. - When installing the stack during a FlowFuse installation the process would quit on Windows [#62](https://github.com/FlowFuse/installer/issues/62){rel=""nofollow""} - After accepting an invite to join a team, users are no longer seeing a blank page [#1208](https://github.com/FlowFuse/flowfuse/issues/1208){rel=""nofollow""} - Pagination on device deployments wasn't showing all devices [#1207](https://github.com/FlowFuse/flowfuse/issues/1207){rel=""nofollow""} - Markdown rendering when selecting the project type wasn't quite working, fixed now! [#1171](https://github.com/FlowFuse/flowfuse/issues/1171){rel=""nofollow""} - Continuous spinner in UI body when entering a new (short) password [#1280](https://github.com/FlowFuse/flowfuse/issues/1280){rel=""nofollow""} - Friendly stack name not shown in the Change Project Stack option list [#1169](https://github.com/FlowFuse/flowfuse/issues/1169){rel=""nofollow""} - The stacks view in the admin area didn't render properly [#1260](https://github.com/FlowFuse/flowfuse/issues/1260){rel=""nofollow""} - Like the deployments page, pagination for stacks was broken. [#1164](https://github.com/FlowFuse/flowfuse/issues/1164){rel=""nofollow""} - Several UX and UI bugs got polished away! ## Contributors We'd like the thank the following for their contributions to this release: [mikermcneil](https://github.com/mikermcneil){rel=""nofollow""} for their work on [#1301](https://github.com/FlowFuse/flowfuse/pull/1301){rel=""nofollow""} As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ### Try it out As said before, a lot of effort went into the local installer. We're confident you can have your own FlowFuse running locally in about 30 minutes. [Get started right away!](https://flowfuse.com/docs/contribute/local/) ([Docker](https://flowfuse.com/docs/install/docker/) and [Kubernetes](https://flowfuse.com/docs/install/kubernetes/) are available too!) If you'd rather use our hosted offering: [Sign up for FlowFuse Cloud](https://app.flowfuse.com/account/create?code=RELEASE11){rel=""nofollow""} with the coupon **RELEASE11** to get your first project free for a month. ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.1. To use persisted files you'll need to upgrade your projects stack. You'll be prompted to do so on the project page. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/) ### Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. You can also raise a support ticket by emailing # Re-spin of Docker-Compose install package After [yesterdays 1.1.0 FlowFuse release](https://flowfuse.com/blog/2022/11/flowforge-1-1-released/) we noticed a few minor issues with the docker-compose install instructions. We had relied on the fact we are now publishing container images to Docker Hub to install the new `flowforge/file-server` container. At this time we are only building images for amd64 and arm64. Further, it was only tagged them with the current release number. To remedy this we tagged v1.1.1 of the docker-compose project in which we have included the required `Dockerfile` and resources to build the `flowforge/file-server` locally, updated the `build-containers.sh` script to build this container. We will also build the published containers for armv7 and include a latest tag going forward. # Challenges scaling Node-RED with DIY tooling In this post, I'm going to share some of the challenges customers face when scaling Node-RED with Do-It-Yourself tooling. Specifically, we'll talk about common threads in their journey building their own tooling around Node-RED, its flows, and deploying them. Node-RED is a visual programming environment for wiring together hardware devices, APIs and online services in a single application. It's great because it's flexible enough to be used by both beginners and experts alike; however, going from one instance of Node-RED to 100 isn't for the faint-hearted. ### Zero to hero in a few days If you’re new to Node-RED and are just getting started, the first step is simple: go to [nodered.org](https://nodered.org){rel=""nofollow""}, download, install and run. There’s no need to configure anything or set up credentials or security or alerts. You can get started with Node-RED straight away by simply running it on your machine. The guides and scripts provided on Node-RED or are more than enough to get started. You'll dive right in and start developing your flows. ### Onto the second instance The next instance is a simple copy of the first. You'll need to make sure that you are installing the same version on both instances, and that any configuration files (such as the `settings.js`) are also in sync. The second instance improves the first equally. Docs are read again, improvements are made, and copied over. Life's good! Although there's a slight itch to start automating the setup, it's ignored. Just too many open questions on how to achieve it: write scripts in bash? Or can we use Node-RED to manage Node-RED? Automation can wait, there's new flows to implement! ### Wake-up call; how many of them do we have? There's nothing like a good ol' wake-up call to get you back on track. One of our team members asked a questions about a buggy flow you've got no recollection of. During the investigation there's issues left and right; it's running a much older version, missing standard packages, the settings are out of date, and the timezone not set to UTC? Adoption of Node-RED is going quite well, it's useful and effective. Gets the job done without fuss. But now there's toil in maintaining them; ensure the right tooling is build, properly documented, it needs dashboards and overviews, lots of work to be done. Not quite business related, but Node-RED is important for the company, the bosses sure would approve spending 2 months building these tools! But then something happened, there was a higher priority project to be picked up. Some tooling could be written for a couple of hours a month, but not two dedicated months to get it in tip top shape. Better than nothing! Built a dashboard in Node-RED to keep track of all devices, there's some scripts and a few flows that aid in monitoring and maintenance. Scaling further is possible, but confidence in the tooling isn't sky high. ### Data extraction at scale; now there's over 100 At [FlowFuse](https://flowfuse.com) we've got regular conversations with customers managing 100s of devices. Scaling to that many devices and runtimes requires hours of development each week alongside monitoring, maintenance, and auditing. As a side effect of investing more time into Node-RED and its ecosystem the organization has developed a few standards. Standard custom nodes that are pre-installed (👋 `moment.js`), a style guide published for developing flows, maybe even flow linting: {rel=""nofollow""} The security model is fairly decent. One just hopes the [CISO](https://nl.wikipedia.org/wiki/Chief_Information_Security_Officer){rel=""nofollow""} doesn't inspects them, but it's a fair bet they won't; we're far away from the headquarters, right? Many other edge cases itch in the back of our heads, but we can't focus on those right now. ### Conclusion Node-RED is a great tool, it's got many built-in features that make it easy to get started with no coding experience necessary. Running it at scale, in a production environment can require a lot of sys-ops and dev-ops time and we think [FlowFuse](https://flowfuse.com) is a great solution to keep that admin and tech debt in check. If you're running into these challenges we believe we can help, you can adopt our [free and open source edition](https://flowfuse.com/docs/install/). Additionally, to get a head start or enhance your current setup, explore our [Beginner's Guide to Professional Node-RED](https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/). This comprehensive ebook offers a clear overview of Node-RED’s capabilities and practical tips for making the most of its features. # Community News November 2022 Welcome to the FlowFuse newsletter for November 2022, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. If you've got something that you think we should share on our newsletters please [get it touch](mailto\:contact@flowfuse.com). [**Node-RED Nears 3.1 Release**](https://github.com/node-red/node-red/milestone/19){rel=""nofollow""} As we mentioned last month, the release of Node-RED 3.1 is expected very soon. 3.1 includes lots of great new features such as support for [locking flows in the editor](https://github.com/node-red/node-red/pull/3938){rel=""nofollow""} and [improving the user experience around hiding flows](https://github.com/node-red/node-red/pull/3930){rel=""nofollow""}. As an open source project the development of Node-RED is entirely dependent on individuals and companies giving their time to work towards each new release. If you'd like to know how you can get involved you can read more on the [Node-RED web site](https://nodered.org/about/contribute/){rel=""nofollow""}. [**FlowFuse 1.1 Released**](https://flowfuse.com/blog/2022/11/flowforge-1-1-released/):br Version 1.1 of FlowFuse was released on 24th November. Our latest release included some great new features such as [Persistent file storage](https://github.com/FlowFuse/flowfuse/issues/998){rel=""nofollow""}, the ability to [import Node-RED snapshots](https://flowfuse.com/docs/migration/node-red-tools/) from outside of FlowFuse and a much improved interface to [deploy projects to your devices](https://github.com/FlowFuse/flowfuse/issues/1046){rel=""nofollow""}. We're now working towards our final release of 2022 which is due just before Christmas. You can see what we are planning to deliver in that release and beyond on [FlowFuse's project board](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""}. If you’d like to learn more about what else was included in 1.1 you can do so on our [blog post](https://flowfuse.com/blog/2022/11/flowforge-1-1-released), [GitHub release page](https://github.com/FlowFuse/flowfuse/releases/tag/v1.1.0){rel=""nofollow""}, and [Youtube channel](https://www.youtube.com/watch?v=134iljE_urI){rel=""nofollow""}. [**Node-Redscape - 100% Free, Open-Source Escape Room Control Software**](https://github.com/playfultechnology/node-redscape){rel=""nofollow""}:br As we'll come to later in this newsletter, FlowFuse visited an Escape Room in Winchester as part of our team meet-up. Co-incidentally, a great Node-RED project came up a few days after our visit which we thought was worth sharing. In their own words [Node-Redscape](https://github.com/playfultechnology/node-redscape){rel=""nofollow""} 'provides a set of templates, flows, and examples that turn Node-RED into a complete Escape Room automation system'. Very topical for us and also seems like a great project. You can learn more about the project on [Youtube](https://www.youtube.com/watch?v=f9yYDxqK_2E){rel=""nofollow""} as well as on [Github](https://github.com/playfultechnology/node-redscape){rel=""nofollow""}. **FlowFuse team meetup** FlowFuse is a fully remote team, we currently have a strong skew towards western Europe but we are in the process of adding team members in each of the continents. On that point, any great Product Managers who live in Antarctica are encouraged to [apply for a job with us](https://boards.greenhouse.io/flowfuse/jobs/4717778004){rel=""nofollow""}! Remote work is great but it's also valuable to get everyone together in the real world from time to time. FlowFuse had such a meet up last month in Winchester, UK. We came from near and far and took the opportunity to have productive round-table discussions about some features we are working towards in 1.2 and beyond. We also dropped into [Clue Capers](https://cluecapers.co.uk/){rel=""nofollow""}, a great Escape Room in the center of Winchester who provided the photo below to mark the occasion. !["The FlowFuse team pictured during our visit to Clue Capers"](https://flowfuse.com/blog/2022/12/images/clue-capers.jpg "The FlowFuse team pictured during our visit to Clue Capers") [**FlowFuse Team News**](https://flowfuse.com/about/):br We are currently recruiting a [Product Manager](https://boards.greenhouse.io/flowfuse/jobs/4717778004){rel=""nofollow""}, and a [Senior Community Manager](https://boards.greenhouse.io/flowfuse/jobs/4700809004){rel=""nofollow""}. You can view any of the roles we currently have open and apply on our [Jobs page](https://boards.greenhouse.io/flowfuse){rel=""nofollow""}. [**Try FlowFuse for Free**](https://app.flowfuse.com/account/create?code=RELEASE11){rel=""nofollow""}:br As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on our managed FlowFuse platform when you create a new team. To get this discount please follow [this link](https://app.flowfuse.com/account/create?code=RELEASE11){rel=""nofollow""} or use the code RELEASE11 when on the payment page after creating a new team. As an open source project you can also use [FlowFuse](https://flowfuse.com/docs/install/) for free, forever. # Create HTTP triggers with authentication Having an HTTP endpoint trigger your flows is very useful. From any browser or command line you now have the ability to trigger your flows. Doing so safely with authentication is slightly harder, but not a lot. FlowFuse makes it rather easy to accomplish. ### Creating the HTTP flow When you start a project on FlowFuse, remember the project name. For this how-to we’ll use `example`. Open the editor and drag in the HTTP In node as well as the HTTP response node. Connect them, and add a debug node, which is connected to the “HTTP in” node. First off; let’s set the HTTP in node properties: ![Shows the UI to edit the node's properties](https://flowfuse.com/blog/2022/12/images/edit-http-node.png "Shows the UI to edit the node's properties") You can import this flow into your own project if you’d like: ```text [{"id":"4faa84d37a52bb28","type":"group","z":"3c6e2dc732ada815","name":"Allow HTTP Post request to trigger a flow","style":{"label":!0},"nodes":["1fa26e0ed3ddec1a","45a180052e1a2f43","09347881f4fa4057"],"x":34,"y":79,"w":472,"h":122},{"id":"1fa26e0ed3ddec1a","type":"http in","z":"3c6e2dc732ada815","g":"4faa84d37a52bb28","name":"HTTP Trigger","url":"/http-trigger","method":"post","upload":!1,"swaggerDoc":"","x":130,"y":120,"wires":[["45a180052e1a2f43","09347881f4fa4057"]]},{"id":"45a180052e1a2f43","type":"http response","z":"3c6e2dc732ada815","g":"4faa84d37a52bb28","name":"Empty HTTP response","statusCode":"200","headers":{},"x":360,"y":120,"wires":[]},{"id":"09347881f4fa4057","type":"debug","z":"3c6e2dc732ada815","g":"4faa84d37a52bb28","name":"Print HTTP Request","active":!0,"tosidebar":!0,"console":!1,"tostatus":!1,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":360,"y":160,"wires":[]}] ``` Now I’ve opened a terminal and executed: ```text curl -X POST https://example.flowforge.com/http-trigger ``` When there’s no output, that means it’s all good! There should be an empty message in the debug console in the Node-RED editor though ### Securing the HTTP trigger with a username and password The problem with our trigger is that anyone with internet access could trigger it. That’s not a great idea. So let’s secure this endpoint with HTTP Basic Authentication. There are various ways to include a secure endpoint in Node-RED, we’ve built authentication directly into FlowFuse to make it easier for all users. On the FlowFuse project, go to settings and then to ‘Editor’. Under the section HTTP Auth you can set a username and password. You should generate both by a random string generator, and store the credentials somewhere safe. Restart the project to have the runtime pick up the changes, and the endpoint is secured! Let’s validate the endpoint that worked a minute ago doesn’t anymore: ```text curl -X POST https://example.flowforge.cloud/http-trigger => Unauthorized ``` Let’s get it working again: (replace `` and `` with the details from the sticky note) ```text curl -X POST https://:@example.flowforge.cloud/http-trigger ``` That’s it! You now have a flow that’s protected by a username and password combination! # FlowFuse 1.1.2 released We've published a maintenance fix with an important fix for the Palette Manager in Node-RED. A [bug](https://github.com/FlowFuse/flowfuse/issues/1367){rel=""nofollow""} was reported this week where a user was unable to install additional nodes into their Node-RED project using the editor's palette manager. We tracked it down to an issue that was introduced in the 1.1 release, where the project template lets you list nodes that should be blocked from being installed. It was interpreting an empty list to mean *disallow everything*! Not quite the intended behaviour. Whilst tracking this down, we also spotted a bug ([#1379](https://github.com/FlowFuse/flowfuse/issues/1379){rel=""nofollow""}) around editing this same setting that made it tricky to work around without this release being published. These issues have now been fixed and FlowFuse 1.1.2 published. ## Bug Fixes In additional to the above issues, this release includes some further fixes around the docker and helm components: - Fix fileStore hostname by @flecoufle in [#59](https://github.com/FlowFuse/docker-compose/pull/59){rel=""nofollow""} - Fix healthcheck [#62](https://github.com/FlowFuse/docker-compose/pull/62){rel=""nofollow""} [#63](https://github.com/FlowFuse/docker-compose/pull/63){rel=""nofollow""} [#74](https://github.com/FlowFuse/helm/pull/74){rel=""nofollow""} ## Contributors We'd like to thank the following people for their contributions to this release: [flecoufle](https://github.com/flecoufle){rel=""nofollow""} for their work on [#59](https://github.com/FlowFuse/docker-compose/pull/59){rel=""nofollow""} As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ### Upgrading FlowFuse This release has already been rolled out to \[FlowFuse Cloud]\({{ site.appURL }}). If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/) ### Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform, please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. You can also raise a support ticket by emailing # FlowFuse 1.2 is now available with single sign on and persistent context storage Control access to FlowFuse using single sign-on and retain context values when restarting projects. We're pleased to announce version 1.2 is now available! The latest release of the FlowFuse application contains new features, improvements, better documentation, and bug fixes. We've put a great deal of work in this release to make it easier to run your own self-managed instance of FlowFuse. That includes significant improvements for FlowFuse in [Kubernetes](https://flowfuse.com/docs/install/kubernetes/) and [Docker](https://flowfuse.com/docs/install/docker/). Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above. ## Features [Single Sign-On](https://github.com/FlowFuse/flowfuse/issues/226){rel=""nofollow""} Single sign-on (SSO) is a method of authentication that allows a user to access multiple applications or systems with a single set of login credentials, improving security, productivity, and user experience, and reducing IT overhead. We've implemented SSO using the Security Assertion Markup Language [(SAML)](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language){rel=""nofollow""} framework. This allows users of FlowFuse Cloud, Premium and the open source edition to easily access their FlowFuse projects. [Persistent Context](https://github.com/FlowFuse/flowfuse/issues/212){rel=""nofollow""} Node-RED provides a way to store information that can be shared between different nodes and flow executions without using the messages that pass through a flow. This is called ‘context’. You can now select if context should be stored in memory or in persistent storage. Persistent storage allows the stored values to be recalled after a restart of your project. You can see a demonstration of this feature on our [Youtube channel](https://youtu.be/ma2vYrXmssc){rel=""nofollow""}. ## Improvements - In FlowFuse 1.1 we added logging of user actions. In 1.2 we’ve [improved the audit log interface](https://github.com/FlowFuse/flowfuse/issues/517){rel=""nofollow""} to help you read the recorded user actions. !["An image of the new audit log interface of FlowFuse"](https://flowfuse.com/blog/2022/12/images/audit-log.png "An image of the new audit log interface of FlowFuse") - Configuring DNS for FlowFuse has historically been challenging as for most FlowFuse installs you'll need two entries. One for the FlowFuse application, and one for the Node-RED projects. There's been updates to the documentation to make it much easier to set this up, and much faster. Please checkout the new [documentation](https://flowfuse.com/docs/install/dns-setup/). - We've updated our documentation to always link to the latest build (older builds are still available). - Previously customers were asked to build their own containers for the main FlowFuse applications, as well as the Node-RED ones. For the Node-RED containers this allows customers to pre-install packages in the container they intent to use. For FlowFuse Cloud these containers are build by FlowFuse. These containers are now published to the [Docker Hub](https://hub.docker.com/u/flowforge){rel=""nofollow""}. This makes it much easier to get up and running with your first containers. - We are now pushing our Docker builds to Docker Hub, this saves users from having to build the Docker images when installing or updating. These containers are used by default by `docker-compose`. - Setting up MQTT for inter-project communication and communication with devices has been simplified. Please read the improved the documentation around configuration of [MQTT](https://github.com/FlowFuse/flowfuse/issues/1397){rel=""nofollow""}. ## Bug Fixes We've fixed the following bugs in this release. - Unable to edit 'Prevent Install of External nodes' template option [#1376](https://github.com/FlowFuse/flowfuse/issues/1376){rel=""nofollow""} - Self-managed FlowFuse needs an external email server to deliver email to users. FlowFuse should be able deal with the email server being offline and gracefully recover once it is back online. [#1159](https://github.com/FlowFuse/flowfuse/issues/1159){rel=""nofollow""} - Duplicate Activity Log for Project whose state is in flight [#1461](https://github.com/FlowFuse/flowfuse/issues/1461){rel=""nofollow""} ## Contributors We'd like the thank the following for their contributions to this release: [flecoufle](https://github.com/flecoufle){rel=""nofollow""} for their work on [#59](https://github.com/FlowFuse/docker-compose/pull/59){rel=""nofollow""} [sumanpaikdev](https://github.com/sumanpaikdev){rel=""nofollow""} for their work on [#53](https://github.com/FlowFuse/docker-compose/pull/53){rel=""nofollow""} [sdirosa](https://github.com/sdirosa){rel=""nofollow""} for their work on [#1326](https://github.com/FlowFuse/flowfuse/pull/1326){rel=""nofollow""} As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ### Try it out In 1.2 we've continued to improve the experience of running your own self managed FlowFuse installation. We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install our [local build](https://flowfuse.com/docs/contribute/local/), through [Docker](https://flowfuse.com/docs/install/docker/), or [Kubernetes](https://flowfuse.com/docs/install/kubernetes/). If you'd rather use our hosted offering: [Sign up for FlowFuse Cloud](https://app.flowfuse.com/account/create?code=FF12){rel=""nofollow""} with the coupon **FF12** to get your first project free for a month. ### Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.2. To use persistent context you'll need to upgrade your projects stack. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ### Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. You can raise a support ticket by emailing . We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you. # Configure FlowFuse in Docker to secure all traffic Following on from our [previous article](https://flowfuse.com/blog/2022/10/ff-docker-gcp/) in which we covered how to run FlowFuse in Docker on Google’s Cloud Platform, today we are going to look at how to secure HTTP traffic to your FlowFuse server. ### Introduction When we wrote the first part of this series FlowFuse didn't have an easy path to secure HTTP traffic. Happily, two versions of FlowFuse later and at least partially inspired by these blogs, we have added the configuration you need in Docker to use HTTPS with minimal work. That addition makes our job of explaining this setup a lot easier, credit to our developers for seeing the value of having an easy implementation of HTTPS in FlowFuse as part of our [1.0 build](https://flowfuse.com/blog/2022/10/flowforge-1-released/). To achieve secure HTTPS traffic we are employing a great service called [Let's Encrypt](https://letsencrypt.org/){rel=""nofollow""}. In their own words, "Let’s Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit". In practice Let's Encrypt will allow us to easily secure HTTPS traffic. We are also utilising a Docker image called [acme-companion](https://github.com/nginx-proxy/acme-companion){rel=""nofollow""} which makes the configuration of Let's Encrypt a breeze. To quote the project's own Github page "acme-companion is a lightweight companion container for nginx-proxy. It handles the automated creation, renewal and use of SSL certificates for proxied Docker containers through the ACME protocol". It's a great project and credit to the team over there for making it a lot easier to secure the internet. Now we've covered our goals and the tools we are going to use let's configure our existing GCP VM to secure all web traffic. ### Prerequisites As mentioned above, you will need to be running FlowFuse version 1.0 or higher to follow this guide. If you are using an older version you can upgrade now using the [instructions here](https://flowfuse.com/docs/upgrade/). ### Update Docker Compose The first step is to edit our Docker compose file. We're using Nano again to edit files so we will run this command: ```text sudo nano /opt/flowforge/docker-compose-1.1.1/docker-compose.yml ``` In the docker-compose.yml file, un-comment the following lines: ```yaml - "./certs:/etc/nginx/certs" ``` ```yaml - "443:443" ``` ```yaml acme: image: nginxproxy/acme-companion volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./acme:/etc/acme.sh" volumes_from: - nginx:rw environment: - "DEFAULT_EMAIL=mail@example.com" depends_on: - "nginx" ``` We should also redirect all traffic to use HTTPS, to do that un-comment the following in the nginx service section: ```yaml Environment: - "HTTPS_METHOD=redirect" ``` We now need to add the configuration for LetsEncrypt, edit the following lines to include a valid email address and the correct domain for where you are hosting your FlowFuse server: ```yaml - "DEFAULT_EMAIL=mail@example.com" ``` ```yaml - "LETSENCRYPT_HOST=mqtt.example.com" ``` ```yaml - "LETSENCRYPT_HOST=forge.example.com" ``` Save and exit from that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key. ### Update flowforge.yml Next, we need to edit the public\_url for the MQTT broker: ```text sudo nano /opt/flowforge/docker-compose-1.1.1/docker-compose.yml ``` Then replace ws\:// with wss\:// ```yaml public_url: wss://mqtt.flowforge-demo.com ``` Save and exit from that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key. ### Restart your Docker containers OK, we should be ready to restart the Docker containers, run the command: ```text sudo docker compose -p flowforge up -d ``` If you reload your FlowFuse root directory in a web browser you should now see that your traffic is encrypted using LetsEncypt. ![A screenshot from Safari web browser showing that the traffic to FlowFuse is encrypted](https://flowfuse.com/blog/2022/12/images/https-working.png "A screenshot from Safari web browser showing that the traffic to FlowFuse is encrypted") Nice! That’s it, you can now access your FlowFuse installation securely. In the next and final part of this series of articles, we are going to look at how we can actually use FlowFuse including how to build flows and deploy and update them on Devices linked to a project. # FlowFuse Inc. becomes a member of the OpenJS Foundation We're pleased to share the news we've joined the OpenJS Foundation to bolster our support for the Node-RED community. One of our [founding principles](https://flowfuse.com/handbook/company/principles/#open-source-stewardship) is the importance of Open Source being at the core of what we do. Node-RED has been part of the [OpenJS Foundation](https://openjsf.org/){rel=""nofollow""} since it was formed in 2019. The Foundation provides the project a vendor-neutral home and enables its open governance model that allows anyone to get involved. It also provides some vitally important resources to the community, such as the hosting for the [community forum](https://discourse.nodered.org){rel=""nofollow""}, which regularly gets over 1.5 Million page views a month. With my Node-RED Project Lead hat on, I regularly see the impact the foundation has had on the growth of the Node-RED community. These are things that may not always be obvious to our end users, but we wouldn't be where we are today with them. The success of what we're building with FlowFuse relies on a strong and healthy Node-RED community. Becoming members of the Foundation allows us to provide more yet more support to Node-RED and the wider JavaScript communities represented by the foundation. For more information about this news, check out the [OpenJS Foundation blog post](https://openjsf.org/announcement/2022/12/13/welcoming-flowforge-to-the-openjs-foundation/){rel=""nofollow""}. # Format your Node-RED flows for better team collaboration When it comes to working on Node-RED flows as part of a team, there are a few best practices that can make things go more smoothly. From formatting your flows for readability to providing clear comments on nodes and groups, a little bit of effort upfront can save your team a lot of headaches down the road. In this post, we'll cover some of the main things to keep in mind when working on Node-RED flows as part of a team. ### Give your groups descriptive names Let’s start with [grouping your flows](https://nodered.org/docs/user-guide/editor/workspace/groups){rel=""nofollow""} and giving each group a clear explanation of what it does. Compare the first to the second example below and consider how much more quickly you can understand what the flow is doing. #### This is not helpful, 'Time' doesn't tell you enough to understand the flow's purpose. !["Screenshot showing the example of flow having the bad group name"](https://flowfuse.com/blog/2022/12/images/name-bad.png "Screenshot showing the example of flow having the bad group name") #### This is much better, we know what the flow is doing without inspecting the nodes. !["Screenshot showing the example of flow having the good group name"](https://flowfuse.com/blog/2022/12/images/name-good.png "Screenshot showing the example of flow having the good group name") ### Explain what your switches do Try to make it obvious what each switch does without having to open the node editor. Ask a question in the switch's name and make a positive answer the top connection out. #### This is not easy to understand, what does the switch do? !["Screenshot showing the example of flow having the switch with bad name"](https://flowfuse.com/blog/2022/12/images/switch-bad.png "Screenshot showing the example of flow having the switch with bad name") #### This is a lot better, we can see that the top debug should be triggered. !["Screenshot showing the example of flow having the switch with good name"](https://flowfuse.com/blog/2022/12/images/switch-good.png "Screenshot showing the example of flow having the switch with good name") ### Where possible your flows should work down the canvas It makes it so much easier to understand what happens and in which order if your flows start at the top of the canvas and work down to the bottom. #### This is almost unreadable, it's very hard to work out the order of the groups. !["Screenshot showing an example of flow that doesn't work down the canvas"](https://flowfuse.com/blog/2022/12/images/flowdown-bad.png "Screenshot showing an example of flow that doesn't work down the canvas") #### Where as this is so much easier to understand. !["Screenshot showing an example of flow that works down the canvas"](https://flowfuse.com/blog/2022/12/images/flowdown-good.png "Screenshot showing an example of flow that works down the canvas") ### Use link nodes rather than wires to join groups Groups should not be joined using wires, it just looks untidy and quickly reduces readability of your flows. #### The wire is blocking the title, it only gets worse as you add more wires. !["Screenshot showing an example of flow with wires blocking group titles"](https://flowfuse.com/blog/2022/12/images/link-bad.png "Screenshot showing an example of flow with wires blocking group titles") #### You can see the group titles easily now. !["Screenshot showing an example of flow with link nodes improving readability"](https://flowfuse.com/blog/2022/12/images/link-good.png "Screenshot showing an example of flow with link nodes improving readability") ### Keep your groups compact Keeping your groups compact will save time when reading the flow. This is especially helpful if when viewed on a smaller screen. #### Consider how hard a flow made of groups spaced out like this would be to read on a smaller laptop screen. !["Screenshot showing an example of flow with widely spaced groups"](https://flowfuse.com/blog/2022/12/images/compact-bad.png "Screenshot showing an example of flow with widely spaced groups") #### This now takes up less space and is arguably easier to read on any screen size. !["Screenshot showing an example of flow with compact groups"](https://flowfuse.com/blog/2022/12/images/compact-good.png "Screenshot showing an example of flow with compact groups") ### Don’t cross ~~beams~~ wires Crossed wires are not only hard to read, they can lead to misinterpretation of what a flow actually does. Where possible don’t cross your wires, where you can’t avoid it try to make sure it’s easy for the reader to understand where wires cross as rather than join. #### This is confusing, which change node does the top switch output link to? !["Screenshot showing an example of the flow with nodes having crossed beams/wires"](https://flowfuse.com/blog/2022/12/images/wires-bad.png "Screenshot showing an example of the flow with nodes having crossed beams/wires") #### This is better, much less chance of confusing the change nodes. !["Screenshot showing an example of the flow with nodes having correctly linked beams/wires"](https://flowfuse.com/blog/2022/12/images/wires-good.png "Screenshot showing an example of the flow with nodes having correctly linked beams/wires") ### Don’t use link nodes in groups where avoidable Excessive link nodes within groups can make a flow much harder to understand, where possible use wires to join nodes within a group. #### This is hard to read and you will end up checking the link nodes again and again. !["Screenshot showing the example of flow having the uneccessary link nodes"](https://flowfuse.com/blog/2022/12/images/groupwires-bad.png "Screenshot showing the example of flow having the uneccessary link nodes") #### Functionally identical to the example above, it should only take a few seconds to understand this flow now. !["Screenshot showing an example of the flow with the avoided link nodes"](https://flowfuse.com/blog/2022/12/images/groupwires-good.png "Screenshot showing an example of the flow with the avoided link nodes") ### Boost Collaboration with FlowFuse [FlowFuse](https://flowfuse.com) is a cloud-based platform that makes working together on Node-RED projects easier and more efficient. It’s trusted by industries like manufacturing and smart building management, as well as textiles, to improve their systems. For more information, refer to our [customer stories](https://flowfuse.com/customer-stories/). With FlowFuse, you can quickly [set up and manage teams](https://flowfuse.com/docs/user/team/), giving each member the right level of access. It keeps all your [Node-RED instances organized in one place](https://www.youtube.com/watch?v=KOnQnR7yfT0&list=PLpcyqc7kNgp3nRacWBJ9JUVUJqtTjXdvh&index=2){rel=""nofollow""}, so your team can collaborate seamlessly. Plus, it features [snapshots](https://www.youtube.com/watch?v=m2Onip4Lf4w){rel=""nofollow""}, which let you restore previous versions of your flows if something goes wrong. FlowFuse simplifies team collaboration, making it easier to manage and work on Node-RED projects. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} now for a free trial and experience FlowFuse's powerful collaboration tools!** ### Conclusion Working on Node-RED flows as part of a team doesn't have to be a headache. By following some simple best practices you can make collaboration smooth sailing for everyone involved. So next time you're starting work on a new Node-RED flow, remember these tips and make life easier for yourself and your teammates. # Why you need FlowFuse when you already have Node-RED? Many organizations face challenges in IT/OT convergence. There are many protocols, disparate devices, and everything is a brownfield project. Furthermore, skilled engineers are hard to find. Many organizations find themselves maintaining systems and integrations, and have little time left to improve continuously. This is why Node-RED is adopted so widely, it makes a world of difference through two of its properties: Low-Code Development and the ability to integrate with a wide range of hardware and software systems. Node-RED makes it easy to program data collection, then use that data for decision making, and provide feedback to both your digital platform and the physical reality of your business. Furthermore, Node-RED is unrivalled in making data accessible. The data previously locked in a walled garden of your hardware providers is now accessible and obtainable. With that, more data is available and better decisions are made, faster. FlowFuse makes developer collaboration, flow deployment, and scaling of infrastructure easy when working with Node-RED. It offers an intuitive user interface for creating, deploying, monitoring, and managing multiple Node-RED projects. FlowFuse also provides one-click deployment to thousands of devices, making it easy to manage large-scale environments. These features make FlowFuse a valuable tool for organizations using Node-RED to build applications and automate processes. Enabling for further adoption of Node-RED to integrate more systems, for even better decisions. This not only reduces operational costs but it also allows an organization to be far more agile with their Node-RED projects than was possible without FlowFuse. We would be happy to talk to you more about how FlowFuse can help you get the most value from Node-RED. Please [contact us](https://flowfuse.com/contact-us/) to learn more. # Community News December 2022 Welcome to the FlowFuse newsletter for December 2022, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. 2022 was a really exciting year for the development of both Node-RED and FlowFuse. Node-RED released version 3, another major milestone for the project. FlowFuse hit version 1, leaving the beta development stage. We are excited to see what the community can achieve in 2023! If you've got something that you think we should share on our newsletters please [get it touch](mailto\:contact@flowfuse.com). [**Node-RED Nears 3.1 Release**](https://github.com/node-red/node-red/milestone/19){rel=""nofollow""} The release of Node-RED 3.1 is expected very soon. 3.1 includes lots of great new features such as support for [locking flows in the editor](https://github.com/node-red/node-red/pull/3938){rel=""nofollow""} and [improving the user experience around hiding flows](https://github.com/node-red/node-red/pull/3930){rel=""nofollow""}. As an open source project the development of Node-RED is entirely dependent on individuals and companies giving their time to work towards each new release. If you'd like to know how you can get involved you can read more on the [Node-RED web site](https://nodered.org/about/contribute/){rel=""nofollow""}. [**FlowFuse 1.2 Released**](https://flowfuse.com/blog/2022/12/flowforge-1-2-0-released/):br Version 1.2 of FlowFuse was released on 23rd December. Our last release of the year included some great new features such as [Single Sign-On](https://flowfuse.com/docs/cloud/introduction/#single-sign-on) to make it easier for your team to access your projects and [Persistent Context](https://flowfuse.com/docs/cloud/introduction/#node-red-context) which allows you to retain context values even when restarting projects. We're now working towards our first release of 2023 which is due on 19th January. You can see what we are planning to deliver in that release and beyond on [FlowFuse's project board](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""}. If you’d like to learn more about what else was included in 1.2 you can do so on our [blog post](https://flowfuse.com/blog/2022/12/flowforge-1-2-0-released/), [GitHub release page](https://github.com/FlowFuse/flowfuse/releases/tag/v1.2.0){rel=""nofollow""}, and [Youtube channel](https://www.youtube.com/watch?v=u7TjqUAub1g){rel=""nofollow""}. [**5 IoT Sensor Technologies to Watch**](https://iot-analytics.com/5-iot-sensor-technologies/){rel=""nofollow""}:br According to this detailed article from iot-analytics.com, on average, four new sensors are connected with every new IoT device that comes online. With approximately 14 billion current IoT connections, this means more than 50 billion connected sensors have been deployed. IoT sensor technology plays a crucial role in the IoT tech stack because these sensors collect data from the physical world and convert it into digital signals. We think [their article](https://iot-analytics.com/5-iot-sensor-technologies/){rel=""nofollow""} is worth a read. [**Custom Node Spotlight - node-red-contrib-string**](https://flows.nodered.org/node/node-red-contrib-string){rel=""nofollow""} String manipulation is the bread and butter of so many programming tasks. Node-RED has a lot of tools to help you edit your strings including support for [JSONata](https://jsonata.org/){rel=""nofollow""} and using the trusty function node. For those of us who prefer to keep things 'no-code' [node-red-contrib-string](https://flows.nodered.org/node/node-red-contrib-string){rel=""nofollow""} allows you to stack string manipulations together quickly and easily. It has a huge library of ready to use functions. [**FlowFuse Team News**](https://flowfuse.com/about/):br We’d like to welcome two new members to the FlowFuse team. [Ian Skerrett](https://twitter.com/ianskerrett){rel=""nofollow""} has joined as our Head of Marketing and [Tracy Anthony](https://www.linkedin.com/in/tracyanthonyfernandez/){rel=""nofollow""} has joined as our HR Manager. FlowFuse continues to grow and we are becoming a truly global team. Would you like to work for FlowFuse? We are currently recruiting [NodeJS Developers](https://boards.greenhouse.io/flowfuse/jobs/4463977004){rel=""nofollow""} to join our team. You can view any of the roles we currently have open and apply on our [Jobs page](https://boards.greenhouse.io/flowfuse){rel=""nofollow""}. [**Try FlowFuse for Free**](https://app.flowfuse.com/account/create?code=FF12){rel=""nofollow""}:br As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on our managed FlowFuse platform when you create a new team. To get this discount please follow [this link](https://app.flowfuse.com/account/create?code=FF12){rel=""nofollow""} or use the code FF12 when on the payment page after creating a new team. As an open source project you can also use [FlowFuse](https://flowfuse.com/docs/install/) for free, forever. # Using Environment Variables in Node-RED (2026) Programs, written with Node-RED or otherwise, need to sometimes retrieve information that wasn’t decided on during the creation of the program. Contextual data like configuration, which user is executing the code, differentiate based on what device is executing a flow, or sometimes secrets which shouldn’t be exposed in the code. This is usually done through environment variables. These are pairs of strings, a key with an attached value, which are accessed by their key. Say you want to access an API endpoint with a key, you’d save the key as `API_KEY` with the value set to `yoursupersecretkey`. FlowFuse allows setting environment variables. Let’s start using them to understand how they work. One of the options for the `inject` node is to inject a `env variable`, short for; you guessed it: Environment Variable. In this case we’re going to one that’s pre-defined by Node-RED: `NR_FLOW_NAME`. The name of each variable is in all caps by convention. When connecting this inject to a debug it prints “Flow 1” for me. :cta-image{alt="Walk through your FlowFuse setup with our team - book a demo" cta="demo" src="https://flowfuse.com/images/cta/book-a-demo.png"} ![Using an environment variable in Node-RED](https://flowfuse.com/blog/2023/01/images/node-red-use-env-var.png "Using an environment variable in Node-RED") Leveraging environment variables can also be done with other nodes, like for example `change`, `switch`. Note however; you can set the `inject` node to output the value for `FOO` even when it doesn’t exist, but it doesn’t allow you to check in the switch node for example if `FOO` exists. Node-RED allows you to set environment variables, but not to change them when executing flows. If you want to update data during execution, look into using [persistent context](https://flowfuse.com/docs/user/persistent-context/). Node-RED doesn’t support Environment Variables like other programming environments do. When the flow is deployed the environment variables are replaced with the known values at that time. This is the biggest gotcha for most developers. ### Predefined variables Our first example was using a predefined variable, exposed by Node-RED. As of 3.0 it exposes a few environment variables among which `NR_NODE_NAME`, `NR_GROUP_NAME`, and `NR_FLOW_NAME`. [FlowFuse](https://flowfuse.com) extends this list with for example a `FF_PROJECT_ID` allowing you to for example understand what group of instances sent a certain message, but also sets them for each [device agent](https://flowfuse.com/docs/device-agent/introduction/). This allows users to pinpoint which device sent a message, for example to update a dashboard accordingly. ### Managing environments variables In FlowFuse it’s easy to manage variables set for instances. Under settings in the environment tab it’s a form to set them. You’ll have to restart your instances to make them available in the cloud, and update the target snapshot for devices. When done, these are available. !["Setting a environment variable in FlowFuse"](https://flowfuse.com/blog/2023/01/images/flowforge-set-env-var.png "Setting a environment variable in FlowFuse") ## Boost Your Node-RED Security with FlowFuse FlowFuse provides a comprehensive platform for managing and securing your Node-RED solutions. It includes advanced security features such as role-based access control, Multi-factor Authentication (MFA), Single Sign-On (SSO), and encryption to protect your data and enhance operational efficiency. Learn how FlowFuse can boost your Node-RED security and streamline management through the [FlowFuse security statement](https://flowfuse.com/platform/security/#application). ### Explore More on Security - [Role-Based Access Control (RBAC) for Node-RED with FlowFuse](https://flowfuse.com/blog/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/) - [Protecting Instances from Being Modified](https://flowfuse.com/docs/user/devops-pipelines/#protected-instances) - [How to Set Up SSO LDAP for Node-RED](https://flowfuse.com/blog/2024/07/how-to-setup-sso-ldap-for-the-node-red/) - [How to Set Up SSO SAML for Node-RED](https://flowfuse.com/blog/2024/07/how-to-setup-sso-saml-for-the-node-red/) - [Securing HTTP Traffic for Node-RED with FlowFuse](https://flowfuse.com/blog/2024/03/http-authentication-node-red-with-flowfuse/) - [FlowFuse is now SOC 2 Type 1 Compliant](https://flowfuse.com/blog/2024/01/soc2/) # FlowFuse 1.3 is now available, share your flows through our new team libraries and much more Share your flows via team libraries, control access to your Node-RED dashboards using FlowFuse credentials, and filter your audit logs by users and actions. We're pleased to announce version 1.3 is now available! Due to the recent holiday season, most of our team have been away from their desks but we still have some great new features to share. Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above. To make it easy for everyone to experience FlowFuse, we are introducing a new [free 30-day trial](https://app.flowfuse.com/account/create){rel=""nofollow""}. With this trial, you can experience the power of using FlowFuse to quickly deliver Node-RED applications in a reliable, repeatable, collaborative, and secure manner. To get your trial simply [sign up for a new FlowFuse team](https://app.flowfuse.com/account/create){rel=""nofollow""}. ## Features [Share your flows via team libraries](https://github.com/FlowFuse/flowfuse/issues/237){rel=""nofollow""} :br FlowFuse has now added the ability for you to share your flows via the import and export features in Node-RED. Once you export a flow everyone else in your FlowFuse team will be able to import your work into their projects. You can see a demonstration of this new feature in [the video](https://youtu.be/B7XK3TUklUU){rel=""nofollow""} below. ::div :::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: B7XK3TUklUU --- ::: [Control access to your Node-RED dashboards using FlowFuse credentials](https://github.com/FlowFuse/flowfuse/issues/1325){rel=""nofollow""} :br In FlowFuse 0.10 we added the ability to secure endpoints created within your FlowFuse projects. This allows you to create dashboards or APIs and limit who can access them. In 1.3 we've added the ability for you to limit access to those same resources based on the visitor having a user account on your FlowFuse team. You can see a demonstration of this new feature in [the video](https://youtu.be/JRk-Cf7eNIo){rel=""nofollow""} below. :::div ::::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: JRk-Cf7eNIo --- :::: [Filter your audit logs for easier reading](https://github.com/FlowFuse/flowfuse/issues/1448){rel=""nofollow""} :br In FlowFuse 1.3 we’ve added the ability to filter your admin logs by user or action type. We think this is a great new feature which will help admins have confidence that they will be able to review the audit logs quickly when needed. You can see a demonstration of this new feature in [the video](https://youtu.be/p0Vuy5x42Go){rel=""nofollow""} below. ::::div :::::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: p0Vuy5x42Go --- ::::: ## Improvements [Allow installation of FlowFuse on devices which can't access npm](https://github.com/FlowFuse/device-agent/issues/45){rel=""nofollow""} :br We've had feedback from customers that in some cases they want to use FlowFuse devices on hardware which cannot access [Node Package Manager](https://www.npmjs.com/){rel=""nofollow""} (npm). In a standard configuration of Node-RED, access to npm is mandatory to run your flows. In FlowFuse 1.3.0 we've added the ability for you to import all the data usually installed from npm without your devices having access to the npm service. ## Bug Fixes We've fixed the following bugs in this release. - Project status UI sometimes getting stuck when restarting [#1232](https://github.com/FlowFuse/flowfuse/issues/1232){rel=""nofollow""} - SSO users asked to click link in email to verify [#1543](https://github.com/FlowFuse/flowfuse/issues/1543){rel=""nofollow""} - SSO users unable to edit settings [#1542](https://github.com/FlowFuse/flowfuse/issues/1542){rel=""nofollow""} - SSO users not redirected to editor when signing in [#1481](https://github.com/FlowFuse/flowfuse/issues/1481){rel=""nofollow""} ## Contributors We'd like to thank the following for their contributions to this release: [flecoufle](https://github.com/flecoufle){rel=""nofollow""} for their work on [#89](https://github.com/FlowFuse/helm/pull/89){rel=""nofollow""} As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.3. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That's also a great place to send us any feedback or feature requests. You can also get help on [the Node-RED forums](https://discourse.nodered.org/){rel=""nofollow""} AS well as in the [forum within our Github project](https://github.com/FlowFuse/flowfuse/discussions){rel=""nofollow""} Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""} You can raise a support ticket by emailing We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you. :::: ::: :: # FlowFuse 1.2.1 released We've published a maintenance release containing a fix for new users. This release fixes an [issue](https://github.com/FlowFuse/flowfuse/issues/1537){rel=""nofollow""} introduced in FlowFuse 1.2 where users were not being sent their welcome email when they first sign up to the platform. As the sign-up page asks them to click on the link in that email, it left them waiting for an email that wasn't going to arrive. The same fix also addresses an [issue](https://github.com/FlowFuse/flowfuse/issues/1514){rel=""nofollow""} where users could not sign up using an email with a `+` in it. This is a restriction we apply to users signing up with Single Sign-On enabled email domains - but the check was being applied to everyone. ### Upgrading FlowFuse This release has already been rolled out to \[FlowFuse Cloud]\({{ site.appURL }}). If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/) ### Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform, please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That also includes if you have any feedback or feature requests. Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""}. You can also raise a support ticket by emailing # Telling the FlowFuse Story During my first month at FlowFuse, I have been helping to refine the FlowFuse story. Trying to answer the questions: What is the value FlowFuse brings to our customers and what features does FlowFuse want to offer the Node-RED community? People love Node-RED because it makes it trivial to connect different types of services and data, create flows of the data, and store/forward data to the cloud or a database. The Node-RED [library of nodes and flows](https://flows.nodered.org/){rel=""nofollow""} enables connections to almost anything. The speed and ease of use of Node-RED allows non-traditional programmers to accelerate their innovation and exploration of what is possible. Node-RED is great for individuals to unlock what is possible for an organization. However, there are some challenges if you want to scale Node-RED into a corporate development platform. Challenges like how do you set up a development -> test -> production delivery pipeline, how to share instances with different developers, how do you create repeatable release processes, how can you deploy the same Node-RED instance out to multiple target environments (devices or servers). Today, we see many industrial engineers using Node-RED to collect and integrate data from different factory and industrial equipment. Many PLC vendors, like Opto22, are enabling this by making Node-RED as a development environment for the PLC platform. In the manufacturing and industrial automation industries, benefits like reliability and security are important for the continuous operation of these facilities. This is why we believe these types of organizations will be looking for tools that improve their software development lifecycle. In the larger software development community, DevOps tools are being used to address many of these same challenges. DevOps is all about increasing the reliability and speed of the software development lifecycle. Using tools that increase automation of the software development process and improve collaboration and communication between developers. These are also many of the same challenges that FlowFuse solves for Node-RED users. For these reasons, we see FlowFuse as being the DevOps platform for Node-RED. We believe FlowFuse enables a more reliable way to deliver Node-RED applications in a repeatable, collaborative and secure manner. What FlowFuse adds to Node-RED are some key features: - Tools for collaborative Node-RED development - The ability to manage remote deployments of Node-RED instances - A more streamline way to deliver Node-RED applications - FlowFuse Cloud provides a hosted Node-RED platform but FlowFuse can also be self-hosted - FlowFuse also offers professional technical support to organizations that require the assurance of receiving help with production deployments. We will be rolling out this new way of talking about FlowFuse over the next couple of days and weeks. We are always open to feedback about FlowFuse and would love to hear about your experience of using Node-RED. # Getting Started with Node-RED Node-RED is a visual programming tool for working with IoT devices and web services. It allows users to create flows using a drag-and-drop interface, making it easy to connect different nodes together to build powerful automations. In this blog post, we'll take a look at how to get started with Node-RED and create some basic flows. We'll also explore the palette manager, a powerful feature that allows users to install and manage additional nodes for Node-RED. ### Installing Node-RED First, you'll need to get an installation of Node-RED up and running. There are several ways to do this. We suggest using FlowFuse as it's very easy to get Node-RED running. You can also install Node-RED locally using npm (Node Package Manager), which comes with Node.js. #### FlowFuse To get Node-RED running on FlowFuse [sign up as a new user](https://app.flowfuse.com/account/create){rel=""nofollow""}. New users are enrolled in a trial and a Node-RED instance will be started for you within a minute. Once that instance has booted up you can access Node-RED by pressing "Open Editor". #### npm To install Node-RED locally using npm, open up your terminal and type the following command: ```bash npm install -g node-red ``` Once Node-RED is installed, you can start it by running the following command: ```bash node-red ``` This will start the Node-RED server and open up the [editor in your web browser](http://localhost:1880){rel=""nofollow""}. You can also specify a different port or a settings file if you want to. If you want to run Node-RED locally but manage it remotely through FlowFuse, check out [this guide](https://flowfuse.com/blog/2025/09/installing-node-red/). ### First Flow Now that you have Node-RED running, let's take a look at how to create a simple flow. In this example, we'll create a simple "Hello World" endpoint. To do this, we'll use the `http in`, `http response`, and the `change` nodes, which can be found in the common nodes menu on the left of Node-RED. First, drag an `http in` node into the editor. This node will listen for incoming HTTP requests. Next drag in the "change" and the `http response` node into the editor. Connect the `http in` node to the `change` node and connect the `change` node to the `http response` node. Your flow should look similar to this: !["Screenshot showing the HTTP-in, Change, and HTTP-response nodes that we will be using throughout this blog for demonstration."](https://flowfuse.com/blog/2023/01/images/three-nodes.png "Screenshot showing the HTTP-in, Change, and HTTP-response nodes that we will be using throughout this blog for demonstration.") To configure the `http in` node, double-click on it to open its properties. Here, you can set the URL that the node will listen to, as well as the method (GET, POST, etc.). In this example, we'll set the URL to `/hello` and the method to `GET`. Now we need to set what the endpoint will respond with, we will do that in the `change` node. Double-click the `change` node then add "Hello World" to the field which says "to the value". It should look like this: !["Configuring the change node to set the payload to Hello World"](https://flowfuse.com/blog/2023/01/images/set-reply.png "Configuring the change node to set the payload to Hello World") To configure the `http response` node, double-click on it to open its properties. Here, you should set the "Status Code" to be 200. This is not vital for the demo to work but it's good practice to return the correct codes when something connects to an API. Status code 200 means the API responded OK. This is how your `http response` node should look: !["Configuring the status node to set the response to 200"](https://flowfuse.com/blog/2023/01/images/response-code.png "Configuring the status node to set the response to 200") You can read more about HTTP response codes in [this article](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes){rel=""nofollow""}. ### Testing Your Flow Now that we have our flow set up, we can deploy it by clicking the "Deploy" button in the top right corner of the editor. Once the flow is deployed, you can test it by opening up a web browser. If you installed Node-RED using npm navigate to "{rel=""nofollow""}". If you are working on FlowFuse and running cloud hosted instance, use your instance URL with "/hello" added to the end, it should look something like "{rel=""nofollow""}". You should see "Hello World!" displayed in the browser. ### Debug Output One of the most powerful features in Node-RED is the ability to debug your flow. This can be done by adding a debug node to your flow and connecting it to the nodes you want to debug. When a message is sent through the connected node, the debug node will print the message in the debug sidebar on the right side of the editor. This can be very helpful when trying to understand how a flow is working or troubleshoot any issues. ### The Palette Manager In addition to the built-in nodes, Node-RED also has a palette manager feature which allows users to easily install and manage additional nodes from the community. To access the palette manager, go to the menu in the top right corner and select "Manage Palette". Here, you can search for and install new nodes, as well as update or remove existing ones. This is a great way to extend the functionality of Node-RED and add new capabilities to your flows. ### Import the flow If you want to view this flow you can import it using the code below. Copy the code then select Import from the top right menu in Node-RED. Paste the code into the field then press Import. ::render-flow ```json [ { "id": "a742e7a95697bb40", "type": "http in", "z": "9e9af3caa4dc14d3", "name": "", "url": "/hello", "method": "get", "upload": false, "swaggerDoc": "", "x": 180, "y": 200, "wires": [ [ "883e7d597f7c7c4b" ] ] }, { "id": "aca024dcb79bdb92", "type": "http response", "z": "9e9af3caa4dc14d3", "name": "", "statusCode": "200", "headers": {}, "x": 500, "y": 200, "wires": [] }, { "id": "883e7d597f7c7c4b", "type": "change", "z": "9e9af3caa4dc14d3", "name": "", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "Hello World", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 340, "y": 200, "wires": [ [ "aca024dcb79bdb92" ] ] } ] ``` :: ### What's Next? Well done, you've now got your first flow up and running. Enjoy using Node-RED and thanks for reading. Now if you want to start with your first beginner friendly project, building a weather dashboard is great, read this [article for getting started](https://flowfuse.com/blog/2025/12/getting-weather-data-in-node-red/). If you'd like to dive deeper into more Node-RED capabilities and how it can help in an enterprise setting, check out our [eBook The Ultimate Beginner Guide to a Professional Node-RED](https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/). # Node-RED Tips - Wiring Shortcuts There is usually more than one way to complete a given task in software and Node-RED is no exception. In this blog post we are going to share three useful tips to save yourself time when working on your flows. ### 1. Use control+left-click to search your nodes Sometimes it's quicker to search for a node using its name rather than scrolling through the palette. Simply hold control then left-click to bring up a searchable list. :video{ariaLabel="Selecting a node without having to use the palette" autoPlay="true" height="318" loop="true" muted="true" playsInline="true" preload="none" width="774"} ### 2. Split sections of your code using the link nodes If you want to separate your flow into two distinct sections, link nodes are a great way to format your work. As we covered in our blog on [Node-RED best practices](https://flowfuse.com/blog/2022/12/node-red-flow-best-practice), the combination of link nodes and grouped flows is very powerful. To split your flow select the input and output nodes then right click, select 'Show Action List' and then type 'split'. Select 'Split wire with link nodes'. :video{ariaLabel="Spliting your nodes with link nodes" autoPlay="true" height="440" loop="true" muted="true" playsInline="true" preload="none" width="726"} ### 3. Link multiple inputs and outputs in one command Once a switch node has several outputs it can be slow to manually wire each to the new node. Using the action menu (right click), select 'Show Action List' then 'Wire Node to Multiple' this option will join everything up in one step. :video{ariaLabel="Linking multiple inputs and outputs in one command" autoPlay="true" height="362" loop="true" muted="true" playsInline="true" preload="none" width="538"} We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). # Node-RED Tips - Deploying, Debugging, and Delaying There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows. ### 1. Deploy just what you've changed When deploying your changes, the default option is deploy everything which also restarts all your flows. You can also select to deploy just the nodes you edited or just the flows in which any changes were made. This allows you to update part of your flow without restarting other sections. This can be really handy when you have different flows spread across your workspace or tabs but you don't want to reload them all each time you deploy. :video{ariaLabel="Deploying only the changed nodes" autoPlay="true" height="322" loop="true" muted="true" playsInline="true" preload="none" width="898"} ### 2. Find which debug node generated an entry in the log Once your flow has a few debug nodes it can become challenging to see which particular node generated an entry in the log. To quickly track an entry back to its source, click the text 'node: debug' and you will be whisked back to the specific debug node, even it it's elsewhere on you workspace or even on a different tab. :video{ariaLabel="Finding the debug node which generated the log line" autoPlay="true" height="258" loop="true" muted="true" playsInline="true" preload="none" width="898"} ### 3. The delay node can be used as a rate limiter Sometimes it's useful to be able to limit messages to only allow one every so many minutes. You may for example send alerts when a temperature sensor goes above a particular threshold but you don't want your email or instant messaging inbox being swamped with repeated alerts for the same issue. You can use the delay node to limit how many messages can pass through in a given period of time. Open the delay node settings, select Rate Limit then select 1 message per 15 minutes, then select 'Drop intermediate messages'. This flow will now output a maximum of one message every quarter of an hour, all others will be deleted. :video{ariaLabel="Limiting how many alerts are sent" autoPlay="true" height="298" loop="true" muted="true" playsInline="true" preload="none" width="764"} We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). ## Enhance Efficiency with FlowFuse FlowFuse is a cloud-based platform designed to boost collaboration, security, and scalability for your Node-RED applications. It features [numerous tools and functionalities](https://flowfuse.com/platform/features/) that streamline the development-to-deployment process, including one-click deployment, the [FlowFuse Assistant](https://flowfuse.com/docs/user/expert/), and other capabilities that simplify managing your Node-RED environment. For more tips, tricks, and professional development techniques with Node-RED, check out our recommended eBook: [The Ultimate Beginner's Guide to Professional Node-RED](https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/) # Community News February 2023 Welcome to the FlowFuse newsletter for February 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. If you've got something that you think we should share on our newsletters please [get in touch](mailto\:contact@flowfuse.com). ## News ### Node-RED Ask Me Anything ![AMA Session with Nick O'Leary and Rob Marcer](https://flowfuse.com/images/webinars/ama-feb.jpg "AMA Session with Nick O'Leary and Rob Marcer") Do you have any questions about Node-RED or need some advice on a tricky issue using Node-RED? Here is your opportunity to get help from the experts. Nick O'Leary, co-founder of Node-RED & CTO of FlowFuse, and Rob Marcer, Node-RED community member & Developer Educator at FlowFuse, will be hosting an interactive Ask Me Anything session. This is a great opportunity to ask questions of the Node-RED experts. If you have any questions for Nick and Rob you can send them in before the session using [this form](https://docs.google.com/forms/d/e/1FAIpQLSdfPq4lAQjdvqhTpoYtKiMNgP8vcMhZsAf_AG0MHuVMRK83_Q/viewform){rel=""nofollow""}. ### [Free FlowFuse project for 30 days, no catches](https://app.flowfuse.com/account/create){rel=""nofollow""} To make it easy for everyone to experience FlowFuse, we are introducing a new [free 30-day trial](https://app.flowfuse.com/account/create){rel=""nofollow""}. With this trial, you can experience the power of using FlowFuse to quickly deliver Node-RED applications in a reliable, repeatable, collaborative, and secure manner. To get your trial simply [sign up for a new FlowFuse team](https://app.flowfuse.com/account/create){rel=""nofollow""}. ### [1.3 Released](https://flowfuse.com/blog/2023/01/flowforge-1-3-0-released) Version 1.3 of FlowFuse was released on 19th January. Our first release of 2023 included some great new features such as the ability to share flows via a [team library](https://www.youtube.com/watch?v=B7XK3TUklUU){rel=""nofollow""}, [control access to your Node-RED dashboards](https://www.youtube.com/watch?v=JRk-Cf7eNIo){rel=""nofollow""} using FlowFuse credentials, and [filtering on your audit logs](https://www.youtube.com/watch?v=p0Vuy5x42Go){rel=""nofollow""} for easier reading. We also added the ability to use FlowFuse on devices which cannot access npm, we think this will be really valuable to users of networks with limited access to the internet. We're now working towards release 1.4 which is due on 16th February. You can see what we are planning to deliver in that release and beyond on [FlowFuse's project board](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""}. If you’d like to learn more about what else was included in 1.3 you can do so on our [blog post](https://flowfuse.com/blog/2023/01/flowforge-1-3-0-released/), [GitHub release page](https://github.com/FlowFuse/flowfuse/releases/tag/v1.3.0){rel=""nofollow""}, and [Youtube channel](https://www.youtube.com/watch?v=ey3xv5j5x7k){rel=""nofollow""}. ### [Team News](https://flowfuse.com/about/) We are currently recruiting [NodeJS Developers](https://boards.greenhouse.io/flowfuse/jobs/4463977004){rel=""nofollow""} as well as a [Graphic Designer](https://boards.greenhouse.io/flowfuse/jobs/4785058004){rel=""nofollow""} to join our team. You can view any of the roles we currently have open and apply on our [Jobs page](https://boards.greenhouse.io/flowfuse){rel=""nofollow""}. ## Node-RED in the Community ### [Twitch Streamer beats Elden Ring boss using mind control (and a little Node-RED)](https://www.vice.com/en/article/bvmqmm/watch-an-elden-ring-streamer-beat-a-boss-using-her-thoughts){rel=""nofollow""} [![Twitch Streamer beats Elden Ring boss using mind control (and a little Node-RED)](https://flowfuse.com/blog/2023/02/images/twitch.webp)](https://www.vice.com/en/article/bvmqmm/watch-an-elden-ring-streamer-beat-a-boss-using-her-thoughts){rel=""nofollow""} Streamer [Perrikaryal](https://www.twitch.tv/videos/1717013810){rel=""nofollow""} used an electroencephalography (EEG) headset to read her brain activity which in turn sends commands via Node-RED to her gaming computer. She then proceeded to use that control method to beat one of the harder bosses in Elden Ring, a notoriously difficult game to start with. The end result is a great example of how Node-RED can link disparate tech together easily. You can [watch the full stream on Twitch](https://www.twitch.tv/videos/1722048787){rel=""nofollow""}. ### [Run Node-RED in a web browser, client side!](https://www.linkedin.com/posts/kazuhitoyokoi_nodered-webassembly-activity-7015696090112958464-F3MA/?utm_source=share&utm_medium=member_android){rel=""nofollow""} [Kazuhito Yokoi](https://www.linkedin.com/in/kazuhitoyokoi/){rel=""nofollow""} has implemented an early prototype of Node-RED runtime for WebAssembly. As a demonstration, he built a simple flow which shows the current location of the International Space Station (ISS). This proof-of-concept shows that an entire Node-RED flow can be executed in a browser. We think this could be a very useful option for running Node-RED in places where it wasn’t previously practical. You can view his work on this [GitHub project](https://github.com/kazuhitoyokoi/node-red-wasm){rel=""nofollow""}. ### [Custom Node Spotlight - Moment](https://flows.nodered.org/node/node-red-contrib-moment){rel=""nofollow""} [![Moment converting a timestamp to ISO standard date and time](https://flowfuse.com/blog/2023/02/images/moment.png)](https://flows.nodered.org/node/node-red-contrib-moment){rel=""nofollow""} Being able to easily switch dates and times from one format to another is a huge timesaver, node-red-contrib-moment makes those tasks a breeze. The package actually includes two custom nodes, the first ‘Moment’ produces a nicely formatted Date/Time string using the Moment.JS library. The second custom node ‘Humanizer’ converts time durations (time spans) into textual descriptions (e.g. 2 minutes). We recommend you keep it in mind for any flows working with time conversions. # FlowFuse v1.4 with device provisioning in bulk and staged development process Deploy Node-RED to many devices quickly, and allow a staged development process with the latest release of FlowFuse v1.4. Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above. To make it easy for everyone to experience FlowFuse, we are introducing a new free 30-day trial. You can now experience the power of using FlowFuse to quickly deliver Node-RED applications in a reliable, repeatable, collaborative, and secure manner. To get your free trial simply [sign up for FlowFuse Cloud;](https://app.flowfuse.com/account/create){rel=""nofollow""} no credit card is required! ## New User Features **Automatic Device Provisioning** Most prominently, FlowFuse 1.4 features automatic device onboarding for fleets. Simply download a FlowFuse device provisioning credential to allow quick roll-out to a whole fleet, without the need to have device specific configuration. When the agent starts, the FlowFuse agent and the Node-RED snapshot will automatically be provisioned to the device and start operations. [Issue #1212](https://github.com/FlowFuse/flowfuse/issues/1212){rel=""nofollow""} ::div :::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: XTVw4O4-Crg --- ::: **Support for Staged Development** A new feature of 1.4 is the ability to setup staged deployments. This makes it possible to simply move a project between a Development > Test > Production for your Node-RED application delivery. [Issue #1580](https://github.com/FlowFuse/flowfuse/issues/1580){rel=""nofollow""} :::div ::::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: 6QOmotlrwWw --- :::: ## Improvements - It's now much easier to change the resources available to your Node-RED instance. With a few clicks a resource intensive workload can be processed faster by changing between small, medium, or large instance types. [#595](https://github.com/FlowFuse/flowfuse/issues/595){rel=""nofollow""} - Last release allows users to capture flows in a shared library to reuse in another flow, now it's possible to preview the stored flows. [#1657](https://github.com/FlowFuse/flowfuse/issues/1657){rel=""nofollow""} - Add a synchronous mode for the FlowFuse persisted context store, next to the asynchronous mode already available. [#17](https://github.com/FlowFuse/flowforge-nr-persistent-context/issues/17){rel=""nofollow""} - Add a Last Seen status for devices connecting to FlowFuse. [#1599](https://github.com/FlowFuse/flowfuse/issues/1599){rel=""nofollow""} - Added a check to ensure the team slug is unique. [#1609](https://github.com/FlowFuse/flowfuse/issues/1609){rel=""nofollow""} - Optionally set snapshot as target at creation, to quickly roll out changes to remote deployments [#1527](https://github.com/FlowFuse/flowfuse/issues/1527){rel=""nofollow""} - Agents are more rugged when starting up if they're unable to connect to FlowFuse, and will retry to connect. - With FlowFuse v1.4 some changes were made under the hood to speed up the recovery of Node-RED instances. On terminal failures of an instance it will now be automatically be redeployed with the correct flows. This uses Kubernetes features and is also available if you've installed through [kubernetes](https://flowfuse.com/docs/install/kubernetes/). To migrate the old style of deployments to this system a restart or stack upgrade is needed. ## Bug Fixes We've fixed the following bugs in this release. - Deleting your only team, doesn't exit from the team UI. [#1630](https://github.com/FlowFuse/flowfuse/issues/1630){rel=""nofollow""} - Async Team Slug Check. [#1609](https://github.com/FlowFuse/flowfuse/issues/1609){rel=""nofollow""} - Improve communication of Device Last Seen and Status [#1599](https://github.com/FlowFuse/flowfuse/issues/1599){rel=""nofollow""} ## Contributors We'd like the thank the following for their contributions to this release: - [UlisesGascon](https://github.com/UlisesGascon){rel=""nofollow""} for their work on [#74](https://github.com/FlowFuse/installer/pull/74){rel=""nofollow""} As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.4. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That's also a great place to send us any feedback or feature requests. You can also get help on [the Node-RED forums](https://discourse.nodered.org/){rel=""nofollow""} As well as in the [forum within our Github project](https://github.com/FlowFuse/flowfuse/discussions){rel=""nofollow""} Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""} You can raise a support ticket by emailing We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you. ::: :: # Toward Highly Available Node-RED Over the past few months we've held a lot of product discovery sessions and a topic which keeps coming up is "HA Node-RED". All software will have failures, with HA (high availability) the intent is to allow the workload to be processed regardless. There's quite a few considerations which are often not covered during product discovery calls, I'm going to discuss some of those points in this article. When my job title was software engineer I was fortunate to design and implement a HA system. It's an incredibly challenging and rewarding task for any software engineer. As a topic, it's studied when obtaining a Computer Science Bachelors degree, masters and even PhD. When tasked to make a HA system, it took me a good month to define what our goals were, and what we were willing to exchange for the properties sought. This might be extra hardware, engineering hours, as well as organizational challenges. For now, let's focus on the first two. Let's start with defining the goal; reduce the impact of a Node-RED instance being unresponsive for an arbitrary reason. In many use-cases the MTTR (Mean Time To Recovery) is what's measured. When for example a hardware failure takes down the instance and the time to detection is zero, it will likely still take a few hours to recover. Most of the recovery work is also manual, and knowledge on how to recover is usually [tribal knowledge](https://en.wikipedia.org/wiki/Tribal_knowledge){rel=""nofollow""}. If the right person is on-site, the right hardware is available, you kept great backups, and are able to deploy the new hardware right away without support from other functions you might just achieve an MTTR of 120 minutes! ### 5-10 minute Mean Time to Recovery What's needed to bring this back to say 10 minutes? First, adopting FlowFuse will help massively here. FlowFuse can be installed on-premise, or you can use our managed Cloud offering. The software is the same, provided the on-premise install uses our [Kubernetes install](https://flowfuse.com/docs/install/kubernetes/) method. The key of the installation is the fact that the hardware layer is generalized as a fleet. Detecting failures is included in the install, and very fast. Comparing that to most alerting systems currently, it's usually a difference between night and day. Furthermore, to decrease the recovery time significantly there's a requirement to make software responsible for the whole procedure. Human intervention is much too slow. To get the MTTR down to 5 minutes there's a requirement to either make hardware automatically available to the fleet, or to over-provision (more hardware is available than is needed at any given moment). When a hardware failure occurs FlowFuse is configured to ensure all Node-RED instances that are KIA are replaced. Bringing down the time to recovery to about 5 minutes. For many use-cases a MTTR of 5 minutes is *good enough*. ### Sub minute MTTR To go below the minute, or dare I say go below 10 seconds, we'll need to increase the number of running Node-RED instances. Let's start with a hot-spare. Meaning there's a running Node-RED instance with the flows exactly the same as another, ready to pick up the work when the first has some failure. Note this isn't like a relay race, there's no baton being passed from one Node-RED to the other. While some data and messages might be lost, it's possible to redirect all workload from the plagued Node-RED to the hot-spare in a matter of seconds. Hot-spares taking over are usually only observed by humans a good few minutes after they replace a failed instance. ### Sub second! Before this post turns into a theoretical exercise we'd really need to understand which trade-offs are acceptable to you. There's the [CAP Theorem](https://en.wikipedia.org/wiki/CAP_theorem){rel=""nofollow""} which states 3 guarantees are wanted: Consistency, Availability, and Partition Tolerance. You get to pick only two. In manufacturing the line must never be stopped due to a software failure where possible, so Availability is the most important. The question is, what comes next? Is it Consistency meaning all 3 instances have the same view of the global state? Or maybe Partition Tolerance where it's vital for each instance need to be able to predict the intended action even if it can't communicate to the others? Whichever 2 you choose will dictate engineering choices in the pursuit of a great HA solution. ### The roadmap With FlowFuse v1.4, released February 2023, a 5 minute mean time to recovery is achieved for all flows running locally, that is: in the cluster. Going beyond this milestone requires your input! I'd love to chat about your challenges, please [pick a timeslot to discuss your requirements](https://meetings-eu1.hubspot.com/zeger-jan){rel=""nofollow""}! # MING Stack for IoT The folks at Balena have created a bundle they call the [MING stack](https://hub.balena.io/organizations/marc6/apps/MING){rel=""nofollow""}, (Mosquitto/MQTT, InfluxDB, Node-RED and Grafana) which first appeared back in [2019](https://forums.balena.io/t/ming-an-iot-sensor-stack-mosquitto-influxdb-nodered-grafana/36540){rel=""nofollow""}. It is an interesting way to look at the IoT tech stack and a pattern I have seen used many times. In the early days of the web, the [LAMP stack](https://en.wikipedia.org/wiki/LAMP_\(software_bundle\)){rel=""nofollow""} was popularized as being the open source tech stack for hosting web sites. First used in 1998, LAMP stood for Linux, Apache, MySQL and Python/Perl/PHP. The LAMP stack did a lot to popularize open source software and create architecture patterns for building web applications. The LAMP stack did a lot to simplify a confusing technology landscape back in the early days of the web. Can the IoT industry benefit from a MING stack? There is a fair amount of complexity building IoT systems. Therefore, having defined architecture patterns might help reduce some of the confusion. In fact, MING does bring together the key open source components of an IoT system: - [Mosquitto](https://mosquitto.org/){rel=""nofollow""} is the popular open source MQTT broker. MQTT has become the default protocol for IoT communications. The MQTT pub/sub protocol solves a lot for the communication challenges for IoT applications. In fact, I would define the M as being MQTT since there are a lot of MQTT broker implementations available. - [InfluxDB](https://www.influxdata.com/){rel=""nofollow""} is the popular open source time series database. Many IoT use cases are based on analyzing trends from different IoT devices. The classic examples is preventive maintenance of factory equipment. Having a time series database in your IoT architecture to record trending information will solve a lot of your data problems. - [Node-RED](https://flowfuse.com/node-red/) is the popular low-code development environment that helps create flows of data. IoT systems are often pulling data from many different sources. The data needs to be filtered, analysed or transformed before being forward to another service. Node-RED has a large community of data nodes that makes it easy to collect data from a wide variety of sources. For instance in the industrial world, Node-RED nodes are available for OPC-UA, Modbus, S7, MQTT, various PLC platforms like Opto22, etc, etc. - Finally [Grafana](https://grafana.com/){rel=""nofollow""} is a popular open source visualization platform. Real time monitoring of IoT data is often the first applications deployed for IoT systems. Having graphing and dashboard technology available in your architecture makes perfect sense. Another important feature of MING is that it can be deployed on the edge or in the cloud. IoT systems are inheritenty distributed so having the same technology available at different tiers is useful for lower barriers to adoption. The LAMP stack was successful because it was: - Open source and freely available for anyone to adopt and use. - Highly flexible and customizable that allowed developers to adopt the stack to their use case. - Back by large developer communities creating plugins/extensions, documentation, tutorials, etc. - Easy to learn for developers with limited experience. The MING stack has all the same characteristics. All four technologies are open source, highly flexible, back by large developer communities and relatively easy to learn. There are a lot of similarities between MING and LAMP. Is MING relevant for IoT use cases? In my experience, people building IoT solutions are using 3-4 of the MING components. What is your experience? ## Simplify IoT Complexity with FlowFuse’s MIND Stack If you’re concerned about managing multiple components in your IoT stack, FlowFuse has you covered. By bundling three of the four core elements of the MING stack, MQTT, Node-RED, and Grafana. FlowFuse simplifies your setup and management. This streamlined approach means fewer moving parts and less complexity, allowing you to focus on what matters most: your data and your applications. With the FlowFuse [MIND stack](https://flowfuse.com/blog/2024/05/node-red-mind-stack-with-flowfuse/), you get a unified platform that integrates MQTT for efficient communication, Node-RED for seamless integration and data transformation, and [Dashboard 2.0](https://flowfuse.com/platform/dashboard/) for powerful visualizations, all from one place. This integration reduces configuration and maintenance overhead while ensuring consistent performance and security across your deployments. Explore the MIND stack now. **Want an easier, more secure, and scalable IoT stack? Start your [free trial](https://app.flowfuse.com/account/create){rel=""nofollow""} of FlowFuse today.** # Service Disruption Report for January 27th, 2023 On January 27th, 2023, we were alerted to an issue on FlowFuse Cloud where a user was not able to access a newly created Node-RED Project, receiving a 404 error instead. This post examines the issue that was hit, the timeline of events and what we've done to resolve it. ## Summary We hit a limit in the AWS Load Balancer that capped how many projects could be exposed to the internet within our FlowFuse Cloud deployment. The result of this was that users could create a new Node-RED project, but they would not be able to access the editor. We freed up capacity on the platform to allow user projects to be created without hitting the limit, whilst also asking AWS to increase the limit in question which they duly did. However, we later discovered a second limit that was also being applied. That limit was not one AWS permits us to change. We successfully completely deployment of a change to our platform architecture today that removes these limits from our environment. In total, this lead to approximately 2 hours of disruptions on January 27th 2023 and again on February 8th 2023 during which newly created Node-RED projects were not accessible. Our logs show that two users were impacted during these times. ## Technical Details When running FlowFuse within a Kubernetes environment, each project creates a new Ingress Object configuration to tell the platform how to route HTTP traffic to that project. Our FlowFuse Cloud deployment runs within Amazon Elastic Kubernetes Service (EKS) and uses the Application Load Balancer (ALB) service as its ingress controller. When the FlowFuse platform creates the new Ingress Object configuration, EKS passes that to ALB to generate the necessary configuration, which, given the configuration we were using, created both a Target Group and Rule object. With a default of limit of 100 Target Groups and Rules, that meant we had a technical limit of 100 Node-RED projects within the FlowFuse Cloud environment. Increasing the Rule limit did not solve the problem as the Target Group limit still applied. Our initial mitigation was to delete any Node-RED projects we had created for our own internal testing. We also identified that we could safely delete any rules for suspended projects. A suspended project is one that is not actively running in the platform. The code that resumes a suspended project would recreate any ingress objects needed - so deleting the rule whilst suspended would not have any impact on the project. This gave us a small amount of headroom on the platform which crucially meant we had time to develop a longer term solution. ## Resolution There were two possible routes we could take to resolve this issue: - Investigate how to reuse existing Target Groups rather than create one for each project :br Our intial research was inconclusive on how to achieve this. The AWS docs weren't clear enough to give us a definitive answer we felt comfortable to invest our time in. - Move away from ALB in favour of nginx to provide our ingress load balancing. :br This was always our long term strategy as it was a prerequisite to FlowFuse features we have in the roadmap such as providing custom domains to projects. However it was potentially a large piece of work with a complicated migration for the existing environment. Given it fitted with our longer-term strategic goals, we decided to move ahead with replacing ALB with nginx. After some initial development work and experimentation, we felt comfortable that the migration was not as complicated as initially feared. We would have to manually copy the existing ALB rules over - something that could be scripted. Once we had nginx deployed we could push a small code change to FlowFuse to use it rather than ALB, and also switch over the DNS entry to point at nginx. Following a successful run through in our testing/staging environment, we decided to move ahead updating the production environment. This change was applied today, whilst we closely monitored the system to ensure no further disruption occurred. We have validated that new projects can be created without issue and everything is working as it should. ## Next Steps With FlowFuse Cloud updated to use the new load balancer, we'll be closely monitoring it over the next few days to ensure it operates normally. We will also be taking on some follow-up activities to minimise the risk of this type of issue happening again: 1. Review all AWS limits within our architecture. Identify any that pose a potential issue in the future. Ensure they are documented and a plan put in place to mitigate the impact based on our expected platform growth. 2. Add additional external monitoring for project liveness. 3. Review all logging around k8s apis ## Timeline *All times are GMT.* **2023-01-27 22:15** : (Friday evening) Customer reports via our support channel a newly created project was not accessible and returning a 404 error. **2023-01-27 22:22** : We start examining the platform logs **2023-01-27 22:47** : We identify we've hit the default Rules limit on the AWS Application Load Balancer **2023-01-27 23:15** : To free-up capacity on the platform we delete any ununsed internal projects we were using for general testing. We also identify we can safely delete any rules associated with suspended projects. **2023-01-27 23:41** : We complete deleting rules to give us enough head-room to see us through the weekend. **2023-01-30 10:00** : We submit a request to AWS to increase the rule limit to 200 which is accepted and actioned later that day. ... **2023-02-08 14:05** : Another customer reports seeing a newly created project returning a 404 error. We examine the ALB configuration and whilst it reports the new limit has been changed to 200, it appears to still be limiting at 100. We start identifing more suspended projects we can delete the rules for to free up capacity. **2023-02-08 14:20** : Sufficient capacity is freed to enable the customer's projects to be accessible. **2023-02-08 15:00** : We identify we've hit the ALB Target Group limit. This is a hard limit that AWS does not allow you to change. We begin researching options. **2023-02-09** : Commited to plan to replace ALB with nginx. Successful migration of our staging environment. **2023-02-10** : Change applied to production, FlowFuse 1.3.3 deployed and DNS updated to use the new load balancer. # Some questions we didn't get to in our first webinar There were some great questions from our [first webinar](https://www.youtube.com/watch?v=47EvfmJji-k){rel=""nofollow""} that we didn't get time to answer, we wanted to share those questions and our answers here. ### Irvin asks, 'is it possible to save the debug information or data flow to a storage like Splunk or SQL'? Hi Irvin, thanks for the question. I suspect you might be better using a custom node which is designed for logging data rather than capturing the debug node content to a database. I've come across [Flogger](https://flows.nodered.org/node/node-red-contrib-flogger){rel=""nofollow""} which seems to do a good job of logging, including support for multiple log files, and built in support for log rotation. :video{ariaLabel="Capturing debug to a log file using Flogger" autoPlay="true" height="580" loop="true" muted="true" playsInline="true" preload="none" width="1470"} If you really wanted to log to a database rather than a log file you could create your own logging subflow. Once that's in place you can drop it into your flow as needed to capture your debug data for later consumption. ### Anonymous asks 'can we make an mobile application with Node-RED or can the content only be accessed through a web browser'? Hello Anon', it would be great if a Node-RED flow could be built into a mobile app. Sadly, there isn't a simple way to do so at the time of writing. Assuming you want an easy way to package up and distribute the functionality you might be best creating a link to where the application is hosted. Both [iOS](https://www.macrumors.com/how-to/add-a-web-link-to-home-screen-iphone-ipad/){rel=""nofollow""} and [Android](https://www.androidauthority.com/add-website-android-iphone-home-screen-3181682/){rel=""nofollow""} support making a home icon. Once added using them is basically the same user experience as a locally installed application. ### John asks 'From the random node example in the webinar, the node connected to four different nodes. Is there an order to which is invoked first? Can that be controlled'? Hi John, interesting question, thanks for sending it in. For the sake of any readers who were not an the webinar here is what you are describing. !["Image showing the Flow where the random number generator sends a message to 4 nodes at the same time"](https://flowfuse.com/blog/2023/02/images/chart-flow.png "Image showing the Flow where the random number generator sends a message to 4 nodes at the same time") All downstream nodes linked to the same prior node will be triggered at practically the same time. You could use a delay node if you want to ensure a particular node is triggered first. It might also make sense to wire your flow in series rather than parallel. This would allow your functions to all execute in a specific order. That being said, in this case all but one of the nodes do not have outputs so using delays might be the only practical option. ### Abdelhamid asks, 'How can I delete a subflow'? Thanks Abdelhamid, that's actually really easy to do. Double click the subflow you want to delete, then select 'delete subflow' from the top of your workspace. !["Image showing how to delete a subflow"](https://flowfuse.com/blog/2023/02/images/delete-subflow.png "Image showing how to delete a subflow") Thanks again to everyone who attended and participated in our first webinar. We have lots of other useful live content coming up soon, you can view and register for future events on our website's [webinars page](https://flowfuse.com/webinars/). # Node-RED Tips - Exec, Filter, and Debug There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows. ### 1. The Exec node allows you to interact with BASH from Node-RED Exec allows you to run Shell commands and receive the value back into your flow. This opens up almost any command which can be run on the host devices CLI to your Node-RED flows. :video{ariaLabel="Example flow using the Exec node" autoPlay="true" height="220" loop="true" muted="true" playsInline="true" preload="none" width="810"} ### 2. The Filter node helps you discard duplicate messages It can be useful to only allow messages to proceed through a flow where their value is unique. Filter makes that task simple, no need to store the past values and check each new message against a list. ![Configuring the Filter node to only allow unique payloads through](https://flowfuse.com/blog/2023/03/images/filter-config.png "Configuring the Filter node to only allow unique payloads through") Once your filter is configured as shown above, try sending different payloads through to see the outcome. :video{ariaLabel="Demonstration showing the Filter node" autoPlay="true" height="194" loop="true" muted="true" playsInline="true" preload="none" width="802"} ### 3. Counting the amount of messages sent to a Debug node The Debug node has a lot of great features that we don't see used that often. One example is the ability to show a count of how many messages have been sent to that Debug node since the last deploy. ![Setting up the debug to count messages](https://flowfuse.com/blog/2023/03/images/setup-counting-debug.png "Setting up the debug to count messages") Once you've setup the node as shown above, you will see a counter under the debug. :video{ariaLabel="Each message sent to the debug node is counted" autoPlay="true" height="380" loop="true" muted="true" playsInline="true" preload="none" width="916"} We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). ## Effortless Communication Between Node-RED Instances with FlowFuse Project Nodes Managing communication between multiple Node-RED instances can be a complex task, but FlowFuse [Project Nodes](https://flowfuse.com/docs/user/projectnodes/) simplify this process dramatically. With these nodes, you can easily send messages between different Node-RED instances without worrying about complex configurations or network setup. All you need to do is select the target instance by name, and FlowFuse takes care of the rest. This makes it faster and more efficient to handle multi-instance environments, ensuring seamless communication between flows across different devices or locations. Whether you're managing multiple environments or working on large-scale projects, FlowFuse Project Nodes save you time and reduce the risk of errors. FlowFuse continues to innovate, making collaboration and scalability in Node-RED projects even easier. To learn more about these features, check out the [FlowFuse website](https://flowfuse.com). # Node-RED Tips - Smooth, Catch, and Math There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows. ### 1. Use the Smooth node to get the minimum and maximum values of your payloads When taking data in from sensors sometimes a spurious value can be sent into your flow. This can result in oddities in a graph or even misfiring of actions such as turning on a heating system. The Smooth custom node allows you to store the min and max of a payload for the last few messages received. ![Using the Smooth node to return highest value from the last 100 payloads](https://flowfuse.com/blog/2023/03/images/smooth.png "Using the Smooth node to return highest value from the last 100 payloads") :cta-image{alt="Walk through your FlowFuse setup with our team - book a demo" cta="demo" src="https://flowfuse.com/images/cta/book-a-demo.png"} You can in turn use this to ignore values that deviate too far from the sample. To help demonstrate the Smooth node, I've created a flow you can import into Node-RED. ```json [{"id":"9484c25a0120bd48","type":"group","z":"dd95c0bca1101c86","name":"Automatically outputs random value (temperature in Celcius) between 0 & 25 every second","style":{"label":!0},"nodes":["e4f972f9daad6246","c7fc075a1915e87b","966a772c46dc2888"],"x":34,"y":59,"w":574,"h":82},{"id":"e4f972f9daad6246","type":"link out","z":"dd95c0bca1101c86","g":"9484c25a0120bd48","name":"link out 1","mode":"link","links":["33594c64783cdc45","e6bf7b494b48861e"],"x":355,"y":100,"wires":[]},{"id":"c7fc075a1915e87b","type":"inject","z":"dd95c0bca1101c86","g":"9484c25a0120bd48","name":"","props":[],"repeat":"1","crontab":"","once":!1,"onceDelay":0.1,"topic":"","x":130,"y":100,"wires":[["966a772c46dc2888"]]},{"id":"966a772c46dc2888","type":"random","z":"dd95c0bca1101c86","g":"9484c25a0120bd48","name":"","low":"0","high":"25","inte":"true","property":"payload","x":260,"y":100,"wires":[["e4f972f9daad6246"]]},{"id":"37380f26e8bfc98a","type":"group","z":"dd95c0bca1101c86","name":"Calculate average, high and low, save to flow","style":{"label":!0},"nodes":["cc3978c7c4ea56ed","e1819526a5f365c8","33594c64783cdc45","fea261a15b3b7683","e28a69232f1cac53","aecb1727be523240","e30039ee13e480a8"],"x":34,"y":259,"w":552,"h":142},{"id":"cc3978c7c4ea56ed","type":"change","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"","rules":[{"t":"set","p":"high","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":!1,"x":310,"y":300,"wires":[["aecb1727be523240"]]},{"id":"e1819526a5f365c8","type":"change","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"","rules":[{"t":"set","p":"low","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":!1,"x":310,"y":360,"wires":[["e30039ee13e480a8"]]},{"id":"33594c64783cdc45","type":"link in","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"link in 1","links":["c07b2e101cecbd3b","e4f972f9daad6246"],"x":75,"y":320,"wires":[["e28a69232f1cac53","fea261a15b3b7683"]]},{"id":"fea261a15b3b7683","type":"smooth","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"Min","property":"payload","action":"min","count":"100","round":"2","mult":"single","reduce":!1,"x":170,"y":360,"wires":[["e1819526a5f365c8"]]},{"id":"e28a69232f1cac53","type":"smooth","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"Max","property":"payload","action":"max","count":"100","round":"2","mult":"single","reduce":!1,"x":170,"y":300,"wires":[["cc3978c7c4ea56ed"]]},{"id":"aecb1727be523240","type":"debug","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"debug 23","active":!0,"tosidebar":!1,"console":!1,"tostatus":!0,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":480,"y":300,"wires":[]},{"id":"e30039ee13e480a8","type":"debug","z":"dd95c0bca1101c86","g":"37380f26e8bfc98a","name":"debug 25","active":!0,"tosidebar":!1,"console":!1,"tostatus":!0,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":480,"y":360,"wires":[]},{"id":"ec11a9ee9148b0b5","type":"group","z":"dd95c0bca1101c86","name":"Evaluate if an incoming value is between flow.high and flow.low, if it is not, send the message down a different wire and show an alert in debug","style":{"label":!0},"nodes":["9393c22b2e3c1ec8","1cd18307cb919159","7c1f474e646765a7","a209cd10f33ec401","f4b15283e55babf4","e6bf7b494b48861e"],"x":34,"y":419,"w":1032,"h":162},{"id":"9393c22b2e3c1ec8","type":"switch","z":"dd95c0bca1101c86","g":"ec11a9ee9148b0b5","name":"Was the value between flow.high and flow.low?","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"high","vt":"flow","v2":"low","v2t":"flow"},{"t":"else"}],"checkall":"true","repair":!1,"outputs":2,"x":300,"y":480,"wires":[["1cd18307cb919159"],["7c1f474e646765a7","a209cd10f33ec401"]]},{"id":"1cd18307cb919159","type":"debug","z":"dd95c0bca1101c86","g":"ec11a9ee9148b0b5","name":"debug 15","active":!0,"tosidebar":!0,"console":!1,"tostatus":!1,"complete":"false","statusVal":"","statusType":"auto","x":560,"y":460,"wires":[]},{"id":"7c1f474e646765a7","type":"debug","z":"dd95c0bca1101c86","g":"ec11a9ee9148b0b5","name":"debug 16","active":!1,"tosidebar":!0,"console":!1,"tostatus":!1,"complete":"false","statusVal":"","statusType":"auto","x":560,"y":540,"wires":[]},{"id":"a209cd10f33ec401","type":"change","z":"dd95c0bca1101c86","g":"ec11a9ee9148b0b5","name":"Alert to debug when value is outside of the range","rules":[{"t":"set","p":"payload","pt":"msg","to":"The value was outside of the range","tot":"str"}],"action":"","property":"","from":"","to":"","reg":!1,"x":690,"y":500,"wires":[["f4b15283e55babf4"]]},{"id":"f4b15283e55babf4","type":"debug","z":"dd95c0bca1101c86","g":"ec11a9ee9148b0b5","name":"debug 17","active":!0,"tosidebar":!0,"console":!1,"tostatus":!1,"complete":"false","statusVal":"","statusType":"auto","x":960,"y":500,"wires":[]},{"id":"e6bf7b494b48861e","type":"link in","z":"dd95c0bca1101c86","g":"ec11a9ee9148b0b5","name":"link in 2","links":["e4f972f9daad6246","c07b2e101cecbd3b"],"x":75,"y":480,"wires":[["9393c22b2e3c1ec8"]]},{"id":"caf4214602d5f2c9","type":"group","z":"dd95c0bca1101c86","name":"Manually send a spurious value","style":{"label":!0},"nodes":["14097fb7ba3a9ecc","c07b2e101cecbd3b"],"x":34,"y":159,"w":232,"h":82},{"id":"14097fb7ba3a9ecc","type":"inject","z":"dd95c0bca1101c86","g":"caf4214602d5f2c9","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":!1,"onceDelay":0.1,"topic":"","payload":"75","payloadType":"num","x":130,"y":200,"wires":[["c07b2e101cecbd3b"]]},{"id":"c07b2e101cecbd3b","type":"link out","z":"dd95c0bca1101c86","g":"caf4214602d5f2c9","name":"link out 2","mode":"link","links":["33594c64783cdc45","e6bf7b494b48861e"],"x":225,"y":200,"wires":[]}] ``` ### 2. Perform simple maths functions using JSONata in Change nodes You can perform basic maths functions using the Change node and JSONata. Let's say you wanted to take a payload and multiply it by a value. You could use a custom node such as [node-red-contrib-calc](https://flows.nodered.org/node/node-red-contrib-calc){rel=""nofollow""} but you can also easily complete the same task within a change node. ![Using JSONata in a Change node to multiply a payload by 2.5](https://flowfuse.com/blog/2023/03/images/jsonata.png "Using JSONata in a Change node to multiply a payload by 2.5") This will take the input payload, multiply it by 2.5 then output it as the new payload. You can try this out using the code below. ::render-flow ```json [{"id":"6bbe9c1e81c4ee39","type":"inject","z":"cfe9fec308e144db","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2","payloadType":"num","x":350,"y":400,"wires":[["07aa636f3db17775"]]},{"id":"07aa636f3db17775","type":"change","z":"cfe9fec308e144db","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"msg.payload * 2.5","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":440,"wires":[["8bde558e6e2f8551"]]},{"id":"8bde558e6e2f8551","type":"debug","z":"cfe9fec308e144db","name":"debug 26","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":680,"y":440,"wires":[]},{"id":"1c04633997beb150","type":"inject","z":"cfe9fec308e144db","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"num","x":350,"y":440,"wires":[["07aa636f3db17775"]]},{"id":"bc52c3d2f38115b1","type":"inject","z":"cfe9fec308e144db","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"num","x":350,"y":480,"wires":[["07aa636f3db17775"]]}] ``` :: ### 3. Use the Catch node to trigger flows on errors Sometimes you might be working with nodes which don't output anything when they error or maybe output text directly to debug. This makes it difficult for you to run flows when something fails. For example, when using the Read File node, where the expected file is not found, it would be useful to be able to run a specific flow which sends an alert. You can do this using the Catch node. Drop the node onto your workspace then select if you want errors from some or all nodes. For this example I am going to select just the Read File node. If I then rerun the flow I get an error message out of the Catch node every time there is an error with reading the file. :video{ariaLabel="Catching an error from the Read File node and outputting a message to debug" autoPlay="true" height="234" loop="true" muted="true" playsInline="true" preload="none" width="836"} Note that there are no wires connecting the flow to the error output. This means you can have a single Catch node monitoring a whole project and logging errors as well as sending alerts as needed. You can import the flows from this example using the code below. ::render-flow ```json [{"id":"d6399c6fddb572ef","type":"debug","z":"0c6a2ba248b5933f","name":"debug 28","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1100,"y":300,"wires":[]},{"id":"de70fda720070c57","type":"inject","z":"0c6a2ba248b5933f","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":790,"y":260,"wires":[["a18f9c8638c78e57"]]},{"id":"a18f9c8638c78e57","type":"file in","z":"0c6a2ba248b5933f","name":"","filename":"example.txt","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":930,"y":260,"wires":[[]]},{"id":"2dbb0cc4bc10d0bc","type":"catch","z":"0c6a2ba248b5933f","name":"","scope":["a18f9c8638c78e57"],"uncaught":false,"x":790,"y":300,"wires":[["b22988df6357a52a"]]},{"id":"b22988df6357a52a","type":"change","z":"0c6a2ba248b5933f","name":"Debug message","rules":[{"t":"set","p":"payload","pt":"msg","to":"There was an error reading the file","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":940,"y":300,"wires":[["d6399c6fddb572ef"]]}] ``` :: We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). ### Simplifying Multi-Instance Communication with FlowFuse Project Nodes Coordinating communication between multiple Node-RED instances can be challenging, but FlowFuse's [Project Nodes](https://flowfuse.com/docs/user/projectnodes/) make it effortless. With these nodes, you can seamlessly send messages between instances without dealing with complicated network configurations. Simply choose the target instance by name, and FlowFuse handles the connection automatically. This streamlines the management of multi-instance environments, ensuring smooth communication between flows across different devices or locations. Whether you're handling multiple projects or managing large-scale systems, FlowFuse Project Nodes help you save time and minimize errors. FlowFuse continues to push the boundaries of collaboration and scalability in Node-RED projects. For more details on these features, visit the [FlowFuse website](https://flowfuse.com). # Node-RED Tips - Importing, Exporting, and Grouping Flows There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows. ### 1. Copy and share your flows using Export and Import Node-RED provides both import and export features that allow you to save your flows and settings as compressed JSON files. You can use these features to easily move your flows between multiple Node-RED instances or to back up your work. To import a flow, follow these steps: 1. Click on the three horizontal lines in the upper right corner of the Node-RED editor and click on "Import". 2. Select the JSON file that contains the flow you want to import. 3. Click on "Import" and the flow will be imported into the current instance of Node-RED. To export a flow, follow these steps: 1. Click on the three horizontal lines in the upper right corner of the Node-RED editor and click on "Export". 2. Select the type of export you want to perform - this can be either the "Clipboard" or "File". 3. If you chose "File" you will be prompted to save a compressed JSON file to your computer; if you chose "Clipboard" the flow JSON will be copied to your clipboard. 4. Use the exported file or clipboard content to import into a different instance of Node-RED. :video{ariaLabel="Importing and exporting your flows" autoPlay="true" height="474" loop="true" muted="true" playsInline="true" preload="none" width="978"} Keep in mind that some nodes or flows may require additional setup or node installation on the Node-RED instance you import your flow to. ### 2. Import helpful example flows provided with custom nodes In Node-RED, custom nodes can include examples that provide users with a starting point for using the node. These examples can help users understand how a node works and how it can be integrated into their flows. To use example flows in custom nodes, follow these steps: 1. Open the Node-RED editor and drag the custom node you want to use into your flow. 2. Double-click on the node to open its configuration panel. 3. Look for an "Examples" menu or button within the node configuration panel. The name and location of the Examples button can vary depending on the node. 4. Click on the "Examples" button to bring up a list of example flows included with the node. 5. Select the example flow you want to use and click on "Import" to add the example flow to your Node-RED workspace. Once the example flow has been added to your workspace, you can modify it to fit your specific needs. :video{ariaLabel="Using the example flow included in the moment node" autoPlay="true" height="474" loop="true" muted="true" playsInline="true" preload="none" width="978"} It's important to note that while custom node examples can be a useful starting point, they may not always work seamlessly with your other flows or nodes. Be sure to thoroughly test any custom node examples before incorporating them into a production environment. ### 3. Group nodes together to make your flows easier to read The group feature in Node-RED allows users to visually group nodes together within the workspace. This feature offers several benefits: 1. Improved organization: The group feature allows you to group related nodes visually, which can make your flow easier to understand and navigate. This can be particularly helpful for larger, more complex flows. 2. Simplified editing: When you group nodes together, you can edit or move them as a single unit, rather than individually. This can save time and reduce the chance of errors. 3. Easier sharing: When you share your flow with others, the group feature allows you to package related nodes together, making it easier for others to understand and use your flow. 4. Reduced clutter: Grouping nodes can help reduce the visual clutter in your workspace, making it easier to focus on key aspects of your flow. :video{ariaLabel="Grouping your nodes to make them easier to read" autoPlay="true" height="474" loop="true" muted="true" playsInline="true" preload="none" width="978"} Overall, the group feature in Node-RED is a valuable tool that can help users better organise, edit, and share their flows. We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). You can read our previous Node-RED tips here. [Node-RED Tips - Smooth, Catch, and Math](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-4/):br[Node-RED Tips - Exec, Filter, and Debug](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-3/):br[Node-RED Tips - Deploying, Debugging, and Delaying](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-2/):br[Node-RED Tips - Wiring Shortcuts](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-1/) ## Version Control and Collaboration for Your Node-RED Flows [FlowFuse](https://flowfuse.com) simplifies managing and collaborating on your Node-RED flows with seamless version control. You can easily track changes, take snapshots, and revert to previous versions, ensuring your work is always safe and recoverable. With FlowFuse [Team Library](https://flowfuse.com/docs/user/shared-library/#shared-team-library), sharing flows across different Node-RED instances is effortless. This Library feature allows you to organize and share flows among team members without the need for manual copying, making collaboration more efficient and effective. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} for a free trial today and discover how FlowFuse can enhance your Node**\* # Community News March 2023 Welcome to the FlowFuse newsletter for March 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. ## Upcoming events ### Node-RED Ask Me Anything Back by popular demand, FlowFuse is hosting a monthly Node-RED Ask Me Anything session on March 9th. This is a great opportunity to ask Nick O’Leary, co-creator of Node-RED & FlowFuse CTO, and Rob Marcer, Node-RED FlowFuse Developer Educator your questions about Node-RED. ### DevOps for Node-RED: An Introduction to FlowFuse Webinar Join Nick O'Leary, FlowFuse CTO, as he presents an Introduction to FlowFuse and demonstrates FlowFuse’s platform for providing DevOps for Node-RED. This webinar will be on March 30th. [Register today](https://flowfuse.com/webinars/2023/introduction-to-flowforge/). ## From our Blog [Toward Highly Available Node-RED](https://flowfuse.com/blog/2023/02/highly-available-node-red/) - High availability is an often requested feature for Node-RED. This post from FlowFuse CEO discusses our approach to HA for Node-RED. [MING Stack for IoT](https://flowfuse.com/blog/2023/02/ming-blog/) - MING technology stack include M (Mosquitto/MQTT), InfluxDB, Node-RED and Grafana. Ian Skerrett, FlowFuse Head of Marketing discusses how this tech stack is used for IoT. [Introduction to Node-RED](https://www.youtube.com/watch?v=47EvfmJji-k){rel=""nofollow""} - An in-depth webinar recording on key Node-RED concepts and demonstration on how to get started with Node-RED. Node-RED Quick Tips - Rob Marcer, FlowFuse Developer Educator has a weekly series of Node-RED hints and tips - [Tips #1](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-1/) - [Tips #2](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-2/) ## From the Community ### Node-RED Community Survey The Node-RED open source project is running an Node-RED Community Survey. Give your [feedback on how you are using Node-RED](https://nodered.org/blog/2023/02/23/community-survey){rel=""nofollow""}. ### Good alternatives to Pis for your next project Raspberry Pis continue to be difficult to purchase. Eben Upton of the Raspberry Pi Foundation has said that [supply should improve this year](https://www.raspberrypi.com/news/supply-chain-update-its-good-news/){rel=""nofollow""} but in the mean-time there are some good alternatives you could consider. The Youtube channel [ExplainingComputers](https://www.youtube.com/@ExplainingComputers){rel=""nofollow""} has shared a [great video covering some of the most popular SBCs](https://www.youtube.com/watch?v=k8clrUclPIs){rel=""nofollow""} you could use for your next project, it’s worth a watch. ### Custom Node Spotlight - node-red-contrib-os [OS](https://flows.nodered.org/node/node-red-contrib-os){rel=""nofollow""} is a great custom node which allows you to monitor the performance of the device you are running Node-RED on. It can check RAM usage, disk space, CPU load, and a lot more. It’s really easy to use, we recommend you [take a look](https://flows.nodered.org/node/node-red-contrib-os){rel=""nofollow""}. ## Join Our Team FlowFuse is expanding our team. We have two openings right now: - **[Developer Advocate - Manufacturing & Industrial Automation](https://boards.greenhouse.io/flowfuse/jobs/4798023004){rel=""nofollow""}** - **[DevOps Engineer](https://boards.greenhouse.io/flowfuse/jobs/4796271004){rel=""nofollow""}** # Comparing Node-RED Dashboards Solutions Dashboards are a great feature of Node-RED, allowing you to easily expose data visualisations and interactive elements of your flows to users via a web browser. I often see discussions in the community about which dashboard option is best for any given scenario, I wanted to compare the most popular options as they stand in early 2023. ::div{.blog-update-notes} **UPDATE:** Since this article was published, it's worth noting a couple of important updates: - FlexDash is no longer maintained and supported. - [Node-RED Dashboard 2.0](https://dashboard.flowfuse.com) has been released, which is a new, modern dashboard stack for Node-RED, and offers all of the benefits of the original "Node-RED Dashboard", plus more. :: ## Which dashboards am I going to consider? Based on their downloads per week and active development, I believe there are 3 main dashboards worth considering. In no particular order, they are [Dashboard](https://flows.nodered.org/node/node-red-dashboard){rel=""nofollow""}, [uibuilder](https://flows.nodered.org/node/node-red-contrib-uibuilder){rel=""nofollow""}, and [FlexDash](https://flows.nodered.org/node/@flexdash/node-red-fd-corewidgets){rel=""nofollow""}. It's not to say that there are not other options, I am focusing on the dashboards I believe are popular in the Node-RED community. I'd like to take this opportunity to thank the project leads for each of the three dashboards for responding to me and providing their take on the current state and future development of each. Where possible I have quoted their words, either from their messages to me or from the projects' documentation. Thanks to [Dave](https://github.com/dceejay){rel=""nofollow""}, [Julian](https://github.com/TotallyInformation){rel=""nofollow""}, and [Thorsten](https://github.com/tve){rel=""nofollow""} for their replies as well as all the work they've put into these great projects! ## Methodology To compare these dashboards, I am going to consider each of them based on the following factors: - How easy is it to install? - How easy is it to get your first demo dashboard running? - How extensive is the collection of UI elements? - How good is the support and documentation? - How 'cloud native' is the dashboard? - How active is each project's development? - What are the future development plans? I am assuming the user is a low-code developer. They may have limited experience with coding and are most comfortable working in visual interfaces. So, that's the methodology, let's get on with looking at the strengths of each project. :cta-image{alt="Wenco deploys new dashboard pages in days with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/wenco-book-demo.png"} ## How easy is it to install? ### uibuilder - 1st place A search on Google for uibuilder returns the correct custom node. When searching for the custom node in the palette manager there is only one result, this is great as users are very likely to install what they were searching for. Once you've found the correct custom node, the installation takes just a few moments using the palette manager. ### Dashboard - 2nd place As Dashboard is currently the most popular solution to build dashboards in Node-RED, it's very easy to find both in search engines and in the Node-RED interface. A Google search brings up the correct custom node. Finding this custom node in Node-RED's palette manager is not quite as easy, at the time of writing it's the third from top result for the search term 'dashboard'. Some users might not select the intended item from the palette manager on first attempt. However, once you have found the correct custom node, installation is easy and takes just a few moments. ### FlexDash - 3rd place When searching for 'FlexDash node red' on Google, the top result is the Node-RED website for the custom node. The issue with this, and this is also a problem when searching in the palette manager, is the project 'FlexDash' is apparently not what we actually need to install. When reading the readme for the project on Github it says *'You most likely do not want to explicitly install this package, you want to install the [core widgets](https://github.com/flexdash/node-red-fd-corewidgets){rel=""nofollow""}, which will bring in this package and more and will provide a usable whole'.* Credit to the developers for adding in this helpful text but I suspect most users will start off by installing FlexDash then later discover that was not the correct way to proceed. It would be great if the custom node which needs to be installed was the one called 'FlexDash' in my opinion. This problem is compounded by there being no help file at all for 'FlexDash' showing up on the Node-RED web site. That may well be a deliberate attempt to help users get the right custom node installed, but it was still a confusing start for me and I suspect other users will have a similar experience. When setting up FlexDash, one thing that wasn't immediately obvious was that I needed to restart Node-RED before the custom node showed in the palette. This step is [covered in the docs](https://flexdash.github.io/docs/quick-start/#installing-flexdash-in-node-red){rel=""nofollow""} but I suspect a lot of users will get stuck working out why the palette manager says the custom node is installed but nothing new has been added to the palette. There is also an [ongoing discussion](https://github.com/node-red/node-red/issues/569){rel=""nofollow""} about a way to resolve issue by changing how Node-RED deals with dependencies which sounds promising. I believe a few improvements to the install process could make FlexDash a much more popular custom node. ## How easy is it to get your first demo dashboard running? ### FlexDash - 1st place Getting an example dashboard up and running in FlexDash is very easy thanks to the example flows which are included in the package. Simply go to 'Import', 'Examples' then select 'Hello-world' from the example flows. Now deploy and add /flexdash to the end of the URL of your Node-RED editor and you should have your first dashboard running. ### uibuilder - 2nd place It was quite simple to get an example dashboard up and running in uibuilder. As with FlexDash, there are examples you can import. Once we import an example we do start to see the significantly different approach to delivering dashboards with uibuilder to the other two solutions. The examples seem to demonstrate how you could build a dashboard rather than showing specific UI elements such as charts in use. ### Dashboard - 3rd place Getting your first dashboard running in Dashboard is quite easy, once installed you need to drag in a Dashboard UI element then assign that to a UI group and tab. The group and tab can be left as their default options (home) which I suspect most users will work out quickly. You then need to deploy your flow and visit the dashboard using '/ui' on the end of the URL of your Node-RED editor and you are up and running. Dashboard would benefit from some example flows as we see with the other two custom nodes. ## How extensive is the collection of UI elements? ### FlexDash and Dashboard - joint 1st place It's really hard to separate these two, when considering the UI elements they come with. They both have out of the box solutions for charts, gauges, buttons, drop downs, toggles, text etc. I think they both deserve 1st place in this category. ### uibuilder - 3rd place This is possibly a little unfair on uibuilder. Arguably by design, uibuilder does not currently include many UI elements. To add most useful elements (charts, gauges etc) to your dashboard, you will need to set out your design in HTML or look at using one of the supported frontend frameworks. This makes uibuilder more versatile for users who are comfortable using code to set out dashboards but for the low-coders among us it's less ideal. ## How good is the support and documentation? ### All three - joint first place All three projects have an active community and good support documentation. Where as I may have a personal preference about how I like documentation to be set out, I don't think that makes any one project better than the rest. ## How 'cloud native' is the dashboard? In the words of the [Cloud Native Computing Foundation](https://www.cncf.io/){rel=""nofollow""} '*Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach*'. '*These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil*'. So, how well does each project conform to these ideals? ### Dashboard - 1st place Dashboard stores all configuration data within the Node-RED instance. When deploying an existing Node-RED project to a new instance everything just works exactly as it did previously. ### FlexDash - 2nd place As with Dashboard, everything required to define each dashboard is stored within Node-RED. This makes redeployment trivial. Unfortunately, as the Node-RED instance currently needs to be restarted before FlexDash works, it just missed out on joint first place. It would be great to see that issue resolved in future versions. There is also an [ongoing discussion](https://github.com/node-red/node-red/issues/569){rel=""nofollow""} about a way to resolve issue by changing how Node-RED deals with dependencies. ### uibuilder - 3rd place uibuilder uses the filesystem of the host instance to store its configuration. In practice this means that if you migrate the Node-RED project files to a new location you probably will find your dashboard no longer works. This can be mitigated by also migrating the filesystem (for example using persistent storage in Docker) and re-deploying via a Docker registry but it would be great to see uibuilder move towards not being dependant on the filesystem as it will make DevOps tasks that much easier. ## How active is each project's development? ### uibuilder - 1st place ![Image showing the uibuilder Github Commits](https://flowfuse.com/blog/2023/03/images/uibuilder-activity.png "Image showing the uibuilder Github Commits") uibuilder has has consistent commits to the project going back several years with even greater activity since the start of 2022. ### FlexDash - 2nd place ![Image showing the FlexDash Github Commits](https://flowfuse.com/blog/2023/03/images/flexdash-activity.png "Image showing the FlexDash Github Commits") The commits to FlexDash have been regular since mid 2022. ### Dashboard - 3rd place ![Image showing the Dashboard Github Commits](https://flowfuse.com/blog/2023/03/images/dashboard-activity.png "Image showing the Dashboard Github Commits") Dashboard is now in a a maintenance only state. In the words of the project lead, *'Angular 1 (the framework used to build Dashboard) is now unsupported and it's just a matter of time before there is a serious security hole raised against it, for which there will be no fix. Of course we don't use all the features of it so we may be lucky that an exploit doesn't necessarily expose us directly but it will compromise any audits people may wish to do'*. In practice, this means that sooner or later using Dashboard might become a significant security risk. ## What are the future development plans? ### uibuilder - joint firstplace During the writing of this article, uibuilder released a new version with some significant new features. In the words of the project lead when talking about version 6.1.0, '*It feels like uibuilder really is growing up. No more apologising for not being a direct Node-RED Dashboard replacement, uibuilder has its own path*. *You can now create and update visible web page elements direct from Node-RED data without needing to understand all of the intricacies and inconsistencies of HTML. You can create your own utility tools either in Node-RED or in front-end code that leverages the low-code UI features of uibuilder*'. It's great to see projects under active development. My greatest difficulty when using uibuilder, is I found it hard to create the UI elements I needed based on low-code workflows. According to the [development roadmap for uibuilder](https://totallyinformation.github.io/node-red-contrib-uibuilder/#/roadmap){rel=""nofollow""} the project should progress towards being easier and easier for low-coders to make use of. To again quote the project lead, '*In general, the ongoing direction of travel is to enable more zero-code features that will work both in Node-RED flows and in front-end custom code. The low-code feature set is already quite mature now and is well documented enough that it can be used by other tools should anyone wish to do so. I will be making sure that both the zero-code and low-code features are as easy to use as possible both from Node-RED and front-end code for maximum flexibility*'. ### FlexDash - joint firstplace The project Lead for FlexDash had the following to say about the future development of the project. '*FlexDash currently has a fairly rigid overall page structure: there's a tab bar and each tab's content is organized in grids of widgets. The plan is to open this up fully so the user can start from a blank page and place containers which contain widgets. This way almost any layout could be implemented in FlexDash*'. '*I also would like to improve the multi-user capabilities of FlexDash by supporting authentication and making it easier for users to implement flows that present per-user data in the dashboard*'. After using FlexDash over the past couple of weeks and finding it to be already be a strong contender for all my Node-RED dashboard needs, it's great to see it continuing to be improved. ### Dashboard - third place ***Important Update: New Generation of Node-RED Dashboard Released:*** *A new generation of the outdated and unmaintained Node-RED Dashboard has been released to replace it. Introducing [Node-RED Dashboard 2.0](https://dashboard.flowfuse.com/){rel=""nofollow""}, built on Vue.js, offering significantly more versatility than its predecessor. This new dashboard, managed by FlowFuse, is designed to allow full customization, addressing the limitations of the previous version.* \*Node-RED Dashboard 2.0 retains most of the widgets and concepts from the old version, so transitioning from Node-RED Dashboard 1.0 to 2.0 is easy. For a full feature comparison you can check out the [Migration Guide](https://dashboard.flowfuse.com/user/migration.html){rel=""nofollow""}. Dashboard 2.0 includes various chart types such as line, scatter, bar, gauge, and more, and it's compatible with all the [Vuetify component library](https://vuetifyjs.com/en/components/all/#containment){rel=""nofollow""}, making it easier to build advanced dashboards.\* *Furthermore, the team is continuously working on adding more amazing features to enhance the user experience. For a smooth transition, FlowFuse provides easy-to-follow guides. Refer to [Node-RED Dashboard 2.0 Guides](https://flowfuse.com/blog/dashboard/) for more information.* As mentioned above, Dashboard is no longer in active development. This is due to the framework upon which it was build [(AngularJS)](https://angularjs.org/){rel=""nofollow""} now being unsupported as of the end of 2021. You can read a lot more detail on why ongoing development of Dashboard is not practical in this [thread on the Node-RED forums](https://discourse.nodered.org/t/discussion-about-a-new-dashboard/51119/3){rel=""nofollow""}. There could possibly be new effort put into porting Dashboard over to a new framework but that is a significant amount of work. I suspect it would take hundreds of hours development just to get the feature set back to the same state as the current version so I suspect it won't ever happen. ## Conclusions Personally, I was a little surprised by these results. I have used Dashboard for around 3 years and always found it to be a great tool for putting together quick and informative dashboards. That being said, when attempting to objectively compare it to uibuilder and FlexDash, the other two projects often are individually better in a given category. That coupled with the halt of development for Dashboard due to AngularJS being no longer supported, it's hard to recommend Dashboard for totally new Node-RED users in 2023, especially for commercial projects. If you already use Dashboard, in a non-commercial setting you should probably continue to do so, you might find that its development slows down to a near stall due to the underlying framework now being abandoned but for at least as of right now it's a great solution to build your Node-RED dashboards in. FlexDash is probably the best low-code solution for building dashboards in Node-RED. If you don't get blocked by the confusing install process I believe it's the one to pick up at the time of writing due to it's ongoing support and low-code interface. uibuilder is currently not what I would consider a truly low-code option for creating dashboards but it is moving in that direction. It has some great features and is extremely flexible so it has a good chance of ending up as the most popular solution to build dashboards in Node-RED in the long term. That being said, as of time of writing unless you are a 'coder' you will may struggle to build a dashboard using it. # FlowFuse v1.5 Now Available For FlowFuse 1.5 we have been busy making a lot of UX changes and upgrading our underlying architecture to enable future innovations on the FlowFuse platform. With our recently announced [Terminology Changes](https://flowfuse.com/blog/2023/03/terminology-changes/), we have introduced some new concepts into FlowFuse. - **Application**: A group of Node-RED Instances Each instance can run locally (in FlowFuse) or remotely (on Devices) - **Instances**: We renamed "Projects" to "Instances" to be more inline with the terminology used in the Node-RED community As such, our User Experience has been updated to reflect these changes, and allow for further functionality to be introduced with our plans for [Multiple Instances per Application](https://github.com/FlowFuse/flowfuse/issues/1689){rel=""nofollow""}. ### "Applications" View At the top-level in FlowFuse, you can now see a list of your "Appications". In FlowFuse 1.5, as we still have a 1:1 relationship of Applications to Local Instances, this will be the same as the list of "Projects" that you're used to seeing. !\[Screenshot to show the new "Applications" view]\(./images/screenshot-applications.png "Screenshot to show the new "Applications" view") **"Applications" view in FlowFuse, listing all available Applications** For 1.5, all of your settings, environment variables, etc. are all now at the "Instance" level. Applications will gain a lot more functionality in future releases. ### "Instances" View When clicking on one of your Applications, you will see a list of Node-RED instances bound to that Application. !\[Screenshot to show the new "Instances" view]\(./images/screenshot-instances.png "Screenshot to show the new "Instances" view") **A list of Instances contained within a single Application.** Clicking on this Instance, will open up the "Instance" view, this is an exact replica of the "Project" view you'll be used to seeing in FlowFuse, and contains all of the same functionality: !\[Screenshot to show the new "Instances" view]\(./images/screenshot-instance.png "Screenshot to show the new "Instances" view") **FlowFuse 1.5's "Instance" view. This contains all of the functionality previously found in the "Project" view.** ### Devices & Managing Remote Instances Devices are now bound to "Instances", you'll see these in the "Devices" view, and can be managed and deployed to in exactly the same way as before. Devices will run whatever you've selected as your "Target Snapshot" for this Instance. !["Screenshot to show an Instance's 'Devices' view"](https://flowfuse.com/blog/2023/03/images/screenshot-devices.png "Screenshot to show an Instance's 'Devices' view") **"Devices" view, available for a given Node-RED Instance. This lists all of the connected devices to a given instance, that will automatically update when a new Target Snapshot is set.** ## Node-RED 3.1 Beta Available FlowFuse Cloud is a great place to try out the new Node-RED features, with FlowFuse Cloud now including the [Node-RED 3.1.0-beta.2](https://discourse.nodered.org/t/node-red-3-1-0-beta-2-released/76192){rel=""nofollow""}. If you want to try this version you can [duplicate your application](https://flowfuse.com/docs/user/instance-settings/#copy-instance) or [upgrade your stack](https://flowfuse.com/docs/user/changestack/). ## Other Improvements - Update to audit logs to improve usability \[[#1800](https://github.com/FlowFuse/flowfuse/issues/1800){rel=""nofollow""}] \[[#1785](https://github.com/FlowFuse/flowfuse/issues/1785){rel=""nofollow""}] - Improve how licensing works with overages, for easier scaling of FlowFuse and your Node-RED Instances \[[#1639](https://github.com/FlowFuse/flowfuse/issues/1639){rel=""nofollow""}] \[[#1739](https://github.com/FlowFuse/flowfuse/issues/1739){rel=""nofollow""}] ## Bug Fixes - Device "Last Seen" status shows "never" even though it has previously been seen \[[#1723](https://github.com/FlowFuse/flowfuse/issues/1723){rel=""nofollow""}] - Improved Safe Mode launch for small projects \[[#1579](https://github.com/FlowFuse/flowfuse/issues/1579){rel=""nofollow""}] ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.5. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That's also a great place to send us any feedback or feature requests. You can also get help on [the Node-RED forums](https://discourse.nodered.org/){rel=""nofollow""} As well as in the [forum within our Github project](https://github.com/FlowFuse/flowfuse/discussions){rel=""nofollow""} Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""} You can raise a support ticket by emailing We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you. # IBM Cloud removes Node-RED starter application IBM Cloud has [recently announced](https://www.ibm.com/cloud/blog/announcements/deprecation-of-ibm-cloud-starter-kits){rel=""nofollow""} that they will no longer be providing their Cloud App Service Starter Kits, including the [Node-RED Starter Application](https://developer.ibm.com/tutorials/how-to-create-a-node-red-starter-application/){rel=""nofollow""}. ## Node-RED Starter Application If you're looking for an alternative place to get started with Node-RED then FlowFuse, founded by Node-RED co-creator Nick O'Leary, are here to help you with Cloud-hosted Node-RED as well as infrastructure and tooling to scale your Node-RED instances in production. As an ex-IBM Employee myself, over the years, I've been very dependant upon the Node-RED Starter Application in IBM Cloud. I'd used it dozens of times with clients to showcase the value of Node-RED and how easy it is to spin up integrations between hardware devices, APIs and online services. If you're now looking for somewhere to rely on in order to easily spin up new instance of Node-RED, then FlowFuse is the obvious answer. If you're completely new for Node-RED too, we can also help you there with our [Getting Started](https://flowfuse.com/blog/2023/01/getting-started-with-node-red/) guide. You can sign up for a [free FlowFuse Cloud Account](https://app.flowfuse.com/account/create){rel=""nofollow""} where you'll be given one small Node-RED instance for free, for your first month. ## Integrations If you've seen the excellent ["Create a Node-RED starter application"](https://developer.ibm.com/tutorials/how-to-create-a-node-red-starter-application/){rel=""nofollow""} article on IBM Developer, you'll probably be looking to connect up to a Cloudant Instance, or other IBM Cloud Services. Don't worry. All of that is still available through Node-RED on FlowFuse. You can install the relevant nodes in one of two places: 1. **Node-RED Palette Manager:** Click "Menu > Manage Palette > Install". The menu is available via the icon in the top-right of your running Node-RED Instance) ![Screenshot of Node-RED's Manage Palette menu](https://flowfuse.com/blog/2023/03/images/nr-manage-palette-cloudant.png "Screenshot of Node-RED's Manage Palette menu") 2. **FlowFuse Instance Settings:** For a given Instance in FlowFuse, click "Settings > Palette". You can then define the npm module name and versions explicitely in the "Installed Modules" section ![Screenshot of FlowFuse's "Installed Modules" option in Instance > Settings > Palette](https://flowfuse.com/blog/2023/03/images/ff-installed-modules.png "Screenshot of FlowFuse's 'Installed Modules' option in Instance > Settings > Palette") It's also easy to setup [Environment Variables](https://flowfuse.com/blog/2023/01/environment-variables-in-node-red/) for Node-RED in FlowFuse for when you integrate with external services like APIs too. ## Security As with IBM Cloud, FlowFuse makes it very easy to secure your Node-RED Applications. [FlowFuse offers three tiers of security](https://flowfuse.com/docs/user/instance-settings/#security) options on your Node-RED Instances to secure any exposed HTTP routes on your Node-RED instance, e.g. REST API endpoints or Node-RED Dashboard. - **None**: Anyone will be able to access the exposed routes. - **Basic Auth**: Setup a single, dedicated username and password combination that is required in order to access the routes. - **FlowFuse Credentials**: Visitors can use their FlowFuse username/password in order to access the endpoints. This also includes SSO if you have that configured for your FlowFoerge Team. ## Migrating Existing Instances If you're looking to move your Node-RED applications from IBM Cloud, then you can do so through one of two options: ### Node-RED Tools Plugin You can use our Node-RED Tools plugin to migrate your flows and credentials over to FlowFuse. You can read the details in our [Migration Guide](https://flowfuse.com/docs/migration/introduction/), which also includes instructions on how to export your environment variables too. ### Manual Import This will only enable you to import your Flows, not the associated credentials. 1. Export your existing `flows.json` from your IBM Cloud-hosted Node-RED instance by choosing "Export > All Flows > Download" within Node-RED 2. Create a new Application on FlowFuse 3. Once created, click the Node-RED instance that has been generated within your Application 4. Click "Settings" 5. Scroll down and select the "Import Instance" option 6. Choose your `flows.json` file that you downloaded earlier. *If you have any questions about the above, or more generally about Node-RED or FlowFuse, then please do reach out and [get in touch](https://flowfuse.com/contact-us).* # Node-RED: The Integration Platform for IIoT Edge Computing & PLCs Node-RED has become a widely adopted integration platform for IoT edge computing and PLCs. Discover why! ## The Integration Platform for IIoT Edge Computing & PLCs Node-RED is a widely adopted open-source low-code development tool that makes it easy to connect and integrate different sources of data. With a visual programming interface and drag-and-drop functionality, [Node-RED](https://flowfuse.com/node-red/) makes it possible for software developers and non-professional software developers to create sophisticated applications. In the manufacturing and industrial automation industry, the focus of the Industrial Internet of Things (IIoT) has been on integrating industrial processes and equipment to enable real-time monitoring, control, and analysis of data. For many use cases, instead of sending all the data to the cloud, the best practice for processing industrial data is to deploy the application to the edge of the network, referred to as edge computing. By processing the data closer to the data source, edge computing has many benefits, including reduced latency, limited downtime, conserving bandwidth, and increased privacy, and security. ## How Node-RED fits into IIoT Edge Computing A key challenge for IIoT edge computing is the wide variety of different hardware platforms, protocols and sources of data and processes. Over the years, the Node-RED community has built [thousands of nodes and flow](https://flows.nodered.org/){rel=""nofollow""}s to support a wider range of these sources of data, including support for [Modbus](https://flows.nodered.org/node/node-red-contrib-modbus){rel=""nofollow""}, [OPC-UA](https://flows.nodered.org/node/node-red-contrib-opcua){rel=""nofollow""}, [S7](https://flows.nodered.org/node/node-red-contrib-s7){rel=""nofollow""}, [MQTT](https://cookbook.nodered.org/mqtt/){rel=""nofollow""}, etc. Node-RED also has nodes for graphics and dashboards to make it trivial to visualize industrial data. Node-RED’s visual programming environment makes it accessible to non-developers. Manufacturing, mechanical, and electrical engineers in the factories are typically the domain experts in understanding the existing systems and often lead IIoT initiatives. Node-RED enables these engineers to quickly innovate and create real value for their organizations. This makes it a popular choice for engineers looking to create edge computing solutions. ## PLC and IoT Gateway Vendors Embrace Node-RED PLC and IoT Gateway vendors are at the forefront of promoting edge computing. They see edge computing as a way to modernize hardware in the factory and for remote asset management. PLC and IoT gateways often sit in front of old legacy systems that don’t have the connectivity or compute platform to enable IIoT applications. Many of these hardware vendors realized they need an application delivery platform for their devices. Traditional OT hardware vendors often implement proprietary software stacks that are often difficult to use and closed to integrating with other hardware and software. Forward thinking hardware vendors realized having an open platform is the future of their industry and customers have begun to demand more open platforms. Node-RED’s ease of use, open community and open source license provided the solution many of these hardware vendors were looking for. ## The Standard for Edge Computing and PLCs Today, Node-RED has been adopted by some of the leading PLC and IoT Gateway vendors. The hardware vendor community appears to have standardized on Node-RED as being the edge computing platform for IIoT. Below is a sample of the vendors offering a Node-RED solution: 1. [Advantech](https://www.advantech.com/en-eu/products/node-red-gateways/sub_fb7246cc-cc10-486f-806b-30bb50a90f28){rel=""nofollow""} Node-RED Field Gateway 2. [Bechhoff](https://infosys.beckhoff.com/english.php?content=../content/1033/tf6720_tc3_iot_data_agent/3260672139.html&id=){rel=""nofollow""} TwinCAT 3. [Bivocom](https://www.bivocom.com/products/iot-gateways/edge-iot-gateway-tg452){rel=""nofollow""} TG452 IoT Edge Gateway 4. [BLIIOT Edge Computing Gateway](https://bliiot.com/products/){rel=""nofollow""} EdgeCom BL302 5. [Bosch CtrlX Core](https://developer.community.boschrexroth.com/t5/Store-and-How-to/ctrlX-CORE-Node-RED-App/ba-p/22366){rel=""nofollow""} 6. [Broadsens](https://www.broadsens.com/wireless-gateway/){rel=""nofollow""} GU200 & GU 200S 7. [Emerson](https://www.emerson.com/documents/automation/product-datasheet-pacedge-software-computing-devices-pacsystems-en-7205588.pdf){rel=""nofollow""} PACEdge 8. [Hilscher Automation](https://github.com/HilscherAutomation/netPI-nodered){rel=""nofollow""} 9. [Opto22](https://developer.opto22.com/nodered/general/){rel=""nofollow""} groov RIO & EPIC 10. [Parallax AV](https://www.parallaxcontrol.com/){rel=""nofollow""} Control System 11. [Particle.io](https://docs.particle.io/reference/cloud-apis/node-red/){rel=""nofollow""} Particle 12. [Pepperl+Fuchs](https://www.pepperl-fuchs.com/usa/en/classid_199.htm?view=productdetails&prodid=93839){rel=""nofollow""} AS-Interface gateway 13. [Raspberry Pi](https://projects.raspberrypi.org/en/projects/getting-started-with-node-red){rel=""nofollow""} 14. [Renesas](https://www.renesas.com/us/en/products/programmable-mixed-signal-asic-ip-products/mixed-signal-asics/communication-asics/ftclick-mikrobus-compatible-interface-module){rel=""nofollow""} FT Click 15. [Revolution Pi](https://revolutionpi.com/revpi-connect/){rel=""nofollow""} RevPi Connect 16. [Schneider Electric](https://shop.exchange.se.com/en-US/apps/59823/ecostruxure-plant-data-expert/features){rel=""nofollow""} ExoStructure Plant Data Expert 17. [Siemens](https://github.com/SIMATICmeetsLinux/IOT2050-NodeRed-OPCUA-Server){rel=""nofollow""} S7 PLC 18. [ST-One](https://st-one.io/en/){rel=""nofollow""} 19. [Tulip](https://support.tulip.co/docs/using-node-red-with-edge-mc){rel=""nofollow""} Edge MC & Edge IO 20. [Wago](https://www.wago.com/us/edge-devices-overview){rel=""nofollow""} Edge Controller & Computer 21. [Weidmueller](https://catalog.weidmueller.com/procat/Group.jsp;jsessionid=C885C404E7B4B798B23B8A9BB2200513?groupId=\(%22group14048963834797%22\)&page=Group){rel=""nofollow""} control web There are several key reason Node-RED is so popular for IIoT edge computing, including: - Easy User-friendly interface that makes it accessible to manufacturing engineers that might not have a lot of programming experience. - Large community of open source nodes that integrate with many different OT hardware and protocols. - Open source community and license making it vendor neutral so competing hardware vendors feel comfortable embracing the platform. ## Conclusion IIoT and edge computing is making software more critical to the manufacturing industry. The flexibility to integrate data from different sources to create innovative data centric solutions is primarily software driven. In partnership with OT hardware vendors, Node-RED’s flexible and easy to use environment provides the platform for manufacturing companies to embrace software to develop IIoT solutions. # Terminology Changes As a new product in the market, we constantly have to make choices on how to name things. Naming things is hard! As you name a thing, say "Project", it might be suitable now, but the product evolves, and may outgrow the name such that it doesn’t fit anymore. We are at this point with FlowFuse, and want to walk you through what we have planned, and why we are changing a couple of things. ### Enter the "Application" In [FlowFuse 1.5](https://flowfuse.com/blog/2023/03/flowforge-1-5-0-released/), we have introduced a new concept called an **Application**. An Application will allow you to organize multiple Node-RED instances into a single managed group. As of the 1.5 release, an Application can still only have a single Node-RED instance, but in future releases, Applications will allow for multiple Node-RED instances and will allow us to implement capabilities such as **DevOps Pipelines** and **High Availability**. ### "Projects" to "Instances" Until now, 'Project' encapsulated both the Node-RED instance that was running in FlowFuse, and the associated devices (remote instances), settings and environment variables. It was an overloaded term, and it caused confusion with our users. To simplify things, and to adhere more to the terminology familiar with the Node-RED community, we are renaming Projects to **Instances**. An **Instance** is a customized version of Node-RED that includes various FlowFuse plugins to integrate it with the FlowFuse platform. It can also be used to manage the environment variables used in your Node-RED flows. Instances can either be: - **Local** - An instance of Node-RED running in FlowFuse. - **Remote** - An instance of Node-RED, managed by FlowFuse, running on a Device. In future releases, environment variables will also be able to be stored at the Application level, and shared across multiple Node-RED Instances. ### Devices FlowFuse can also be used to manage remote Node-RED instances. This is typically useful when you have a number of remote devices that are required to run the same Node-RED instance, and may have variation in configuration or environment variables for example. Devices are registered to an Instance, and can be configured to run [Snapshots](https://flowfuse.com/docs/user/concepts/#snapshot) of the Instance running in FlowFuse. To accomplish this remote management capability, the [FlowFuse Device Agent](https://github.com/FlowFuse/device-agent){rel=""nofollow""} needs to be installed on each device. Devices are registered with a Team, and then the appropriate device(s) are assigned to a Node-RED instance that should be deployed to the device(s). When the Node-RED instance is ready for deployment, a user creates a snapshot of the instance and marks it as a target snapshot for the device. We hope these changes will simplify the FlowFuse terminology for our users and allow us to grow the FlowFuse platform. If you have any feedback or thoughts, please do reach out to us. # The benefits and drawbacks of using Node-RED function nodes Function nodes are an essential part of Node-RED. They allow you to write custom JavaScript functions that can be used in your Node-RED flows. In this blog post, I will discuss some of the benefits and drawbacks of using Function nodes in your next project. ## 5 Benefits of using Function Nodes: :video{ariaLabel="Example showing how to use the function node" autoPlay="true" height="394" loop="true" muted="true" playsInline="true" preload="none" width="642"} 1. **Customisation:** Function nodes allow you to write custom JavaScript functions that can be tailored to your specific needs. You can create complex functions that perform a variety of tasks, the only limit is your programming skills. 2. **Reusability:** Function nodes can be reused in multiple flows, saving you time and effort. You can create a library of custom functions that can be easily accessed and reused in different flows. 3. **Debugging:** Function nodes provide an easy way to debug your code. You can use console.log statements to output debug information to the Node-RED debug panel, making it easier to identify and fix issues. 4. **Performance:** Function nodes can be more performant than using multiple nodes to achieve the same result. By combining multiple tasks into a single function, you can improve performance, assuming your code is efficient. 5. **Flexibility:** Function nodes provide a high degree of flexibility. You can use them to perform tasks that are not possible using a single, standard Node-RED node, such as complex data manipulation. ## 5 Benefits of avoiding Function Nodes: :video{ariaLabel="Example showing how to not use the function node" autoPlay="true" height="394" loop="true" muted="true" playsInline="true" preload="none" width="642"} 1. **Simplicity:** Not using function nodes can make your flows simpler and easier to understand. By using standard Node-RED nodes, you can create flows that are easy to follow and maintain for both you and your team. 2. **Ease of Use:** Standard Node-RED nodes are easy to use and require no programming knowledge. This makes it easier for non-technical users to create and maintain flows. 3. **Modularity:** By using standard Node-RED nodes, you can create modular flows that can be easily modified and extended. This makes it easier to add new functionality to your flows as your needs change. 4. **Community Support:** Standard Node-RED nodes have a large and active community, providing support and resources for users. This can make it easier to find solutions to common problems and share knowledge with others. 5. **Compatibility:** Standard Node-RED nodes are usually compatible with all versions of Node-RED, making it easier to migrate flows between different environments. ## How to Easily Create Function Nodes in FlowFuse FlowFuse offers a robust platform for building, scaling, and securing your Node-RED applications. We are constantly adding new features to make it easy to use in the enterprise where you can rapidly improve your industrial processes. The **"FlowFuse Assistant."** for example is an AI-powered tool that simplifies the creation of Function nodes. You only need to provide a prompt, and the assistant generates the Function nodes for you. For more details on using the FlowFuse Assistant, visit [the Assistants Documentation](https://flowfuse.com/docs/user/expert/). ## Conclusion: Function nodes are particularly valuable for users who possess JavaScript programming skills. They allow for complex tasks, advanced data manipulation, and integration with external APIs, providing a high level of customization and flexibility. However, they require a good understanding of JavaScript to implement effectively and can be more challenging to manage and debug compared to standard Node-RED nodes. On the other hand, standard Node-RED nodes offer a simpler and more accessible approach, making it easy for users without programming expertise to create and maintain flows. They are designed for straightforward tasks and provide modularity, benefiting from a supportive community for troubleshooting and knowledge sharing. Ultimately, the choice between using function nodes and standard nodes will depend on your project's requirements and your familiarity with JavaScript. If you seek deep customization and flexibility, function nodes, enhanced by tools like FlowFuse Assistant, might be the best choice. For those who value simplicity and ease of use, standard Node-RED nodes are a great fit. # Node-RED Tips - Subflows, Link Nodes, and the Range Node There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows. ### 1. Subflows Subflows are a great way to reuse sections of your flows. Once you have created a subflow, it can easily be dropped into your workspace one or more times. #### Why use subflows? Without using a subflow, you can copy and paste a flow into each place you need to use it. This takes up quite a bit of workspace, and makes it harder to update your flow in the future as you'll have to update each copy. ![Duplication of the flow](https://flowfuse.com/blog/2023/04/images/no-subflow.png "Duplication of the flow") If we instead put the flow into a subflow we'll save a lot of workspace and it will be easier to update the reused sections of the flow if we need to in the future. #### Creating a subflow You can create a subflow using the burger menu in the top right corner of Node-RED, select Subflows, then Create Subflow. Lay out your subflow, making sure you create an input and output. You can even have more than one output if you want. ![Contents of the subflow](https://flowfuse.com/blog/2023/04/images/subflow.png "Contents of the subflow") You can now drop the subflow into your workspace as needed, saving space and making it easier to manage changes to your flow. ![Using the subflow to reduce duplication of flows](https://flowfuse.com/blog/2023/04/images/using-the-subflow.png "Using the subflow to reduce duplication of flows") ### 2. Link Nodes Link Nodes allow you to separate your flows into distinct sections. The wires between the link nodes are not visible until you select that part of the flow. You can also link flows on different tabs together. Formatting your flows into distinct sections using link nodes can make it easier to read and update your work. To use the link node, drag a link in and out node into your flow's workspace. Now draw a wire as you usually would to link to two nodes together. You should see a link between the nodes but it only shows when you have the link nodes selected. :video{ariaLabel="Linking two link nodes together" autoPlay="true" height="164" loop="true" muted="true" playsInline="true" preload="none" width="532"} In this example below, the first and second flows have the same nodes and functionality. In the second image of the workspace I've split the flow into specific groups of nodes. ![A flow without link nodes](https://flowfuse.com/blog/2023/04/images/flow-without-link-nodes.png "A flow without link nodes") It's easier to read and understand the flow once it's split up using the link nodes and groups. ![The same flow as above, now split up using link nodes](https://flowfuse.com/blog/2023/04/images/flow-with-link-nodes.png "The same flow as above, now split up using link nodes") ### 3. Range Node Sometimes you might need to map one numbering scale onto another. For example, where a user has selected a value between 0 and 10 but you want to use and store their response as a percentage. The Range node makes this task very easy. ![Example of using the range node](https://flowfuse.com/blog/2023/04/images/flow-using-range.png "Example of using the range node") To configure the node, set it up as follows: ![Configuration of the range node](https://flowfuse.com/blog/2023/04/images/range-config.png "Configuration of the range node") You should now see that the input values are translated to the appropriate value out of 100. :video{ariaLabel="The range note in use" autoPlay="true" height="164" loop="true" muted="true" playsInline="true" preload="none" width="534"} We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). You can also read some of our previous Node-RED tips using the links below. [Node-RED Tips - Importing, Exporting, and Grouping Flows](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-5/):br[Node-RED Tips - Smooth, Catch, and Maths](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-4/):br[Node-RED Tips - Exec, Filter, and Debug](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-3/):br[Node-RED Tips - Deploying, Debugging, and Delaying](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-2/):br[Node-RED Tips - Wiring Shortcuts](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-1/) # Community News April 2023 Welcome to the FlowFuse newsletter for April 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. ## Upcoming events ### Node-RED Ask Me Anything Back by popular demand, FlowFuse is hosting a monthly Node-RED Ask Me Anything session on April 13th. This is a great opportunity to ask Nick O’Leary, co-creator of Node-RED & FlowFuse CTO, and Rob Marcer, Node-RED FlowFuse Developer Educator your questions about Node-RED. ### Connect, Integrate, Visual Industrial Production Metrics with Node-RED Join Steve McLaughlin from FlowFuse as he showcases how easy it is to use Node-RED to visualize popular production metrics using Node-RED. [Register today](https://flowfuse.com/webinars/2023/industrial-data-node-red/). ## From our Blog [Comparing Node-RED Dashboards Solutions](https://flowfuse.com/blog/2023/03/comparing-node-red-dashboards/) - A popular article comparing Node-RED Dashboard, uibuilder and FlexDash. [IBM Cloud removes Node-RED starter application](https://flowfuse.com/blog/2023/03/ibmcloud-starter-removed/) - IBM has discontinued their Node-RED Starter Application, discover how to migrate to FlowFuse. [The benefits and drawbacks of using Node-RED function nodes](https://flowfuse.com/blog/2023/03/why-should-you-use-node-red-function-nodes/) - Node-RED function nodes provide a great deal of flexibility in Node-RED. Discover the benefits and drawbacks of using them. [FlowFuse 15. Now Available](https://flowfuse.com/blog/2023/03/flowforge-1-5-0-released/) - FlowFuse 1.5 included updates to the UI and architecture to allow for future features. Node-RED Quick Tips - Rob Marcer, FlowFuse Developer Educator has a weekly series of Node-RED hints and tips - [Node-RED Tips - Importing, Exporting, and Grouping Flows](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-5/) - [Node-RED Tips - Smooth, Catch, and Math](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-4/) ## From the Community ### Quantum for Node-RED Discover how you can incorporate [quantum technologies](https://theailaboratory.wordpress.com/2023/03/24/quantum-for-everyone/){rel=""nofollow""} into your Node-RED flows. ### Image recognition within Node-RED Kazuhito Yokoi, a researcher at Hitachi, has published an [interesting article](https://kazuhitoyokoi.medium.com/sharing-node-red-flow-of-image-recognition-application-on-github-4d667cdea9f7){rel=""nofollow""} detailing how to incorporate TensorFlow into a Node-RED application to do image recognition. ### Custom Node Spotlight - node-red-contrib-web-worldmap If you would like to include a map in your next project Worldmap is a really good place to start. You can pass coordinates in to set the map to a location or you can use the built in search tool to find a location. As a user manipulates the map a stream of updated coordinates can be passed back to your flows to trigger additional actions. It's a really useful tool, take a [look here](https://flows.nodered.org/node/node-red-contrib-web-worldmap){rel=""nofollow""}. ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - **[Developer Advocate - Manufacturing & Industrial Automation](https://boards.greenhouse.io/flowfuse/jobs/4798023004){rel=""nofollow""}** # FlowFuse v1.6 Now Available The new FlowFuse 1.6 adds new support for multi-instance Node-RED within a single application and support for logging from remote devices. ## FlowFuse Applications Can Now Support Multi-Instance Node-RED FlowFuse 1.6 expands the scope of applications to now allow for multiple instances of Node-RED. For complex Node-RED applications, it is common to have different flows interacting with other flows or flows deployed to different target environments. The ability to associate all these different flows with a single application makes it easier for the development, test and deployment of these types of complex applications. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: OHChdWeRI9Q --- :: ## Access Node-RED logs from remote devices FlowFuse makes it easy to deploy Node-RED out to remote devices. However, once Node-RED has been deployed to the remote device it is often difficult to troubleshoot or debug. Now with FlowFuse 1.6, you can get access to the Node-RED logs from remote devices. This makes it much easier to understand and debug the behavior of a remote device. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: yW1zxwiCmto --- :: ## Other Improvements Update email address verification [#813](https://github.com/FlowFuse/flowfuse/issues/813){rel=""nofollow""} Reminder email about trial doesn't include a link to FF Cloud [#1815](https://github.com/FlowFuse/flowfuse/issues/1815){rel=""nofollow""} Sign-up coupons improvement [#1788](https://github.com/FlowFuse/flowfuse/issues/1788){rel=""nofollow""} New FF\_Instance\_\* envvars inline with new terminology [#1844](https://github.com/FlowFuse/flowfuse/issues/1844){rel=""nofollow""} Deprecate FF\_PROJECT\_\* envvars [#1844](https://github.com/FlowFuse/flowfuse/issues/1844){rel=""nofollow""} Integrate with PostHog events [#1922](https://github.com/FlowFuse/flowfuse/pull/1922){rel=""nofollow""} Introduce search bar to docs/handbook [#620](https://github.com/FlowFuse/website/pull/620){rel=""nofollow""} ## Bug Fixes Deleting instances from the instance list fails [#1859](https://github.com/FlowFuse/flowfuse/issues/1859){rel=""nofollow""} Removing old projects with missing subscriptions fails [#1837](https://github.com/FlowFuse/flowfuse/issues/1837){rel=""nofollow""} Changing to a team as a member shows unauthorized error [#1845](https://github.com/FlowFuse/flowfuse/issues/1845){rel=""nofollow""} Application Overview: “Open Editor” shouldn’t show (or should be disabled) if in “Starting” state [#1931](https://github.com/FlowFuse/flowfuse/issues/1931){rel=""nofollow""} ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.6. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That's also a great place to send us any feedback or feature requests. You can also get help on [the Node-RED forums](https://discourse.nodered.org/){rel=""nofollow""} As well as in the [forum within our Github project](https://github.com/FlowFuse/flowfuse/discussions){rel=""nofollow""} Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""} You can raise a support ticket by emailing We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you. # FlowFuse's visit to Hannover Messe 2023 "Do you use Node-RED?" This simple question became our favorite conversation opener as ZJ and I attended Hannover Messe, the world's leading industrial trade fair. To our delight, the answer was almost always "Yes." This allowed us to dive into truly fruitful discussions with Node-RED experts, exploring the latest industry trends, connecting with potential partners, and sharing our vision for the future of Node-RED in the manufacturing sector. During our visit, we had the pleasure of engaging with several vendors, including Bosch Rexroth, Siemens, Wago, Weidmüller, RevolutionPi, and more. It was genuinely inspiring to see the widespread adoption and usage of Node-RED across the entire industry. See also our [arcticle](https://flowfuse.com/blog/2023/03/integration-platform-for-edge-computing/#the-standard-for-edge-computing-and-plcs) about the adoption of Node-RED for PLCs and Gateways. FlowFuse's innovative solutions, such as remote deployment of Node-RED instances, seamless updates, effortless rollbacks, and built-in security, are already addressing many challenges faced by Node-RED users in the industry today. The positive feedback we received at Hannover Messe has bolstered our commitment to making Node-RED more accessible and production-ready for industrial and enterprise scenarios. As we forge ahead, we are eager to collaborate with more partners, learn from industry leaders, and remain at the forefront of Node-RED development. Together, we can build a more connected, efficient, and innovative manufacturing industry. With each new booth we visited and every conversation we had, it became increasingly clear that Node-RED and FlowFuse are well on their way to becoming an integral part of the industrial landscape. # Node-RED Community Health It is often a challenge to measure the health of an open source project, like Node-RED. Individuals can download and use Node-RED without any indication or feedback to their ongoing satisfaction or usage. However, it is still interesting to look at a variety of metrics to understand the size of the Node-RED community. **GitHub Stars** A popular method for demonstrating popularity of an open source project are GitHub stars. Node-RED has over 16K stars and when [compared to other low-code platforms](https://synodus.com/blog/low-code/open-source-low-code-platforms/){rel=""nofollow""} on GitHub has a strong showing. FWIW, GitHub stars are open to gaming so it is not a great long-term indicator of community health and engagement. **Node-RED Library** At the core of the Node-RED community is the library of nodes and flows that have been developed by community members. The current library has over [4300 nodes available](https://flows.nodered.org/search?type=node&sort=downloads){rel=""nofollow""}, some of the more popular nodes are downloaded more than 10K times per week. The large library of community nodes supports a wide range of protocols, data sources, data stores, and much more. This makes Node-RED more relevant and useful to many more potential users. **Node-RED Website Traffic** In the last year, Node-RED has had over 1.9 million unique visitors to the [nodered.org](https://nodered.org/){rel=""nofollow""} website, over 160K on a monthly basis. The most popular pages, after the home page, are the getting started pages. This demonstrates a strong interest in learning more about how to use Node-RED. **NPM Downloads & Docker Pull Requests** Node-RED is installed by lots of people using Docker and NPM. The project averages over 500K/month pull requests from [Docker Hub](https://hub.docker.com/r/nodered/node-red){rel=""nofollow""} and 100K/month pull requests on [NPM](https://npm-stat.com/charts.html?package=node-red&from=2017-03-22&to=2023-03-22){rel=""nofollow""}. **Node-RED Forums** The [Node-RED community forum](https://discourse.nodered.org/){rel=""nofollow""} is the go to place to get community support. It is a very popular community with over 900K page views per month. The amazing thing is that a very large percentage of questions receive replies from other community members. A great sign of a healthy community. Overall, the Node-RED community is large, healthy and engaged. # Securing Node-RED (2026) Node-RED is very easy to get up and running. Whether you run it locally, in Docker, on a Raspberry Pi, or on a service such as FlowFuse Cloud you can have a project up and running in minutes. One thing that can get overlooked is the security of Node-RED. From personal experience, the first few times I installed Node-RED I was more focussed on the possibilities of what I could do with this new tool than I was keeping my projects secure. In this article I’m going to look at some easy ways to make your Node-RED project more secure, even when first learning about it in a hobby environment. ## Protecting access from your LAN to Node-RED Once you have an instance of Node-RED running it can usually be accessed from anywhere on your LAN (local area network) by pointing a web browser to the relevant IP address and port. `http://192.168.0.3:1880` With a URL similar to the one above, depending on your specific network and Node-RED configuration, anyone on your LAN can view but more importantly edit your flows. This can be really useful when you are first learning about Node-RED but it’s always a good idea to get into the habit of locking down access to the editor, even if you trust everyone who can access your LAN. One of the easiest ways to protect your flows is to add a username and password to your Node-RED instance. :cta-image{alt="Power Workplace relies on FlowFuse for scalability, reliability and security audits - book a demo" cta="demo" src="https://flowfuse.com/images/cta/power-workplace-book-demo.png"} The first step is to find your Node-RED settings.js file. It's not always in the same place but on a default Debian Linux installation it can be found in this directory. `cd ~/.node-red` If you list that directory you should now see something like this: ![Where your settings file should show](https://flowfuse.com/blog/2023/04/images/ls.png "Where your settings.js should show") We now need to edit settings.js, I'm going to use my favourite text editor, [Nano](https://www.nano-editor.org/){rel=""nofollow""} to do that. `nano settings.js` We now need to find and edit the following section of the settings file: ![The settings file before being edited](https://flowfuse.com/blog/2023/04/images/without-password.png "The settings file before being edited") For this example, I'm going to add a password and uncomment the relevant section of the settings file, you could also change the username for additional security. To create the password we'll need to use a command line tool which is included in Node-RED. Open a second terminal then run this command: `node-red admin hash-pw` Put in your new password, I'll use the password 'flowforge' in this example. The tool returns your password in a hashed format: ![The Node-RED tool outputs the hashed password](https://flowfuse.com/blog/2023/04/images/password.png "The Node-RED tool outputs the hashed password") We can now return to the other terminal window, uncomment the section then paste in the new password, this is how it looks for me: ![The settings file with the relevant section uncommented and the password set](https://flowfuse.com/blog/2023/04/images/with-password.png "The settings file with the relevant section uncommented and the password set") We can now save and exit out of the settings file. The last step is to restart Node-RED, I'm using Debian so the command is: `node-red-restart` Now, when we try to access Node-RED I will need to provide a username and password. :video{ariaLabel="Using the username and password to login to Node-RED" autoPlay="true" height="476" loop="true" muted="true" playsInline="true" preload="none" width="708"} You might also want to consider turning off the editor interface once you are happy with your flows. This can make it a little harder to make changes to your project but it also gives you peace of mind that nobody has accidentally or deliberately changed your flows. You can turn off the editor interface as follows. Edit your settings.js file as explained above, look for the following section: ![The setting file before turning off the editor](https://flowfuse.com/blog/2023/04/images/editor-on.png "The setting file before turning off the editor") All you need to do is uncomment the bottom line then change the value from false to true, once done it should look something like this: ![The setting file after turning off the editor](https://flowfuse.com/blog/2023/04/images/editor-off.png "The setting file after turning off the editor") Now restart Node-RED as covered above, then try accessing your Node-RED instance again. You will no longer be able to edit or view your flows. Using these two features, we now have much better control over who can access the design interface for Node-RED. ## Traffic to your Node-RED instance is unencrypted Hopefully, we all know the importance of encrypting your connections between devices to stop people intercepting your traffic. This isn't a huge concern when working on your home LAN but what if you want to access your Node-RED instance from a remote location? There are two obvious options, HTTPS, and a VPN (Virtual Private Network). We could setup your Node-RED traffic to run over HTTPS, this solution ensures that all traffic to and from your Node-RED is encrypted. The downside to this approach is it's quite complex to set up. We will need to have a domain name, open up ports on our LAN's firewall, use a HTTPS certificate provider and then make sure we remember to renew the certificates as needed. It's doable if you are comfortable with those concepts (I covered how to do this as part of my blog [hosting FlowFuse on Google Cloud](https://flowfuse.com/blog/2022/12/flowforge-gcp-https-set-up/)) but there is an easier way to get started, using a VPN. A VPN provides a lot of security advantages depending on which you are using and how it is configured. To secure my traffic I'm going to use a great service call [Tailscale](https://tailscale.com/){rel=""nofollow""} which is free for personal projects. I'm going to install Tailscale on the Raspberry Pi I'm running Node-RED on as well as any other devices I want to access my project from. Once that's done I can access Node-RED from anywhere with internet access but more importantly the traffic to and from my devices is also encrypted. Before we start, it's important to remember that a VPN is only as secure as the company who runs it. You should always consider if you trust the VPN provider as they could potentially access your devices. I trust Tailscale but please do your own research before using a VPN provider. The first step we need to take is creating a Tailscale account, [you can sign up for free here](https://login.tailscale.com/start){rel=""nofollow""}. We next need to add our devices to our VPN using their software, I'm installing Tailscale on my Apple laptop, Google phone as well as the Raspberry Pi I'm running Node-RED on. The install process is really easy, even on the Pi running Raspbian the steps you need to take are well explained in the [Tailscale docs](https://tailscale.com/download/linux/debian-bullseye){rel=""nofollow""}. For the Pi, these are the commands we need to run. 1. Add Tailscale to the Apt package manager. `curl -fsSL https://pkgs.tailscale.com/stable/debian/bullseye.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null` `curl -fsSL https://pkgs.tailscale.com/stable/debian/bullseye.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list` `sudo apt-get update` 2. Install Tailscale `sudo apt-get install tailscale` 3. Start Tailscale and connect your device `sudo tailscale up` After running the last command, we need to follow the on screen prompts to link our devices to your VPN. One last thing which you might want to consider doing, every few months you will need to reconnect your devices to your VPN, if you are only going to be accessing your Node-RED device over the VPN you should consider [disabling your Tailscale key expiry](https://tailscale.com/kb/1028/key-expiry/){rel=""nofollow""}. OK, now we've got our devices all connected you should see something like this in the Tailscale dashboard. ![Tailscale dashboard showing my three devices](https://flowfuse.com/blog/2023/04/images/tailscale.png "Tailscale dashboard showing my three devices") I can now access Node-RED on my Pi from my laptop and phone by pointing a browser to the correct IP address (as shown in the image above) with the port for Node-RED: `http://100.71.28.60:1880` I’ve now secured all traffic between my devices and Node-RED project, I can access Node-RED from anywhere on the internet. ![Accessing Node-RED via the VPN](https://flowfuse.com/blog/2023/04/images/nr-via-vpn.png "Accessing Node-RED via the VPN") If you follow these steps you should be on the right path to running a more secure Node-RED instance. There is a lot more you can do and I recommend you read the [relevant docs on the Node-RED](https://nodered.org/docs/user-guide/runtime/securing-node-red){rel=""nofollow""} website to gain some more ideas. ## What about hosting Node-RED on a cloud solution such as FlowFuse? In this article, I've focussed on hosting Node-RED on a Pi on your own LAN but if you use FlowFuse Cloud to host Node-RED the solutions discussed above are either ready out of the box or are not needed. By default, the Node-RED editor is secured using your FlowFuse user credentials. You can also enable SSO to enhance account security and easily grant access to team members. With role-based access control, you can further protect your flows by managing who can view or edit them. All traffic to FlowFuse and your Node-RED instances is protected by HTTPS. FlowFuse has set up the domain name and manages the certificates, so you can spend time on your flows rather than configuring security. Additionally, remote device access is secured through encrypted tunnels, providing comprehensive protection for your deployments. FlowFuse has a [free trial](https://app.flowfuse.com/account/create){rel=""nofollow""} if you'd like to see how we've made secure hosting of Node-RED easy. ## Conclusion How ever you host Node-RED, it's a great idea to get into good security practices as early as possible to ensure that no unsecured Node-RED instances are exposed to the internet. I hope some of the tips above help you get started down the path to creating more secure Node-RED projects. # Bringing High Availability to Node-RED Many companies look to deploy Node-RED into use cases that require the application to have a high degree of availability, reliability, and scalability. Following up our [previous post on the subject](https://flowfuse.com/blog/2023/02/highly-available-node-red/), in this post I’m going to look at some of the technical details of achieving HA, the approaches available and what that means for the work we’re doing at FlowFuse and upstream in Node-RED. Everyone we speak to has a different set of requirements for this topic. To help with the discussion, I’m going to look at two ways of approaching it: - The **hot-spare approach** where you have a second instance of the application ready to take over when the primary fails. This achieves availability but doesn’t contribute to scalability. - The **load-balanced approach** where you have a second active instance of the application and work is shared between them. If either fails, the other continues running. A side-effect of this approach is a higher potential through-put and scalability; although in practice you need to ensure capacity to tolerate an instance failing. To consider which approach is most appropriate in the context of Node-RED, we need to look at the benefits and complications of each approach. It comes down to two factors; statefulness and how work is routed. ### Statefulness There are two types of state to consider when thinking about a Node-RED flow: **explicit** and **implicit** state. Explicit state is what is programmed into the flow. For example, a flow may store state in Context or use an external database service. Within FlowFuse we provide two types of context - the default in-memory context store and a database-backed persistent store. Currently the database-backed store includes a memory-caching layer to provide better performance and interoperability. That gets tricky when you want to have multiple instances sharing the same store. The context API doesn’t provide a way to atomically update values - so you can get into classic concurrency issues around two applications trying to update the same value. The other type of state is that which is implicitly maintained in a flow - even if the user hasn’t explicitly configured it. For example, the Smooth node can be used to calculate a running average value of messages passing through it. The node does that by keeping in memory the recent values so it can recalculate the average with each update. If you have multiple instances, then the node will be calculating the average for just the message its instances sees. Another example of implicit state is the Batch node that can be used to group messages into batches. Again - it will only be able to do that for the selection of messages the instance receives. It very much depends on the requirements of a flow and what nodes it uses, as to how the state can be handled. In the hot-spare approach, as only one instance is active at any time, a lot of the explicit state handling will work as expected. However the implicit state remains bound to the individual Node-RED instances. In the load-balanced approach, care has to be taken to ensure any state generated by the flow is done in a way that copes with multiple instances accessing it at the same time. A key take-away from this being that a flow has to be created with HA and/or scaling in mind. ### Routing work Node-RED makes it easy to integrate with lots of different sources of events. A couple of the most common being HTTP and MQTT. When considering how to handle multiple instances of an application we need to think about how work is routed to those instances. HTTP is the most well understood; you put a load-balancing proxy in front of the Node-RED instances and it takes care of sharing out the incoming requests. In the hot-spare scenario, the proxy needs to know which instance is active - that requires some coordination within the platform to track that properly. MQTT is commonly used with Node-RED, but unlike HTTP which is in-bound, MQTT works by having Node-RED create an out-bound connection to a broker and then subscribing to the topics of interest. In the early days of MQTT that would mean each instance would subscribe to the same set of topics and receive every message. That doesn’t really fit any HA model. With the publication of MQTTv5, the concept of Shared Subscriptions was added; the ability for a group of clients to connect, subscribe to the same topic and have the broker distribute messages between them. At this point you do get load balancing across your Node-RED instances - as long as the MQTT nodes are suitably configured. There are lots of other nodes that can be used to trigger flows, whether by listening for events on an API, connecting to locally attached hardware and many things in between. Typically, those that are more cloud-aligned, such as messaging systems like Kafka and AMQP will have very well established ways of doing load balancing. Managing out-bound connections gets more complicated in the hot-spare scenario. If we only had to deal with in-bound connections, the hot-spare instance can just sit there waiting for work to be passed its way. But once you have out-bound connections, then you have a problem. The hot-spare instance should only create its out-bound connections when it becomes the active instance. In real terms, that means the Node-RED flows should only be started when the instance becomes active. With our goal to minimize the Mean Time To Recovery (MTTR), we need to find a way to get that spare instance running as quickly as possible; if it takes just as long to start the spare instance as it does to restart the failed primary instance, then it isn’t much of an improvement. The key here is that Node-RED allows you to start the runtime without the flows running. That gets everything loaded and the runtime ready ahead of time. It can then start the flows at a moment's notice with a simple call to the runtime admin API. ### Detecting failure A key requirement of the hot-spare approach to HA is knowing when to failover to the spare. This requires close monitoring of the active instance to know whether it's still working. How quickly you can detect failure is key to reducing the time to recovery. This is where you have to think about the different ways an instance could fail - has it crashed, has it hung, has it got ‘stuck’? Detecting failure usually involves some combination of heartbeat ‘pings’ between the instances to check each is able to respond to requests. The spare instance then needs to be able to decide for itself whether it should become the active instance - and do so safely. You do not want to accidentally have two instances active at the same time. This can get quite complicated to achieve safely, but there are a number of approaches that can be used. We’ll be exploring them as we continue our journey towards HA. ### Editing Flows Within the Node-RED architecture, each instance also serves up its own editor. This is what you get when you point your web browser at it. In a HA world, once you have multiple instances running behind an HTTP load balancer, there is a tricky question of how you edit the flows. If each request hits a different instance, just loading the editor will result in different bits coming from different instances. That can typically be solved at the load balancer level by creating sticky-sessions; ensuring for a given client, each request is routed to a consistent instance. That solves part of the issue, but the next challenge is what to do when the Deploy button is pressed. That is how new flows are passed from the editor to the runtime. When you have multiple instances, we need to make sure that they all get updated. That is quite a tricky problem to solve with the current Node-RED APIs - and something we’ll be working on both in FlowFuse and in the upstream Node-RED project to resolve. That said, a more immediate solution could well be to take advantage of separate development/production instances. You develop in a single instance and, when happy with what you’ve got, roll it out to your HA-ready production instance. This bypasses the need to edit the flows in the HA environment at all. Whichever method is used, there is a question of how you minimize downtime whilst deploying an update. In a purely in-bound environment, solutions can be built where the new application is deployed alongside the old version and, when everything is ready, the in-bound events are redirected to the new version. But that isn’t feasible when you have out-bound connections to deal with as well. For some users, having a scheduled maintenance window for doing updates will be completely acceptable. As with the hot-spare approach to failover, a similar method could be used that starts new instances of Node-RED alongside the old, but with the flows all stopped. Then, once everything is ready, the old instances are stopped and the new instances started - minimizing the downtime, although not completely removing it. ### Continuing the HA journey at FlowFuse So the question is how are we going to apply all of this to what we’re building at FlowFuse. We cannot do everything at once, so we have to prioritize which scenarios we’re going to address first. Consequently, drawing from customer feedback, we have chosen to start with the scaling side of high availability - allowing multiple copies of an instance to be run with appropriate load balancing put in front of it. We are building FlowFuse as an open platform with the ability to run on top of Docker Compose and Kubernetes. As we get into some of these HA features, we will need to look carefully at where we can lean on these underlying technologies - we don’t want to reinvent the wheel here. Our initial focus is going to be when running in a Kubernetes environment - just as we do with our hosted FlowFuse Cloud platform. Kubernetes provides lots of the building blocks for creating a scalable and highly available solution, but it certainly doesn’t do all of the work for you. We've identified our initial set of tasks and changes to how we'll run Node-RED instance with the k8s environment. You can follow our progress with this [issue](https://github.com/FlowFuse/flowfuse/issues/2156){rel=""nofollow""} on our backlog. I hope this post has given some useful insight into the problems we’re looking to solve at FlowFuse. As it's such an important requirement for many users we’ll keep you updated as we make progress. # Chat GPT in Node-RED Function Nodes Recently we [posted a demo of ChatGPT integration in a Node-RED function node](https://www.linkedin.com/posts/flowforge_chatgpt-with-node-red-function-nodes-activity-7052725869684953088-2yOA?utm_source=share&utm_medium=member_desktop){rel=""nofollow""} onto our social media accounts. We have now [open-sourced](https://github.com/FlowFuse/node-red-function-gpt) this for all to play with, and **welcome any and all contributions**. ## How it Works - Prompt Engineering OpenAI make a collection of their [Generative AI models](https://platform.openai.com/docs/models){rel=""nofollow""} available via an API. We are wrapping OpenAI's [node.js module](https://www.npmjs.com/package/openai){rel=""nofollow""}, and in particular using the `openai.createChatCompletion()` functionality. For this API, you provide a chat history, and ChatGPT will respond with the next entry in that conversation. In order to "train" ChatGPT for our use case of populating Node-RED function nodes, we first tried a collection of prompts, defining specific requirements for the contents, e.g. *"Always write Javascript"*, *"Never include the wrapping function definition"*, *"Assume the input is always msg"*. It turns out though, that we were over-engineering it, we were not getting reliable results and ended up realising that ChatGPT's existing knowledge of Node-RED was sufficient such that we could use that as a prompt: Here's what we settled on: ```javascript messages: [ {role: "system", content: "always respond with content for a Node-RED function node, and don't add any commentary, always use const or let instead of var. Always return msg, unless told otherwise."}, {role: "user", content: prompt} ], ``` Here we send a `system` prompt in order to setup ChatGPT, and then follow that immediately with whatever the user has typed. From our (limited) testing, this has given us fairly reliable results. Breaking this prompt down: - ***"Always respond with content for a Node-RED function node"***: Ensured no surrounding `function () {}` definition and set expectations that the function would deal with a `msg` and likely `msg.payload` object. - ***"Don't add any commentary"***: ChatGPT likes to, well, chat. It would always return raw text justifying decisions, etc. Here, we just wanted the code. - ***"Always use const or let instead of var"***: This was Steve being picky. - ***"Always return msg, unless told otherwise"***: We found this wasn't mostly required, but occasionally it would try to return a different variable, and we'd lose context of `msg.payload`, or other data stored in `msg`. So this just made sure we had the consistency. The response from this API call is then populated into the contents of the active tab in the function node: ![Screenshot 2023-04-21 at 16 08 47](https://user-images.githubusercontent.com/99246719/233671631-fefa36c1-6db4-4392-a057-314c16fd91b7.png){width="1728"} In order to use it yourself, you will need a [valid API Key from OpenAI](https://platform.openai.com/account/api-keys){rel=""nofollow""}. ## Additional Features This was built in about a day by Steve and Joe, and we had plenty of ideas on what we'd like to add to it. We've [open-sourced](https://github.com/FlowFuse/node-red-function-gpt){rel=""nofollow""} it, and will add these as issues to the repo, but if anyone want so take a stab at contributing - that'd be most welcome! - **Insert at Cursor ([issue](https://github.com/FlowFuse/node-red-function-gpt/issues/11){rel=""nofollow""}):** Currently, the Ask GPT call will replace *all* of the content of that tab. Would be great to have the code insert wherever the cursor last was in order to add to existing code. - **Retain Conversation History ([issue](https://github.com/FlowFuse/node-red-function-gpt/issues/12){rel=""nofollow""}):** Each time a new prompt is provided by the Node-RED user, we send a fresh conversation to OpenAI, meaning that knowledge of previously asked questions are not retained. - **Client side ChatGPT Config ([issue](https://github.com/FlowFuse/node-red-function-gpt/issues/13){rel=""nofollow""}):** Currently, when you add a new "function-gpt" node you need to select the ChatGTP Config node and click "Deploy" before you can ask it a question. Our ChatGPT interaction operates server-side (to protect your API key), so Node-RED needs that in the runtime first, before a call to ChatGPT can be made. Ideally, we'd be smarter here and pass client-side creds along with the call such that we can use any changes made by the user at the time of the call. ## FlowFuse Assistant - No API Keys Required! Great news! You no longer need to manage OpenAI API keys or configure ChatGPT nodes. The [FlowFuse Assistant](https://flowfuse.com/docs/user/expert/) is now built directly into Node-RED on FlowFuse Cloud, making AI-powered development even easier. Available on FlowFuse Cloud, the Assistant offers: - **Quick Function Node Creation**: Add function nodes to your flow without dragging from the palette - **In-line Code Generation**: Generate JavaScript code for function nodes, JSON for JSON editors, and Vue.js for FlowFuse Dashboard ui-template widgets - **Flow Explainer**: Select nodes and click "Explain Flows" to understand what they do FlowFuse Assistant helps developers work faster and smarter with Node-RED. [Start your free trial](https://app.flowfuse.com/account/create){rel=""nofollow""} to experience AI-powered Node-RED development on FlowFuse Cloud. # Community News May 2023 Welcome to the FlowFuse newsletter for May 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. ## Upcoming events ### Ask Me Anything about Debugging Node-RED Our monthly Node-RED AMA session will have a special focus on debugging. Nick and Rob will lead us through some useful debug workflows to show how they approach debugging Node-RED applications. During the live coding sessions there will be opportunities for attendees to ask questions in real-time. Join us to learn from the experts on the tips and tricks for debugging Node-RED flows. ### Getting Started with Node-RED Dashboard How can you use Node-RED to create dashboards and interactive graphs of your data? The answer is the Node-RED Dashboard node, the most popular node in the Node-RED community. In this webinar, Rob Marcer will take you through the steps of how to get started with the Node-RED Dashboard. [Register today](https://flowfuse.com/webinars/2023/getting-started-nodered-dashboard/). ## From our Blog [Chat GPT in Node-RED Function Nodes](https://flowfuse.com/blog/2023/05/chatgpt-nodered-fcn-node/) - Use Chat GPT to write Node-RED functions directly in the Node-RED interface. [Securing Node-RED](https://flowfuse.com/blog/2023/04/securing-node-red-in-production/) - A look at how you can secure Node-RED deployments. [Node-RED Community Health](https://flowfuse.com/blog/2023/04/nodered-community-health/) - Some key community metrics for the Node-RED community. [FlowFuse's visit to Hannover Messe 2023](https://flowfuse.com/blog/2023/04/hannover-messe/) - Our CEO and Product Manager visited Hannover Messe in Germany; one of the largest trade shows for manufacturing. [FlowFuse 1.6 Now Available](https://flowfuse.com/blog/2023/04/flowforge-1-6-released/) - FlowFuse 1.6 included support for multi-instance Node-RED within a single application as well as support for logging from remote devices. [Node-RED Tips - Subflows, Link Nodes, and the Range Node](https://flowfuse.com/blog/2023/04/3-quick-node-red-tips-6/) ## From the Community Jsonata is a very useful and often underutilised tool built into Node-RED. Steve over at [Steve's Node-RED Guide](https://stevesnoderedguide.com){rel=""nofollow""} has published a great beginners guide. If you are new to Jsonata and want to learn more we recommend you [take a look](https://stevesnoderedguide.com/node-red-and-jsonata-for-beginners){rel=""nofollow""}. ### Custom Node Spotlight - node-red-contrib-queue-gate [Queue Gate](https://flows.nodered.org/node/node-red-contrib-queue-gate){rel=""nofollow""} is a handy custom node which allows you to control the flow of messages. You might wish to queue up all the messages in a flow and then release them all, once an hour. Maybe you want to release just one message at a time and wait until the prior message completed a section of your flow. Queue Gate makes message queuing really easy without the need to use an external queue solution. ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - **[Developer Advocate - Manufacturing & Industrial Automation](https://boards.greenhouse.io/flowfuse/jobs/4798023004){rel=""nofollow""}** - **[Sales Representative](https://boards.greenhouse.io/flowfuse/jobs/4843566004){rel=""nofollow""}** # Running the FlowFuse Device Agent as a service on a Raspberry Pi FlowFuse's device agent allows you to manage and run your Node-RED instances on your own hardware such as a Raspberry Pi. This can be very useful where an application you've written needs to run flows with direct access to hardware sensors. In this article I'm going to explain the steps to configure our device agent to run as a service in Raspbian OS, or any other OS that uses systemd. ## Why run the device agent as a service? The standard process for running FlowFuse's device agent is to start it on the command line using the command `flowforge-device-agent`. This works fine for testing but for long-term installations it's useful to run the device agent as a service. Once running as a service, the device agent will continue to run even if your device is restarted or your SSH connection to your Pi fails. ## Set up steps ### Create the Service File The first step is creating the systemd unit file for your service. You can start by creating a new file in the `/etc/systemd/system` directory with a .service file extension: `sudo nano /etc/systemd/system/flowforge-device-agent.service` ### Define the Service In the service file, you'll need to define the following parameters: - `Description`: A brief description of what the service does. - `ExecStart`: The command(s) to execute to start the service. - `User and Group`: The user and group that the service runs as. - `Type`: Whether the service is a simple or a forking type. We've created the content you'll need for this file and shared it via [this GitHub page](https://github.com/FlowFuse/device-agent/blob/main/service/flowfuse-device.service){rel=""nofollow""}. Copy the code from that page into the nano window you created in step 1, then save and exit out of nano. ### Starting the service on boot (optional) If you want Node-RED to run when the Pi is turned on, or re-booted, you can enable the service to autostart by running the command: `sudo systemctl enable flowforge-device-agent.service` To disable the service, run the command: `sudo systemctl disable flowforge-device-agent.service` ### Using your new service You can now start your service with the start command: `sudo systemctl start flowforge-device-agent` You can check the current status with the status command: `sudo systemctl status flowforge-device-agent` Finally, if you need to stop your agent you can do so with the command: `sudo systemctl stop flowforge-device-agent` ## Further reading If you'd like to learn about using services via the systemctl command you can access the help text by running `systemctl -h` from your Pi terminal. # FlowFuse 1.7 Now Available with Remote Node-RED Editor Access FlowFuse 1.7 adds new support for accessing the Node-RED Editor on Devices via FlowFuse. ## Further improving fleet management and maintenance of remote Node-RED instances We are excited to introduce a new feature that will simplify the process of debugging and developing flows for devices. Our latest feature, "Editing Flows on Devices" allows users to access the editor directly on their device without the need for complex network configurations or firewalls. This feature will significantly improve the user experience, making it easier and more efficient to work with devices. This update is a part of our ongoing commitment to making FlowFuse the best possible solution for developing your Node-RED flows, no matter where they're running. In fact, as part of our last release 1.6, we already introduced the feature: ["Access Node-RED logs from remote devices"](https://flowfuse.com/blog/2023/04/flowforge-1-6-released/#access-node-red-logs-from-remote-devices). This feature made it easy for users to troubleshoot and debug. Building on that, we've taken the next step, and it's now possible to access the Device Editor. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: zS6P3RR86vE --- :: ## Device Status Visualisation This FlowFuse version upgrades device monitoring. It's made easier to manage your devices effectively, especially when there's many of them. Now we offer an intuitive, user-friendly method for users to keep an eye on their devices' status and evaluate the health of their team's devices overall. Creating an overview of your fleet's health, however large your fleet might be. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: S--viuPhrS8 --- :: ## Auto Restart for Hung Node-RED Instances This enhancement ensures a more robust and reliable experience when working with Node-RED flows. The launcher now actively monitors the Node-RED process to detect if it has become unresponsive or hung, in addition to the existing checks for start-up and unexpected process exits. This advancement takes us one step further in improving the availability of our Node-RED instances. ## Ongoing Topics ### High Availability We're actively progressing on the topic of enhancing High Availability in FlowFuse for Node-RED. Our initial tasks and modifications have been identified, specifically pertaining to the operation of Node-RED instances within the k8s environment. These adjustments are aimed at constructing a more resilient system. Stay tuned for a comprehensive update regarding our advancements in High Availability. ### SOC2 Certification Dedicated to upholding the highest levels of security and privacy, our company acknowledges the significance of industry-standard certifications like SOC2 in fostering trust with our customers and partners. We aim to achieve SOC2 Type 1 certification by the end of Q2 and subsequently maintain a continuous SOC2 Type 2 certification. We will keep you informed on our progress as we reach essential milestones in our SOC2 certification journey. Rest assured, our commitment to delivering the utmost security and privacy for our customers and partners remains unwavering. ### AWS Marketplace onboarding We are excited to provide an update on our ongoing task of onboarding Node-RED instances to AWS Marketplace via FlowFuse Cloud, which we have started in this Iteration. By offering Node-RED instances through AWS Marketplace, we aim to simplify the deployment process for our customers. One of the significant challenges we are currently addressing is handling our current payment system in parallel with a new method. This will ensure a seamless billing experience for our customers, as they will be able to manage their Node-RED instance subscriptions through their existing AWS accounts. ## Contributors We'd like the thank the following for their contributions to this release: - [@andreikop](https://github.com/andreikop){rel=""nofollow""} for their work on the [flowforge-driver-k8s #80](https://github.com/FlowFuse/flowforge-driver-k8s/pull/80){rel=""nofollow""} and [flowforge/helm #125](https://github.com/FlowFuse/helm/pull/125){rel=""nofollow""} - [@elenaviter](https://github.com/elenaviter){rel=""nofollow""} for their work on [flowforge/helm #126](https://github.com/FlowFuse/helm/pull/126){rel=""nofollow""} As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our [guide in the docs](https://flowfuse.com/docs/contribute/). ## What's next? We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute: - **Roadmap Overview**: Check out our \[Product Roadmap Page/changelog/) to see what we're planning for future updates. - **Entire Roadmap**: Visit our [Roadmap on GitHub](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""} to follow our progress and contribute your ideas. - **Feedback**: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any [issues on GitHub](https://github.com/FlowFuse/flowfuse/issues/new/choose){rel=""nofollow""}. Together, we can make FlowFuse better with each release! ## Bug Fixes Incorrect number of days displayed when adding a new license [#1895](https://github.com/FlowFuse/flowfuse/issues/1895){rel=""nofollow""} Users were unable to upgrade modules in Manage Palette, even after restarting Node-RED. [#2005](https://github.com/FlowFuse/flowfuse/issues/2005){rel=""nofollow""} Enable 'Delete Team' Button [#2031](https://github.com/FlowFuse/flowfuse/issues/2031){rel=""nofollow""} Triggering a Node-RED restart using the action button resulted in two instances of Node-RED running in the container, causing one instance to crash due to port 1880 being already in use. [#2031](https://github.com/FlowFuse/flowfuse/issues/1860){rel=""nofollow""} Error deleting instance with missing subscription [#2080](https://github.com/FlowFuse/flowfuse/issues/2080){rel=""nofollow""} Snapshot Rollback no longer working [#2026](https://github.com/FlowFuse/flowfuse/issues/2026){rel=""nofollow""} Users receiving an unauthorized error when attempting to switch to a team in which they are a member [#1845](https://github.com/FlowFuse/flowfuse/issues/1845){rel=""nofollow""} Cannot select "Member" option when inviting a team member [#2084](https://github.com/FlowFuse/flowfuse/issues/2084){rel=""nofollow""} ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.7. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. If you hit any problems with the platform please raise an [issue on GitHub](https://github.com/FlowFuse/flowfuse/issues){rel=""nofollow""}. That's also a great place to send us any feedback or feature requests. You can also get help on [the Node-RED forums](https://discourse.nodered.org/){rel=""nofollow""} As well as in the [forum within our Github project](https://github.com/FlowFuse/flowfuse/discussions){rel=""nofollow""} Chat with us on the `#flowfuse` channel on the [Node-RED Slack workspace](https://nodered.org/slack){rel=""nofollow""} You can raise a support ticket by emailing We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you. # Best Practices Integrating a Modbus Device With Node-RED (2026) The world of industrial automation is slow to adopt new technology. With legacy equipment already working and in place, paralyzing down-time costs, and fears of introducing instability into a plant, technology change has a cautious pace. Node-RED provides a way to extend the capabilities of the simpler, proven technology, allowing connections with modern systems. However, the same fundamentals that have made industrial equipment dependable, must be incorporated into your Node-RED architecture. And, inversely, the same abstraction and simplicity that makes a low-code, Node-RED environment fast, and easy to work with, can also make it difficult to interface with a lower-level system. Modbus is a widely adopted protocol for accessing data from existing legacy manufacturing equipment. Node-RED makes it very easy to connect to Modbus enabled equipment. However, there are some best practices we have developed to maintain system integrity when integrating Modbus devices with Node-RED: :cta-image{alt="Aperia Technologies stopped reprogramming controllers station by station with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/aperia-book-demo.png"} ### Add Watchdogs to Node-RED Flows To keep your automation system running with peace of mind and little human intervention, use a watchdog timer. A watchdog timer is typically used to detect and recover from system malfunctions. In Node-RED a watchdog timer can be tied to a broadcast messaging system to get push alerts directly to cell phones, as well as to an auto-reset to try to get your Flows back online automatically. A simple implementation of this is to just put two Trigger nodes in a loop, the first one to send an alert when it hasn’t seen any recent events, and the second to to reset the first one. ![Example watchdog flow](https://flowfuse.com/blog/2023/05/images/integrating-modbus-3.png "Example watchdog flow") Each of the trigger nodes are setup with similar parameters, only the delay values differ. ![Trigger node configuration](https://flowfuse.com/blog/2023/05/images/integrating-modbus-14.png "Trigger node configuration") Sending a payload of {"connectorType":"TCP"} to the Modbus Flex Connector is enough to reset the connection without needing to send all the other parameters. ![Change node configuration](https://flowfuse.com/blog/2023/05/images/integrating-modbus-10.png "Change node configuration") ### Choosing a Safe Poll Rate Be careful when adding new traffic to an industrial network, which might not be able to handle the extra load. Some networks have low-bandwidth, especially at large plants that might be using antiquated IP devices and long-distance, Wi-Fi bridges in electrically noisy environments. Furthermore, some PLCs and other Modbus devices have limits as to how many other devices can connect to them, and a new connection might not work, or worse, bump off an old connection. A PLC’s general operation is to poll its inputs every cycle and react accordingly, running logic and triggering outputs as quickly as possible. The general use-case for Node-RED with a PLC is to create an HMI or broader, SCADA system. A poll rate of every second suffices for a simple HMI. For dashboards published to a greater audience, rather than just the operator, poll rates of several minutes to hours might be adequate. If this is a new integration, it’s better to start slow and make sure the current infrastructure can handle the extra load. ![Modbus node configuration](https://flowfuse.com/blog/2023/05/images/integrating-modbus-7.png "Modbus node configuration") ### Coil/Register Grouping Modbus works more efficiently when it is reading and writing addresses as groups. Creating banks of consecutively numbered coils or registers can help with this. If Modbus is already in use, perhaps for an existing HMI, but for your Node-RED dashboard you just want a selection of these addresses and they are too scattered to be read all at once, pick a starting address numbered far higher than what you will use for any HMI work and create a new bank of coils or registers just for the Node-RED dashboard. The PLC can handle this easily and it greatly simplifies the polling for your Node-RED dashboard. On the Node-RED side of the connection, it’s a good idea to parse and to filter this data as soon as it enters the flow. Below, the first node creates an appropriate message for each coil, with a topic name and a true/false payload. This message is then filtered by the “block unless value changes” mode in the filter node and finally a switch (by topic) node separates out each message. In this example, every Tag coming in from the PLC only triggers downstream nodes when there is a change and each Tag has its own output to connect a wire, and subsequent nodes to. ![Using a switch node to separate the messages](https://flowfuse.com/blog/2023/05/images/integrating-modbus-5.png "Using a switch node to separate the messages") The filter node blocks redundant data from triggering subsequent nodes. ![Filter node configuration](https://flowfuse.com/blog/2023/05/images/integrating-modbus-12.png "Filter node configuration") The function node, “modbusMapArray,” creates messages that are much more user-friendly in the Node-RED environment. ![Modbus function node](https://flowfuse.com/blog/2023/05/images/integrating-modbus-1.png "Modbus function node") Quick tip: If your PLC environment has limits on the number of addresses allowed and you want to read a lot of coils, you can work with registers bitwise and stuff 16 coils into one register. ### Reading Data Types No matter what data type the PLC is sending over Modbus, it’s going to be sent using the 16-bit registers. For example, to send 1234.5678 as a 32-bit Float (little-endian), the payload from the PLC will be a seemingly unhelpful array, [21035,17562]. I can simulate this with the [Productivity Suite Programming Software](https://www.automationdirect.com/adc/overview/catalog/software_products/programmable_controller_software/productivity_suite_programming_software){rel=""nofollow""} from Automation Direct set to simulator mode. Below, I have created a 32-bit float “Tag,” named “mySampleFloat32,” using modbus registers 40001 and 40002, and set the “Init Value” to 1234.5678. ![Productivity Suite Programming Software](https://flowfuse.com/blog/2023/05/images/integrating-modbus-11.png "Productivity Suite Programming Software") Node-RED is typically used at a much higher level, but luckily there is still a way to work with this low-level data. Node-RED uses the Buffer Class to work with this type of data stream, but it’s a little tricky. First the 16-bit registers have to be broken into 8-bit chunks, here we use msg.responseBuffer.buffer to retrieve each octet. Once our buffer is properly filled, there are many built-in functions to reconstruct the data into the actual number you are looking for. ![Using msg.responseBuffer.buffer to retrieve each octet](https://flowfuse.com/blog/2023/05/images/integrating-modbus-4.png "Using msg.responseBuffer.buffer to retrieve each octet") Notice below that to read these 2 registers at 400001 and 400002 I have set my Modbus-Read node to start at “Address” 0 and ready “Quantity” 2 registers. Unfortunately, there are two different standards for writing Modbus addresses and my PLC uses the traditional convention (400001 to 465536) and this Modbus node uses the hexadecimal convention (4x0000 to 4xFFFF). ![Modbus node configuration](https://flowfuse.com/blog/2023/05/images/integrating-modbus-8.png "Modbus node configuration") In this example the byte order from msg.responseBuffer.buffer doesn’t quite match the data type, so we have to rebuild the buffer. ![Mapping the data in a function node](https://flowfuse.com/blog/2023/05/images/integrating-modbus-13.png "Mapping the data in a function node") Amazingly after all that work we get the response: 1234.5677490234375. This is the shortcoming of the 32-bit float data type. Although it can handle a huge range of values, they aren’t very accurate. For this reason, many times PLCs will send a number as an integer, say 12345678, and the documentation will prescribe 4 decimal place accuracy to bring the number back to 1234.5678. Find out more ways to work with a Buffer at {rel=""nofollow""}. ### General Architecture Although you can off-load some of the higher-level logic from the PLC into Node-RED, it’s important to remember that Node-RED augments, but doesn’t create a replacement for a PLC’s IDE and the IEC 61131-3 suite of languages. Make a conscious distinction between the type of work the PLC should handle and what you expect from Node-RED. Any real-time responses to inputs should strictly be handled by the PLC. ### Security Connecting Node-RED to your PLC also creates a larger attack surface for cyber threats. Make sure that you follow the guidelines found on the Node-RED.org site at [Securing Node-RED](https://nodered.org/docs/user-guide/runtime/securing-node-red){rel=""nofollow""}. Node-RED’s strength is its ability to make connections where they weren’t possible before, but this can be taken advantage of by a hacker. For instance, maybe it’s tempting to make Node-RED a transparent gateway and make a RESTful API fully exposing a modbus-flex-write node. This is amazingly easy and powerful with Node-RED, but anyone who can access your IP could send `http://:1880/careful?value=true&fc=15&unitid=1&address=0&quantity=10` and remotely turn on and off whatever they wanted. ![Example endpoint flow](https://flowfuse.com/blog/2023/05/images/integrating-modbus-6.png "Example endpoint flow") Instead, a better practice would be to more narrowly define what you want to accomplish and only allow Node-RED to do exactly that. In this case you might send `http://:1880/honkTheLunchHorn?honk=true` ![Locking down the endpoint](https://flowfuse.com/blog/2023/05/images/integrating-modbus-2.png "Locking down the endpoint") ### Final Thoughts If not done right, there could be some hard lessons, so it’s best to monitor the processes to help track down bugs. Add a log, keep your eyes out, and as a community let’s work to create stable systems. [Opto 22 customers rely on this same Node-RED-to-Modbus pattern in production](https://flowfuse.com/customer-stories/opto22-embraces-node-red/), transforming Modbus registry data from groov EPIC controllers into human-readable, contextual data for other applications. ![Logging failures](https://flowfuse.com/blog/2023/05/images/integrating-modbus-9.png "Logging failures") How readily upper management gives an “okay” to this new technology comes with how well it is implemented. There will be some growing pains, but by the end, you will have supercharged your plant, bringing it into the 21st century. # Node-RED Community Survey Results The Node-RED community recently published the results of their [2023 Community Survey](https://nodered.org/about/community/survey/2023/){rel=""nofollow""}, building upon their [2019 survey](https://nodered.org/about/community/survey/2019/){rel=""nofollow""}. The findings reveal some interesting trends within the Node-RED community that are worth highlighting. 1. **Passionate and Experienced Community**. The Node-RED community has shown a remarkable increase in experience. In 2019, only 28.3% of users had been utilizing Node-RED for over two years. However, in 2023, this number has grown to an impressive 65.2%. This indicates that Node-RED has become a go-to tool for many individuals, demonstrating their continued loyalty to the platform. Moreover, the community highly regards Node-RED, with 94% of respondents rating it as a 4 or 5 on a scale of 1 to 5. :br Developer tools can fall out of fashion but it is clear Node-RED is providing value and is being used by developers. 2. **Increasing Adoption in Industrial and Manufacturing Automation**. The survey also revealed a significant increase in Node-RED usage within the manufacturing and industrial automation industries. Several data points support this finding, including a rise from 31.5% to 40.3% in respondents identifying themselves as working in the manufacturing industry. Additionally, there has been an increase in the use of Node-RED applications for Industrial IoT/PLC devices, which grew from 24% to 35.8% in 2023. Furthermore, the adoption of popular manufacturing industry protocols like [OPC-UA](https://opcfoundation.org/){rel=""nofollow""} and [Modbus](https://modbus.org/){rel=""nofollow""} has also increased, with OPC-UA usage rising from 9.3% to 16.7% and Modbus usage increasing from 15.8% to 27.6%. 3. **InfluxDB dominance in the Node-RED community**.[ InfluxDB](https://www.influxdata.com/){rel=""nofollow""} has emerged as the leading database within the Node-RED community. Its usage has grown significantly, from 24.2% in 2019 to 43.9% in 2023. On the other hand, MySQL, the next most popular database, experienced a slight decrease in usage, dropping from 32.4% to 31.4%. :br It is evident that the MING stack (MQTT/Mosquitto, InfluxDB, Node-RED, and Grafana) is gaining momentum and becoming a preferred choice for developers. 4. **Limitations to Node-RED Adoption**. This year's survey also explored factors that might limit the adoption of Node-RED. Approximately 27.9% of respondents stated that they perceived no additional need for Node-RED. However, the next most common responses were related to the perception that Node-RED is only suitable for proof-of-concept (POC) projects (19.9%), the lack of specific Node-RED features (13.3%), and the absence of professional support (10.1%). :br It is important for the Node-RED community to demonstrate the platform's usage in production environments. Although changing the perception of Node-RED as solely a POC tool may take time, FlowFuse is committed to helping shift this perspective. Moreover, FlowFuse aims to address other identified issues by developing the FlowFuse platform, which will provide the necessary features to create reliable, secure, and dependable Node-RED applications. FlowFuse will also offer professional support to all its customers, ensuring that users have the assistance they need. Thank you to everyone that completed the Node-RED Survey. The insights from the survey will help the community to build a better Node-RED. Check out the detailed results on the [nodered.org website](https://nodered.org/about/community/survey/2023/){rel=""nofollow""}. # Persisting chart data in Node-RED Dashboard 1 Node-RED makes it easy to create HMI (Human Machine Interfaces) using [Node-RED Dashboard](https://flows.nodered.org/node/node-red-dashboard){rel=""nofollow""}. One of the most useful features of Dashboard 1 is the ability to store historic data passed to a chart within the chart node itself. This makes your flows far simpler than would be the case if you needed to send the entire data set to the chart for each update. ::div{.blog-update-notes} **UPDATE:** Since this article was published, Node-RED Dashboard (1.0) has been [deprecated](https://discourse.nodered.org/t/announcement-node-red-dashboard-v1-deprecation-notice/89006). Instead, it is recommended to use [FlowFuse Dashboard (Dashboard 2.0)](https://dashboard.flowfuse.com/) which is a more modern and feature-rich dashboard solution for Node-RED. :: ### The Importance of Persisting Chart Data Storing the data in the chart node is fine to show prototypes of HMIs, but where it's vital the correct data is always shown we are going to need a backup. Data can easily be lost when you move your flow to a new device, restart your instance, or simply when upgrading Node-RED. How can we store our chart data so we can be confident it will be there each time a user views your HMI? ### Example Dashboard In this example, we are passing in a random number between one and 10 each second. With each new value received the chart updates and as mentioned about, the values are also stored in the chart node. If you'd like to see and edit the flows I've created, you can copy and paste the JSON below into your Node-RED import feature. ::render-flow ```json [{"id":"c6825b1001216b89","type":"inject","z":"668c56888fd0f960","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":110,"y":220,"wires":[["6b609d978540fb2a"]]},{"id":"6b609d978540fb2a","type":"Number","z":"668c56888fd0f960","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":270,"y":220,"wires":[["794846db6dc8cef8"]]},{"id":"794846db6dc8cef8","type":"ui_chart","z":"668c56888fd0f960","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":430,"y":220,"wires":[["ad53848ee4b0d91e"]]},{"id":"ad53848ee4b0d91e","type":"debug","z":"668c56888fd0f960","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":550,"y":220,"wires":[]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: x ### How can we store and recall the chart data? The chart node has a really useful feature which allows us to access all the data currently shown in the chart. Each time the chart receives new data, it's added to the existing values then the whole data set is sent out the outbound port of the chart node. Now that we have a way to easily access the chart data in a single payload, we next need to store that data somewhere safer. I'm going to explain 3 potential solutions, which I use on a regular basis. #### 1. Node-RED file-out and file-in nodes Node-RED can read and write data to a local filesystem. Being that we already have the chart data in a single payload, we just need to write that payload to a file for later use, which we can do using the file-out node. This example flow shows how to use the file-out node to write the chart data to your local filesystem. ::render-flow ```json [{"id":"ead9df683d29fb8a","type":"inject","z":"668c56888fd0f960","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":110,"y":560,"wires":[["ef5359b8bd3f78b3"]]},{"id":"ef5359b8bd3f78b3","type":"Number","z":"668c56888fd0f960","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":270,"y":560,"wires":[["69ad440cd8d1ce30"]]},{"id":"69ad440cd8d1ce30","type":"ui_chart","z":"668c56888fd0f960","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":430,"y":560,"wires":[["e4e7758028477505","d9d6a2e34767f568"]]},{"id":"e4e7758028477505","type":"debug","z":"668c56888fd0f960","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":550,"y":560,"wires":[]},{"id":"d9d6a2e34767f568","type":"json","z":"668c56888fd0f960","name":"","property":"payload","action":"","pretty":false,"x":550,"y":600,"wires":[["b5b020fb17f615df"]]},{"id":"b5b020fb17f615df","type":"file","z":"668c56888fd0f960","name":"","filename":"example.json","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":690,"y":600,"wires":[["a6d9eab41d4dcf97"]]},{"id":"a6d9eab41d4dcf97","type":"debug","z":"668c56888fd0f960","name":"debug 92","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":600,"wires":[]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: As the chart node sends the full data set each time new data is added, we overwrite the content of the file rather than append the new values. The next step is to pull the data back from the filesystem to your Node-RED instance. Node-RED makes this very easy using the file-in node. ::render-flow ```json [{"id":"ead9df683d29fb8a","type":"inject","z":"668c56888fd0f960","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":110,"y":560,"wires":[["ef5359b8bd3f78b3"]]},{"id":"ef5359b8bd3f78b3","type":"Number","z":"668c56888fd0f960","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":270,"y":560,"wires":[["69ad440cd8d1ce30"]]},{"id":"69ad440cd8d1ce30","type":"ui_chart","z":"668c56888fd0f960","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":430,"y":560,"wires":[["e4e7758028477505","d9d6a2e34767f568"]]},{"id":"e4e7758028477505","type":"debug","z":"668c56888fd0f960","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":550,"y":560,"wires":[]},{"id":"d9d6a2e34767f568","type":"json","z":"668c56888fd0f960","name":"","property":"payload","action":"","pretty":false,"x":550,"y":600,"wires":[["b5b020fb17f615df"]]},{"id":"b5b020fb17f615df","type":"file","z":"668c56888fd0f960","name":"","filename":"example.json","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":690,"y":600,"wires":[["a6d9eab41d4dcf97"]]},{"id":"a6d9eab41d4dcf97","type":"debug","z":"668c56888fd0f960","name":"debug 92","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":600,"wires":[]},{"id":"e9cb9350f1aaeb38","type":"inject","z":"668c56888fd0f960","name":"import data","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":110,"y":660,"wires":[["600f014947f73d8f"]]},{"id":"600f014947f73d8f","type":"file in","z":"668c56888fd0f960","name":"","filename":"example.json","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":270,"y":660,"wires":[["972118c0e114f47b","69ad440cd8d1ce30"]]},{"id":"972118c0e114f47b","type":"debug","z":"668c56888fd0f960","name":"debug 93","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":420,"y":660,"wires":[]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: When you press the 'import data' trigger node, the data is loaded in from the filesystem and shown in the chart. You may want to automate that task to run each you deploy your Node-RED instance. ![Import data on deploy](https://flowfuse.com/blog/2023/05/images/inject-on-deploy.png "Import data on deploy") Bear in mind that your data is stored in your filesystem, if your storage drive fails you will lose your data, you might want to consider taking backups and storing elsewhere for emergencies. #### 2. FlowFuse's persistent context FlowFuse Cloud and premium self hosted version provides persistent context storage as part of its Node-RED instances. This allows you to create, read, update, and delete data as needed, even if you have restarted a Node-RED instance. This flow shows chart data being sent to persistent context so we can access it later. The process is very similar to using the file-out and file-in nodes. ::render-flow ```json [{"id":"c6825b1001216b89","type":"inject","z":"4767c2f7095bee53","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170,"y":100,"wires":[["6b609d978540fb2a"]]},{"id":"794846db6dc8cef8","type":"ui_chart","z":"4767c2f7095bee53","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":490,"y":100,"wires":[["ad53848ee4b0d91e","938c7d878545e623"]]},{"id":"ad53848ee4b0d91e","type":"debug","z":"4767c2f7095bee53","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":610,"y":100,"wires":[]},{"id":"6b609d978540fb2a","type":"Number","z":"4767c2f7095bee53","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":330,"y":100,"wires":[["794846db6dc8cef8"]]},{"id":"938c7d878545e623","type":"change","z":"4767c2f7095bee53","name":"","rules":[{"t":"set","p":"#:(persistent)::chart-data","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":140,"wires":[["3792cc96a748e75a"]]},{"id":"3792cc96a748e75a","type":"debug","z":"4767c2f7095bee53","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":140,"wires":[]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: We now need to have a method to load the data back into our chart. We will again use a manual 'import data' trigger to load the full set of data from the persistent context, and then push it back into the chart. ::render-flow ```json [{"id":"c6825b1001216b89","type":"inject","z":"4767c2f7095bee53","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170,"y":100,"wires":[["6b609d978540fb2a"]]},{"id":"794846db6dc8cef8","type":"ui_chart","z":"4767c2f7095bee53","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":490,"y":100,"wires":[["ad53848ee4b0d91e","938c7d878545e623"]]},{"id":"ad53848ee4b0d91e","type":"debug","z":"4767c2f7095bee53","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":610,"y":100,"wires":[]},{"id":"6b609d978540fb2a","type":"Number","z":"4767c2f7095bee53","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":330,"y":100,"wires":[["794846db6dc8cef8"]]},{"id":"938c7d878545e623","type":"change","z":"4767c2f7095bee53","name":"","rules":[{"t":"set","p":"#:(persistent)::chart-data","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":140,"wires":[["3792cc96a748e75a"]]},{"id":"3792cc96a748e75a","type":"debug","z":"4767c2f7095bee53","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":140,"wires":[]},{"id":"3379276c77b4691c","type":"inject","z":"4767c2f7095bee53","name":"import data","props":[],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":150,"y":180,"wires":[["ddd1ef41321fb4a6"]]},{"id":"ddd1ef41321fb4a6","type":"change","z":"4767c2f7095bee53","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"#:(persistent)::chart-data","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":180,"wires":[["794846db6dc8cef8","fa65b958e34bc971"]]},{"id":"fa65b958e34bc971","type":"debug","z":"4767c2f7095bee53","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":480,"y":180,"wires":[]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: You may have noticed that when we are pushing duplicate data into the chart it automatically checks to see if the data is already stored. If the data points are already in the chart the new data is disregard. This saves us writing an extra section of the flow to delete the data before we load it in. #### 3. Expose the data via an API then manually import it In some cases you may want to copy your chart data to somewhere outside of your Node-RED instances. You can do this by creating a simple API which allows an outside system to request the chart data. You can also manually go to the URL of the API in a web browser to get your data. This example flow allows a user or system to access the chart data via a URL. ::render-flow ```json [{"id":"6523e86042252710","type":"inject","z":"4767c2f7095bee53","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":540,"wires":[["d51aba5f9a808592"]]},{"id":"427c60f2c4f523b7","type":"ui_chart","z":"4767c2f7095bee53","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":470,"y":540,"wires":[["ba84cd1820120139","d9087436f4769691"]]},{"id":"ba84cd1820120139","type":"debug","z":"4767c2f7095bee53","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":590,"y":540,"wires":[]},{"id":"d51aba5f9a808592","type":"Number","z":"4767c2f7095bee53","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":310,"y":540,"wires":[["427c60f2c4f523b7"]]},{"id":"d9087436f4769691","type":"change","z":"4767c2f7095bee53","name":"","rules":[{"t":"set","p":"chart-data","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":580,"wires":[[]]},{"id":"4dd97b29430ad2ba","type":"http in","z":"4767c2f7095bee53","name":"","url":"/data","method":"get","upload":false,"swaggerDoc":"","x":200,"y":640,"wires":[["b4f0eb085cc91834"]]},{"id":"7ca49e5465699355","type":"http response","z":"4767c2f7095bee53","name":"","statusCode":"","headers":{},"x":670,"y":640,"wires":[]},{"id":"b4f0eb085cc91834","type":"change","z":"4767c2f7095bee53","name":"Get the chart data from flow.chart-data","rules":[{"t":"set","p":"payload","pt":"msg","to":"chart-data","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":640,"wires":[["7ca49e5465699355"]]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: We can now access the data by simply visiting the URL of the API. ![The chart data accessed via the API in a web browser](https://flowfuse.com/blog/2023/05/images/data-in-browser.png "The chart data accessed via the API in a web browser") Bear in mind that you should secure the API as appropriate for the data. If you don't put security around the API, anyone on the same network as your Node-RED instance can access your chart data. Potentially that could give access to our data to the whole internet, so where needed take steps to keep your data safe. You can read more about securing Node-RED in our [blog post here](https://flowfuse.com/blog/2023/04/securing-node-red-in-production/). We now need to get that data back into a Node-RED instance. We can do that by editing a node and pasting in the data we got from the API. The flow below shows where you can paste in your data, you will then need to deploy and manually trigger 'import data'. ![Inject the data in JSON format](https://flowfuse.com/blog/2023/05/images/inject-the-json.png "Inject the data in JSON format") ::render-flow ```json [{"id":"6523e86042252710","type":"inject","z":"4767c2f7095bee53","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":540,"wires":[["d51aba5f9a808592"]]},{"id":"427c60f2c4f523b7","type":"ui_chart","z":"4767c2f7095bee53","name":"","group":"af1535b39b74f94a","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":470,"y":540,"wires":[["ba84cd1820120139","d9087436f4769691"]]},{"id":"ba84cd1820120139","type":"debug","z":"4767c2f7095bee53","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":590,"y":540,"wires":[]},{"id":"d51aba5f9a808592","type":"Number","z":"4767c2f7095bee53","name":"Random Number","minimum":"1","maximum":"10","roundTo":"0","Floor":true,"x":310,"y":540,"wires":[["427c60f2c4f523b7"]]},{"id":"d9087436f4769691","type":"change","z":"4767c2f7095bee53","name":"","rules":[{"t":"set","p":"chart-data","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":580,"wires":[[]]},{"id":"4dd97b29430ad2ba","type":"http in","z":"4767c2f7095bee53","name":"","url":"/data","method":"get","upload":false,"swaggerDoc":"","x":200,"y":660,"wires":[["b4f0eb085cc91834"]]},{"id":"7ca49e5465699355","type":"http response","z":"4767c2f7095bee53","name":"","statusCode":"","headers":{},"x":670,"y":660,"wires":[]},{"id":"b4f0eb085cc91834","type":"change","z":"4767c2f7095bee53","name":"Get the chart data from flow.chart-data","rules":[{"t":"set","p":"payload","pt":"msg","to":"chart-data","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":660,"wires":[["7ca49e5465699355"]]},{"id":"d13d16b33ec638b2","type":"inject","z":"4767c2f7095bee53","name":"import data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"[{\"series\":[\"\"],\"data\":[[{\"x\":1684841975036,\"y\":8},{\"x\":1684841976037,\"y\":6},{\"x\":1684841977038,\"y\":7},{\"x\":1684841978037,\"y\":7}]],\"labels\":[\"\"]}]","payloadType":"json","x":170,"y":600,"wires":[["427c60f2c4f523b7"]]},{"id":"af1535b39b74f94a","type":"ui_group","name":"Example","tab":"14f1442eb7525190","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"14f1442eb7525190","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}] ``` :: Each of these solutions has strengths and weaknesses but there are many other ways to persist your chart data. You should consider which approach is the best fit for your needs. To be as confident as possible that you data is safe, you may decide to push your data to dedicate external storage such as a database or backup solution. ### Conclusion Node-RED Dashboard 1 allows you to easily make informative HMIs, but it's important to make sure the chart data you are showing is stored safely. The approaches we have discussed above should give you a good start in ensuring your charts are populated with the correct data, even if your Node-RED instance crashes or you need to move it to a new hosting location. # Node-RED Tips - Dashboard Edition There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows. In this Node-RED Tips article, we are going to focus on [Node-RED Dashboard](https://flows.nodered.org/node/node-red-dashboard){rel=""nofollow""}. Dashboard is a great tool for creating HMI (Human Machine Interfaces), it's also the most popular custom node for Node-RED with thousands of downloads per week. ### 1. Responsive layouts (almost) Responsive design is the ability for a webpage to change its content to best fit the features of a device used to view the page. For example, when viewing a graph on a mobile phone or a laptop the available screen space differs significantly in size as well as aspect-ratio. Dashboard doesn't offer the feature to change graph sizes based on the screen of a viewing device. That being said, there is one trick you can use to make your dashboards a lot more useful on small and large screens alike. Place your content into Dashboard 'groups', those groups can make use of wider screens by sitting side by side where the screen is big enough while stacking vertically on smaller devices. The image below shows what happens when you change the screen size for this dashboard. :video{ariaLabel="Changing the aspect ratio of the screen" autoPlay="true" height="437" loop="true" muted="true" playsInline="true" preload="none" width="600"} If you'd like to try this out on your own Node-RED, you can import the flow below. ::render-flow{:height='500'} ```json [{"id":"e351b1251dfbc2f7","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"d388b48fcbed93a1","type":"ui_gauge","z":"e351b1251dfbc2f7","name":"","group":"ba1ff527abfa5261","order":2,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":350,"y":80,"wires":[]},{"id":"80780894450cfb6d","type":"ui_button","z":"e351b1251dfbc2f7","name":"","group":"ba1ff527abfa5261","order":1,"width":0,"height":0,"passthru":false,"label":"Update","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":80,"y":80,"wires":[["49e78ff51c3a1ea3"]]},{"id":"07b44990e5d5b5f6","type":"ui_chart","z":"e351b1251dfbc2f7","name":"","group":"ba1ff527abfa5261","order":3,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":350,"y":120,"wires":[[]]},{"id":"1482bcf69325aa92","type":"inject","z":"e351b1251dfbc2f7","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":90,"y":120,"wires":[["49e78ff51c3a1ea3"]]},{"id":"49e78ff51c3a1ea3","type":"random","z":"e351b1251dfbc2f7","name":"","low":"0","high":"100","inte":"true","property":"payload","x":220,"y":100,"wires":[["d388b48fcbed93a1","07b44990e5d5b5f6"]]},{"id":"38996d8eb9f6535d","type":"ui_gauge","z":"e351b1251dfbc2f7","name":"","group":"f6052a3dccc77ea3","order":2,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":350,"y":240,"wires":[]},{"id":"cf5599fbda28e614","type":"ui_button","z":"e351b1251dfbc2f7","name":"","group":"f6052a3dccc77ea3","order":1,"width":0,"height":0,"passthru":false,"label":"Update","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":80,"y":240,"wires":[["f093c63e17a620ba"]]},{"id":"936438ebf9eef986","type":"ui_chart","z":"e351b1251dfbc2f7","name":"","group":"f6052a3dccc77ea3","order":3,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":350,"y":280,"wires":[[]]},{"id":"60a40faa335f943e","type":"inject","z":"e351b1251dfbc2f7","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":90,"y":280,"wires":[["f093c63e17a620ba"]]},{"id":"f093c63e17a620ba","type":"random","z":"e351b1251dfbc2f7","name":"","low":"0","high":"100","inte":"true","property":"payload","x":220,"y":260,"wires":[["38996d8eb9f6535d","936438ebf9eef986"]]},{"id":"e284b90164e648e6","type":"ui_gauge","z":"e351b1251dfbc2f7","name":"","group":"1e4a72d62ed7564c","order":2,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":350,"y":380,"wires":[]},{"id":"cabe7a8150dbfaf6","type":"ui_button","z":"e351b1251dfbc2f7","name":"","group":"1e4a72d62ed7564c","order":1,"width":0,"height":0,"passthru":false,"label":"Update","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":80,"y":380,"wires":[["7b9862186954c4b3"]]},{"id":"ac116116c56e6c3c","type":"ui_chart","z":"e351b1251dfbc2f7","name":"","group":"1e4a72d62ed7564c","order":3,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":350,"y":420,"wires":[[]]},{"id":"bb27ef41380ec1d2","type":"inject","z":"e351b1251dfbc2f7","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":90,"y":420,"wires":[["7b9862186954c4b3"]]},{"id":"7b9862186954c4b3","type":"random","z":"e351b1251dfbc2f7","name":"","low":"0","high":"100","inte":"true","property":"payload","x":220,"y":400,"wires":[["e284b90164e648e6","ac116116c56e6c3c"]]},{"id":"e891f555bad51f01","type":"ui_gauge","z":"e351b1251dfbc2f7","name":"","group":"fcc4481a9e329266","order":2,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":350,"y":500,"wires":[]},{"id":"fa090cd1a0e97885","type":"ui_button","z":"e351b1251dfbc2f7","name":"","group":"fcc4481a9e329266","order":1,"width":0,"height":0,"passthru":false,"label":"Update","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":80,"y":500,"wires":[["bb4e945a2a8e681c"]]},{"id":"1c3a8aa84dfc85fe","type":"ui_chart","z":"e351b1251dfbc2f7","name":"","group":"fcc4481a9e329266","order":3,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":350,"y":540,"wires":[[]]},{"id":"796f79538c15dd66","type":"inject","z":"e351b1251dfbc2f7","name":"","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":90,"y":540,"wires":[["bb4e945a2a8e681c"]]},{"id":"bb4e945a2a8e681c","type":"random","z":"e351b1251dfbc2f7","name":"","low":"0","high":"100","inte":"true","property":"payload","x":220,"y":520,"wires":[["e891f555bad51f01","1c3a8aa84dfc85fe"]]},{"id":"ba1ff527abfa5261","type":"ui_group","name":"Machine 1","tab":"39383a7a648193dd","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"f6052a3dccc77ea3","type":"ui_group","name":"Machine 2","tab":"39383a7a648193dd","order":2,"disp":true,"width":"6","collapse":false,"className":""},{"id":"1e4a72d62ed7564c","type":"ui_group","name":"Machine 3","tab":"39383a7a648193dd","order":3,"disp":true,"width":"6","collapse":false,"className":""},{"id":"fcc4481a9e329266","type":"ui_group","name":"Machine 4","tab":"39383a7a648193dd","order":4,"disp":true,"width":"6","collapse":false,"className":""},{"id":"39383a7a648193dd","type":"ui_tab","name":"Node-RED Tips","icon":"dashboard","disabled":false,"hidden":false}] ``` :: ### 2. Add more than one series of data to a line chart Being able to add more than one series of data to a single chart can make the data far more useful. One great way to use this is to compare the same data from different sensors. In this example I'm going to show the external and internal temperature at a location on the same chart. ![Graphing two series on the same line chart](https://flowfuse.com/blog/2023/06/images/temp-graph.png "Graphing two series on the same line chart") To do this you need to give a different msg.topic to each series, you can add that using a change node before passing the data to the chart. If you'd like to view this chart on your own Node-RED, you can import the flow below. ::render-flow ```json [{"id":"3eb08d4843164efc","type":"ui_chart","z":"58569b35dacd54f3","name":"","group":"41e847ff22249c0e","order":3,"width":"12","height":"5","label":"Celsius","chartType":"line","legend":"true","xformat":"dd HH:mm","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"604800","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":280,"y":180,"wires":[[]]},{"id":"c4a13220356f76d3","type":"inject","z":"58569b35dacd54f3","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"series\":[\"inside\",\"outside\"],\"data\":[[{\"x\":1685015544647,\"y\":20.8},{\"x\":1685015844687,\"y\":20.8},{\"x\":1685016144791,\"y\":20.8},{\"x\":1685016444933,\"y\":20.8},{\"x\":1685016745032,\"y\":20.8},{\"x\":1685017045123,\"y\":20.8},{\"x\":1685017345223,\"y\":20.8},{\"x\":1685017645339,\"y\":20.8},{\"x\":1685017945424,\"y\":20.8},{\"x\":1685018245500,\"y\":20.8},{\"x\":1685018545670,\"y\":20.8},{\"x\":1685018860580,\"y\":20.8},{\"x\":1685019160814,\"y\":20.8},{\"x\":1685019460832,\"y\":20.8},{\"x\":1685019760943,\"y\":20.8},{\"x\":1685020061037,\"y\":20.8},{\"x\":1685020361249,\"y\":20.8},{\"x\":1685020661264,\"y\":20.8},{\"x\":1685020961381,\"y\":20.8},{\"x\":1685021261473,\"y\":20.8},{\"x\":1685021561721,\"y\":20.8},{\"x\":1685021876580,\"y\":20.6},{\"x\":1685022176696,\"y\":20.7},{\"x\":1685022476852,\"y\":20.8},{\"x\":1685022776957,\"y\":20.8},{\"x\":1685023077032,\"y\":20.8},{\"x\":1685023377120,\"y\":20.8},{\"x\":1685023677234,\"y\":20.8},{\"x\":1685023977339,\"y\":20.8},{\"x\":1685024277455,\"y\":20.8},{\"x\":1685024577576,\"y\":20.8},{\"x\":1685024892578,\"y\":20.8},{\"x\":1685025192616,\"y\":20.8},{\"x\":1685025492757,\"y\":20.8},{\"x\":1685025792847,\"y\":20.7},{\"x\":1685026092949,\"y\":20.8},{\"x\":1685026393052,\"y\":20.8},{\"x\":1685026693174,\"y\":20.8},{\"x\":1685026993234,\"y\":20.8},{\"x\":1685027293387,\"y\":20.8},{\"x\":1685027593514,\"y\":20.8},{\"x\":1685027908550,\"y\":20.8},{\"x\":1685028208624,\"y\":20.8},{\"x\":1685028508708,\"y\":20.8},{\"x\":1685028808828,\"y\":20.8},{\"x\":1685029108909,\"y\":20.8},{\"x\":1685029409026,\"y\":20.8},{\"x\":1685029709096,\"y\":20.8},{\"x\":1685030009262,\"y\":20.8},{\"x\":1685030309361,\"y\":20.8},{\"x\":1685030609541,\"y\":20.8},{\"x\":1685030924565,\"y\":20.8},{\"x\":1685031239588,\"y\":20.8},{\"x\":1685031539702,\"y\":20.8},{\"x\":1685031839784,\"y\":20.8},{\"x\":1685032139922,\"y\":20.8},{\"x\":1685032440027,\"y\":20.8},{\"x\":1685032740134,\"y\":20.6},{\"x\":1685033040252,\"y\":20.7},{\"x\":1685033340317,\"y\":20.5},{\"x\":1685033640450,\"y\":20.5},{\"x\":1685033940507,\"y\":20.6},{\"x\":1685034240592,\"y\":20.5},{\"x\":1685034540633,\"y\":20.4},{\"x\":1685034840784,\"y\":20.4},{\"x\":1685035140888,\"y\":20.5},{\"x\":1685035441026,\"y\":20.4},{\"x\":1685035741107,\"y\":20.5},{\"x\":1685036041205,\"y\":20.5},{\"x\":1685036341328,\"y\":20.5},{\"x\":1685036641457,\"y\":20.5},{\"x\":1685036941583,\"y\":20.4},{\"x\":1685037256578,\"y\":20.5},{\"x\":1685037556715,\"y\":20.5},{\"x\":1685037856773,\"y\":20.5},{\"x\":1685038156902,\"y\":20.5},{\"x\":1685038457023,\"y\":20.5},{\"x\":1685038757090,\"y\":20.5},{\"x\":1685039057219,\"y\":20.5},{\"x\":1685039357278,\"y\":20.5},{\"x\":1685039657449,\"y\":20.5},{\"x\":1685039957553,\"y\":20.5},{\"x\":1685040257570,\"y\":20.5},{\"x\":1685040557615,\"y\":20.5},{\"x\":1685040857735,\"y\":20.4},{\"x\":1685041157843,\"y\":20.4},{\"x\":1685041457971,\"y\":20.5},{\"x\":1685041758072,\"y\":20.5},{\"x\":1685042058154,\"y\":20.5},{\"x\":1685042358273,\"y\":20.5},{\"x\":1685042658392,\"y\":20.5},{\"x\":1685042958486,\"y\":20.6},{\"x\":1685043258518,\"y\":20.6},{\"x\":1685043558568,\"y\":20.6},{\"x\":1685043858688,\"y\":20.5},{\"x\":1685044158769,\"y\":20.5},{\"x\":1685044458908,\"y\":20.5},{\"x\":1685044758984,\"y\":20.5},{\"x\":1685045059095,\"y\":20.5},{\"x\":1685045359169,\"y\":20.5},{\"x\":1685045659320,\"y\":20.5},{\"x\":1685045959367,\"y\":20.5},{\"x\":1685046259469,\"y\":20.7},{\"x\":1685046559497,\"y\":20.6},{\"x\":1685046859589,\"y\":20.5},{\"x\":1685047159647,\"y\":20.6},{\"x\":1685047459714,\"y\":20.6},{\"x\":1685047759793,\"y\":20.7},{\"x\":1685048059921,\"y\":20.7},{\"x\":1685048359994,\"y\":20.6},{\"x\":1685048659997,\"y\":20.7},{\"x\":1685048960056,\"y\":20.7},{\"x\":1685049260070,\"y\":20.8},{\"x\":1685049560116,\"y\":20.8},{\"x\":1685049860167,\"y\":20.8},{\"x\":1685050160212,\"y\":20.8},{\"x\":1685050460281,\"y\":20.8},{\"x\":1685050760368,\"y\":20.8},{\"x\":1685051060400,\"y\":20.7},{\"x\":1685051360435,\"y\":20.8},{\"x\":1685051660553,\"y\":20.8},{\"x\":1685051960585,\"y\":20.8},{\"x\":1685052260665,\"y\":20.8},{\"x\":1685052560675,\"y\":20.8},{\"x\":1685052860700,\"y\":20.7},{\"x\":1685053160749,\"y\":20.8},{\"x\":1685053460790,\"y\":20.8},{\"x\":1685053760855,\"y\":20.7},{\"x\":1685054060931,\"y\":20.7},{\"x\":1685054361019,\"y\":20.7},{\"x\":1685054661030,\"y\":20.7},{\"x\":1685054961121,\"y\":20.7},{\"x\":1685055261227,\"y\":20.7},{\"x\":1685055576250,\"y\":20.7},{\"x\":1685055891289,\"y\":20.5},{\"x\":1685056191290,\"y\":20.6},{\"x\":1685056491397,\"y\":20.7},{\"x\":1685056791444,\"y\":20.7},{\"x\":1685057091489,\"y\":20.7},{\"x\":1685057391516,\"y\":20.7},{\"x\":1685057691607,\"y\":20.7},{\"x\":1685057991660,\"y\":20.7},{\"x\":1685058291741,\"y\":20.6},{\"x\":1685058606760,\"y\":20.5},{\"x\":1685058906775,\"y\":20.5},{\"x\":1685059206866,\"y\":20.5},{\"x\":1685059506900,\"y\":20.5},{\"x\":1685059806979,\"y\":20.4},{\"x\":1685060107035,\"y\":20.5},{\"x\":1685060407099,\"y\":20.5},{\"x\":1685060707132,\"y\":20.4},{\"x\":1685061007199,\"y\":20.5},{\"x\":1685061307220,\"y\":20.4},{\"x\":1685061607228,\"y\":20.5},{\"x\":1685061907286,\"y\":20.6},{\"x\":1685062207289,\"y\":20.7},{\"x\":1685062507315,\"y\":20.6},{\"x\":1685062807355,\"y\":20.7},{\"x\":1685063107395,\"y\":20.7},{\"x\":1685063407413,\"y\":20.7},{\"x\":1685063707465,\"y\":20.7},{\"x\":1685064007492,\"y\":20.7},{\"x\":1685064307565,\"y\":20.7},{\"x\":1685064607587,\"y\":20.7},{\"x\":1685064907609,\"y\":20.7},{\"x\":1685065207618,\"y\":20.5},{\"x\":1685065507642,\"y\":20.4},{\"x\":1685065807670,\"y\":20.4},{\"x\":1685066107679,\"y\":20.4},{\"x\":1685066407758,\"y\":20.5},{\"x\":1685066707760,\"y\":20.3},{\"x\":1685067007798,\"y\":20.4},{\"x\":1685067307865,\"y\":20.4},{\"x\":1685067622903,\"y\":20.4},{\"x\":1685067937922,\"y\":20.4},{\"x\":1685068237945,\"y\":20.4},{\"x\":1685068537971,\"y\":20.3},{\"x\":1685068838009,\"y\":20.3},{\"x\":1685069138060,\"y\":20.2},{\"x\":1685069453065,\"y\":20.3},{\"x\":1685069753104,\"y\":20.3},{\"x\":1685070053108,\"y\":20.2},{\"x\":1685070353140,\"y\":20.3},{\"x\":1685070653170,\"y\":20.2},{\"x\":1685070953219,\"y\":20.2},{\"x\":1685071253232,\"y\":20.2},{\"x\":1685071553258,\"y\":20},{\"x\":1685071853260,\"y\":20},{\"x\":1685072153324,\"y\":20.2},{\"x\":1685072453329,\"y\":20.2},{\"x\":1685072753343,\"y\":19.9},{\"x\":1685073053359,\"y\":20},{\"x\":1685073353402,\"y\":19.9},{\"x\":1685073653411,\"y\":19.9},{\"x\":1685073953453,\"y\":20},{\"x\":1685074253462,\"y\":20},{\"x\":1685074553509,\"y\":20},{\"x\":1685074853529,\"y\":20},{\"x\":1685075153554,\"y\":20},{\"x\":1685075453556,\"y\":20},{\"x\":1685075753590,\"y\":20},{\"x\":1685076053642,\"y\":20.2},{\"x\":1685076368644,\"y\":20},{\"x\":1685076668676,\"y\":20.2},{\"x\":1685076968685,\"y\":20.3},{\"x\":1685077268752,\"y\":20.3},{\"x\":1685077583729,\"y\":20.3},{\"x\":1685077883800,\"y\":20.2},{\"x\":1685078198760,\"y\":20.2},{\"x\":1685078498762,\"y\":20.4},{\"x\":1685078798782,\"y\":20.4},{\"x\":1685079098837,\"y\":20.5},{\"x\":1685079398903,\"y\":20.5},{\"x\":1685079698965,\"y\":20.6},{\"x\":1685079999013,\"y\":20.7},{\"x\":1685080299091,\"y\":20.8},{\"x\":1685080599234,\"y\":20.8},{\"x\":1685080899276,\"y\":20.8},{\"x\":1685081199340,\"y\":20.8},{\"x\":1685081499403,\"y\":20.8},{\"x\":1685081799441,\"y\":20.8},{\"x\":1685082099467,\"y\":20.9},{\"x\":1685082399584,\"y\":20.8},{\"x\":1685082699624,\"y\":21},{\"x\":1685082999670,\"y\":21},{\"x\":1685083299724,\"y\":21.2},{\"x\":1685083599810,\"y\":21.2},{\"x\":1685083899884,\"y\":21.2},{\"x\":1685084199935,\"y\":21.2},{\"x\":1685084500001,\"y\":21.3},{\"x\":1685084800063,\"y\":21.4},{\"x\":1685085115072,\"y\":21.5},{\"x\":1685085415170,\"y\":21.5},{\"x\":1685085715226,\"y\":21.5},{\"x\":1685086015289,\"y\":21.5},{\"x\":1685086315365,\"y\":21.5},{\"x\":1685086615447,\"y\":21.5},{\"x\":1685086915501,\"y\":21.7},{\"x\":1685087215603,\"y\":21.7},{\"x\":1685087515642,\"y\":21.6},{\"x\":1685087815732,\"y\":21.6},{\"x\":1685088115759,\"y\":21.7},{\"x\":1685088415793,\"y\":21.9},{\"x\":1685088715840,\"y\":21.9},{\"x\":1685089015891,\"y\":21.9},{\"x\":1685089315950,\"y\":21.9},{\"x\":1685089616045,\"y\":21.9},{\"x\":1685089916094,\"y\":21.9},{\"x\":1685090216153,\"y\":21.9},{\"x\":1685090516202,\"y\":21.9},{\"x\":1685090816299,\"y\":21.9},{\"x\":1685091116304,\"y\":22},{\"x\":1685091416326,\"y\":22},{\"x\":1685091716409,\"y\":22},{\"x\":1685092016500,\"y\":22},{\"x\":1685092316555,\"y\":21.9},{\"x\":1685092616597,\"y\":21.9},{\"x\":1685092916687,\"y\":21.9},{\"x\":1685093216749,\"y\":21.9},{\"x\":1685093516770,\"y\":21.9},{\"x\":1685093816833,\"y\":21.8},{\"x\":1685094116862,\"y\":21.8},{\"x\":1685094416941,\"y\":21.9},{\"x\":1685094717014,\"y\":21.8},{\"x\":1685095017079,\"y\":21.8},{\"x\":1685095317146,\"y\":21.9},{\"x\":1685095617198,\"y\":21.9},{\"x\":1685095917267,\"y\":22},{\"x\":1685096217380,\"y\":22},{\"x\":1685096517436,\"y\":22.2},{\"x\":1685096817481,\"y\":22.1},{\"x\":1685097117519,\"y\":21.9},{\"x\":1685097417582,\"y\":21.9},{\"x\":1685097717607,\"y\":21.9},{\"x\":1685098017734,\"y\":21.9},{\"x\":1685098317791,\"y\":21.9},{\"x\":1685098617850,\"y\":21.9},{\"x\":1685098917939,\"y\":21.9},{\"x\":1685099218020,\"y\":21.9},{\"x\":1685099518088,\"y\":21.9},{\"x\":1685099818190,\"y\":21.9},{\"x\":1685100118269,\"y\":21.9},{\"x\":1685100418304,\"y\":21.9},{\"x\":1685100718355,\"y\":21.9},{\"x\":1685101018437,\"y\":22},{\"x\":1685101318544,\"y\":22.1},{\"x\":1685101618646,\"y\":22},{\"x\":1685101918720,\"y\":22},{\"x\":1685102218803,\"y\":22},{\"x\":1685102518914,\"y\":21.9},{\"x\":1685102818997,\"y\":21.9},{\"x\":1685103119093,\"y\":21.9},{\"x\":1685103419119,\"y\":21.9},{\"x\":1685103719183,\"y\":21.9},{\"x\":1685104019263,\"y\":21.9},{\"x\":1685104319361,\"y\":21.9},{\"x\":1685104619467,\"y\":21.9},{\"x\":1685104919565,\"y\":21.9},{\"x\":1685105219692,\"y\":21.9},{\"x\":1685105261657,\"y\":21.9},{\"x\":1685105275726,\"y\":21.9},{\"x\":1685105316848,\"y\":21.9},{\"x\":1685105388676,\"y\":21.9},{\"x\":1685105475001,\"y\":21.9},{\"x\":1685105501466,\"y\":21.9},{\"x\":1685105576628,\"y\":21.9},{\"x\":1685105627162,\"y\":21.9},{\"x\":1685105653330,\"y\":21.9},{\"x\":1685105684112,\"y\":21.9},{\"x\":1685105722222,\"y\":21.9},{\"x\":1685105769629,\"y\":21.9},{\"x\":1685105825666,\"y\":21.9},{\"x\":1685105889137,\"y\":21.9},{\"x\":1685105910507,\"y\":21.9},{\"x\":1685105970749,\"y\":21.9},{\"x\":1685106270828,\"y\":21.9},{\"x\":1685106570844,\"y\":21.9},{\"x\":1685106870845,\"y\":21.9},{\"x\":1685107170872,\"y\":21.9},{\"x\":1685107485846,\"y\":21.9},{\"x\":1685107785884,\"y\":21.9},{\"x\":1685108100859,\"y\":21.9},{\"x\":1685108400859,\"y\":21.9},{\"x\":1685108700881,\"y\":21.9},{\"x\":1685109000885,\"y\":21.9},{\"x\":1685109300898,\"y\":21.9},{\"x\":1685109615915,\"y\":21.8},{\"x\":1685109930873,\"y\":21.9},{\"x\":1685110230928,\"y\":21.9},{\"x\":1685110545931,\"y\":21.7},{\"x\":1685110845948,\"y\":21.7},{\"x\":1685111145989,\"y\":21.7},{\"x\":1685111460988,\"y\":21.7},{\"x\":1685111761062,\"y\":21.8},{\"x\":1685112061067,\"y\":21.6},{\"x\":1685112361107,\"y\":21.7},{\"x\":1685112661141,\"y\":21.7},{\"x\":1685112961145,\"y\":21.8},{\"x\":1685113261191,\"y\":21.8},{\"x\":1685113561195,\"y\":21.8},{\"x\":1685113861234,\"y\":21.8},{\"x\":1685114161272,\"y\":21.8},{\"x\":1685114461288,\"y\":21.7},{\"x\":1685114761326,\"y\":21.7},{\"x\":1685115061359,\"y\":21.7},{\"x\":1685115361389,\"y\":21.7},{\"x\":1685115661413,\"y\":21.6},{\"x\":1685115961522,\"y\":21.7},{\"x\":1685116276440,\"y\":21.7},{\"x\":1685116576471,\"y\":21.5},{\"x\":1685116876500,\"y\":21.5},{\"x\":1685117176511,\"y\":21.5},{\"x\":1685117476538,\"y\":21.6},{\"x\":1685117776599,\"y\":21.6},{\"x\":1685118091580,\"y\":21.8},{\"x\":1685118391620,\"y\":21.6},{\"x\":1685118691646,\"y\":21.7},{\"x\":1685118991656,\"y\":21.7},{\"x\":1685119291661,\"y\":21.5},{\"x\":1685119591684,\"y\":21.5},{\"x\":1685119891711,\"y\":21.5},{\"x\":1685120191732,\"y\":21.5},{\"x\":1685120491763,\"y\":21.5},{\"x\":1685120791797,\"y\":21.4},{\"x\":1685121091832,\"y\":21.2},{\"x\":1685121406802,\"y\":21.2},{\"x\":1685121706853,\"y\":21.1},{\"x\":1685122021885,\"y\":21.1},{\"x\":1685122336872,\"y\":21.2},{\"x\":1685122636878,\"y\":21.2},{\"x\":1685122936903,\"y\":21.1},{\"x\":1685123236933,\"y\":21},{\"x\":1685123536988,\"y\":21},{\"x\":1685123837007,\"y\":21.1},{\"x\":1685124137007,\"y\":21},{\"x\":1685124437098,\"y\":21},{\"x\":1685124737193,\"y\":21},{\"x\":1685125037270,\"y\":21},{\"x\":1685125337370,\"y\":20.9},{\"x\":1685125637493,\"y\":20.9},{\"x\":1685125937530,\"y\":21},{\"x\":1685126237627,\"y\":20.9},{\"x\":1685126537712,\"y\":20.8},{\"x\":1685126837733,\"y\":20.8},{\"x\":1685127137746,\"y\":20.8},{\"x\":1685127437870,\"y\":20.8},{\"x\":1685127737940,\"y\":20.8},{\"x\":1685128038026,\"y\":20.8},{\"x\":1685128338109,\"y\":20.8},{\"x\":1685128638215,\"y\":20.8},{\"x\":1685128938321,\"y\":20.8},{\"x\":1685129238409,\"y\":20.8},{\"x\":1685129538484,\"y\":20.8},{\"x\":1685129838514,\"y\":20.8},{\"x\":1685130138614,\"y\":20.8},{\"x\":1685130438644,\"y\":20.8},{\"x\":1685130738716,\"y\":20.8},{\"x\":1685131038856,\"y\":20.8},{\"x\":1685131338912,\"y\":20.8},{\"x\":1685131639028,\"y\":20.8},{\"x\":1685131939140,\"y\":20.8},{\"x\":1685132239222,\"y\":20.8},{\"x\":1685132539344,\"y\":20.8},{\"x\":1685132839390,\"y\":20.8},{\"x\":1685133154381,\"y\":20.8},{\"x\":1685133454402,\"y\":20.8},{\"x\":1685133754494,\"y\":20.8},{\"x\":1685134054557,\"y\":20.8},{\"x\":1685134354644,\"y\":20.8},{\"x\":1685134654753,\"y\":20.8},{\"x\":1685134954810,\"y\":20.8},{\"x\":1685135254894,\"y\":20.7},{\"x\":1685135554961,\"y\":20.8},{\"x\":1685135854990,\"y\":20.8},{\"x\":1685136155013,\"y\":20.8},{\"x\":1685136455070,\"y\":20.8},{\"x\":1685136755172,\"y\":20.8},{\"x\":1685137055242,\"y\":20.8},{\"x\":1685137355339,\"y\":20.8},{\"x\":1685137655440,\"y\":20.7},{\"x\":1685137955504,\"y\":20.7},{\"x\":1685138255592,\"y\":20.6},{\"x\":1685138555675,\"y\":20.6},{\"x\":1685138855742,\"y\":20.6},{\"x\":1685139155750,\"y\":20.6},{\"x\":1685139455841,\"y\":20.5},{\"x\":1685139755904,\"y\":20.6},{\"x\":1685140055961,\"y\":20.5},{\"x\":1685140356100,\"y\":20.6},{\"x\":1685140656187,\"y\":20.5},{\"x\":1685140956268,\"y\":20.4},{\"x\":1685141256384,\"y\":20.4},{\"x\":1685141556467,\"y\":20.4},{\"x\":1685141856547,\"y\":20.4},{\"x\":1685142156630,\"y\":20.3},{\"x\":1685142456678,\"y\":20.2},{\"x\":1685142756713,\"y\":20.3},{\"x\":1685143056833,\"y\":20.3},{\"x\":1685143356922,\"y\":20.3},{\"x\":1685143657019,\"y\":20.3},{\"x\":1685143957116,\"y\":20.2},{\"x\":1685144257203,\"y\":20.2},{\"x\":1685144557262,\"y\":20.1},{\"x\":1685144857393,\"y\":20.2},{\"x\":1685145157511,\"y\":20.2},{\"x\":1685145457515,\"y\":20.2},{\"x\":1685145757583,\"y\":20},{\"x\":1685146057687,\"y\":20},{\"x\":1685146357780,\"y\":20.1},{\"x\":1685146657878,\"y\":19.9},{\"x\":1685146957969,\"y\":19.9},{\"x\":1685147258066,\"y\":20},{\"x\":1685147558163,\"y\":20},{\"x\":1685147858255,\"y\":19.9},{\"x\":1685148158303,\"y\":19.9},{\"x\":1685148458377,\"y\":19.9},{\"x\":1685148758392,\"y\":19.9},{\"x\":1685149058441,\"y\":19.9},{\"x\":1685149358550,\"y\":19.9},{\"x\":1685149658652,\"y\":19.5},{\"x\":1685149958735,\"y\":19.5},{\"x\":1685150258816,\"y\":19.5},{\"x\":1685150558892,\"y\":19.5},{\"x\":1685150858996,\"y\":19.5},{\"x\":1685151159109,\"y\":19.5},{\"x\":1685151459166,\"y\":19.5},{\"x\":1685151759230,\"y\":19.5},{\"x\":1685152059291,\"y\":19.5},{\"x\":1685152359350,\"y\":19.5},{\"x\":1685152659430,\"y\":19.5},{\"x\":1685152959526,\"y\":19.5},{\"x\":1685153259622,\"y\":19.5},{\"x\":1685153559728,\"y\":19.5},{\"x\":1685153859883,\"y\":19.5},{\"x\":1685154159899,\"y\":19.5},{\"x\":1685154459974,\"y\":19.5},{\"x\":1685154760016,\"y\":19.5},{\"x\":1685155060096,\"y\":19.5},{\"x\":1685155360142,\"y\":19.5},{\"x\":1685155660284,\"y\":19.5},{\"x\":1685155960372,\"y\":19.5},{\"x\":1685156260469,\"y\":19.5},{\"x\":1685156560559,\"y\":19.5},{\"x\":1685156860664,\"y\":19.5},{\"x\":1685157160732,\"y\":19.5},{\"x\":1685157460827,\"y\":19.5},{\"x\":1685157760925,\"y\":19.5},{\"x\":1685158061041,\"y\":19.4},{\"x\":1685158361093,\"y\":19.4},{\"x\":1685158661174,\"y\":19.4},{\"x\":1685158961238,\"y\":19.4},{\"x\":1685159261356,\"y\":19.4},{\"x\":1685159561460,\"y\":19.4},{\"x\":1685159861591,\"y\":19.4},{\"x\":1685160161622,\"y\":19.5},{\"x\":1685160461727,\"y\":19.4},{\"x\":1685160761798,\"y\":19.3},{\"x\":1685161061899,\"y\":19.3},{\"x\":1685161361960,\"y\":19.3},{\"x\":1685161662030,\"y\":19.3},{\"x\":1685161962083,\"y\":19.2},{\"x\":1685162262205,\"y\":19.2},{\"x\":1685162562329,\"y\":19.2},{\"x\":1685162862420,\"y\":19.2},{\"x\":1685163162514,\"y\":19.3},{\"x\":1685163462630,\"y\":19.4},{\"x\":1685163762689,\"y\":19.5},{\"x\":1685164062785,\"y\":19.5},{\"x\":1685164362862,\"y\":19.5},{\"x\":1685164662929,\"y\":19.5},{\"x\":1685164962989,\"y\":19.5},{\"x\":1685165263080,\"y\":19.5},{\"x\":1685165563213,\"y\":19.5},{\"x\":1685165863284,\"y\":19.5},{\"x\":1685166163398,\"y\":19.7},{\"x\":1685166463432,\"y\":20},{\"x\":1685166763562,\"y\":20},{\"x\":1685167063657,\"y\":20.3},{\"x\":1685167363752,\"y\":20.3},{\"x\":1685167663818,\"y\":20.2},{\"x\":1685167963868,\"y\":20.4},{\"x\":1685168263959,\"y\":20.5},{\"x\":1685168564052,\"y\":20.7},{\"x\":1685168864152,\"y\":20.8},{\"x\":1685169164297,\"y\":20.8},{\"x\":1685169464371,\"y\":20.8},{\"x\":1685169764428,\"y\":20.9},{\"x\":1685170064553,\"y\":21},{\"x\":1685170364630,\"y\":20.8},{\"x\":1685170664689,\"y\":20.8},{\"x\":1685170964744,\"y\":20.9},{\"x\":1685171264839,\"y\":21},{\"x\":1685171564944,\"y\":21},{\"x\":1685171865046,\"y\":21.1},{\"x\":1685172165136,\"y\":21.1},{\"x\":1685172465216,\"y\":21.2},{\"x\":1685172765270,\"y\":21.5},{\"x\":1685173065378,\"y\":21.5},{\"x\":1685173365491,\"y\":21.5},{\"x\":1685173665574,\"y\":21.5},{\"x\":1685173965634,\"y\":21.5},{\"x\":1685174265651,\"y\":21.5},{\"x\":1685174565770,\"y\":21.5},{\"x\":1685174865842,\"y\":21.5},{\"x\":1685175165962,\"y\":21.5},{\"x\":1685175466056,\"y\":21.5},{\"x\":1685175766151,\"y\":21.6},{\"x\":1685176066247,\"y\":21.7},{\"x\":1685176366347,\"y\":21.6},{\"x\":1685176666421,\"y\":21.6},{\"x\":1685176966481,\"y\":21.7},{\"x\":1685177266535,\"y\":21.7},{\"x\":1685177566618,\"y\":21.7},{\"x\":1685177866715,\"y\":21.8},{\"x\":1685178166786,\"y\":21.7},{\"x\":1685178466868,\"y\":21.7},{\"x\":1685178766964,\"y\":21.8},{\"x\":1685179067013,\"y\":21.9},{\"x\":1685179367161,\"y\":21.9},{\"x\":1685179667226,\"y\":21.9},{\"x\":1685179967298,\"y\":21.9},{\"x\":1685180267304,\"y\":21.9},{\"x\":1685180567400,\"y\":21.9},{\"x\":1685180867474,\"y\":21.9},{\"x\":1685181167605,\"y\":21.9},{\"x\":1685181467661,\"y\":21.9},{\"x\":1685181767781,\"y\":21.9},{\"x\":1685182067847,\"y\":21.9},{\"x\":1685182367916,\"y\":21.9},{\"x\":1685182668010,\"y\":21.9},{\"x\":1685182968067,\"y\":21.9},{\"x\":1685183268142,\"y\":21.9},{\"x\":1685183568218,\"y\":21.9},{\"x\":1685183868264,\"y\":21.9},{\"x\":1685184168356,\"y\":22},{\"x\":1685184468449,\"y\":22},{\"x\":1685184768557,\"y\":22},{\"x\":1685185068624,\"y\":22.2},{\"x\":1685185368711,\"y\":22.1},{\"x\":1685185668784,\"y\":22.1},{\"x\":1685185968847,\"y\":22},{\"x\":1685186268953,\"y\":21.9},{\"x\":1685186568982,\"y\":21.9},{\"x\":1685186868996,\"y\":21.9},{\"x\":1685187169094,\"y\":21.9},{\"x\":1685187469146,\"y\":21.9},{\"x\":1685187769224,\"y\":21.9},{\"x\":1685188069250,\"y\":21.9},{\"x\":1685188369327,\"y\":21.9},{\"x\":1685188669411,\"y\":21.7},{\"x\":1685188969475,\"y\":21.7},{\"x\":1685189269500,\"y\":21.5},{\"x\":1685189569548,\"y\":21.5},{\"x\":1685189869583,\"y\":21.6},{\"x\":1685190169645,\"y\":21.7},{\"x\":1685190469717,\"y\":21.7},{\"x\":1685190769801,\"y\":21.7},{\"x\":1685191069848,\"y\":21.7},{\"x\":1685191369885,\"y\":21.7},{\"x\":1685191669980,\"y\":21.7},{\"x\":1685191970014,\"y\":21.7},{\"x\":1685192270055,\"y\":21.6},{\"x\":1685192570106,\"y\":21.7},{\"x\":1685192870150,\"y\":21.7},{\"x\":1685193170235,\"y\":21.6},{\"x\":1685193470249,\"y\":21.5},{\"x\":1685193770302,\"y\":21.5},{\"x\":1685194070311,\"y\":21.6},{\"x\":1685194370328,\"y\":21.7},{\"x\":1685194670393,\"y\":21.7},{\"x\":1685194985377,\"y\":21.7},{\"x\":1685195285440,\"y\":21.7},{\"x\":1685195585471,\"y\":21.7},{\"x\":1685195885473,\"y\":21.7},{\"x\":1685196185514,\"y\":21.6},{\"x\":1685196485562,\"y\":21.7},{\"x\":1685196800615,\"y\":21.7},{\"x\":1685197115572,\"y\":21.7},{\"x\":1685197415601,\"y\":21.7},{\"x\":1685197715640,\"y\":21.7},{\"x\":1685198015652,\"y\":21.7},{\"x\":1685198315706,\"y\":21.7},{\"x\":1685198615725,\"y\":21.7},{\"x\":1685198915775,\"y\":21.8},{\"x\":1685199215786,\"y\":21.8},{\"x\":1685199515831,\"y\":21.9},{\"x\":1685199815856,\"y\":21.8},{\"x\":1685200115858,\"y\":21.9},{\"x\":1685200415875,\"y\":21.8},{\"x\":1685200715913,\"y\":21.8},{\"x\":1685201015918,\"y\":21.8},{\"x\":1685201315964,\"y\":21.9},{\"x\":1685201616032,\"y\":21.9},{\"x\":1685201916044,\"y\":21.9},{\"x\":1685202231051,\"y\":21.9},{\"x\":1685202531062,\"y\":21.9},{\"x\":1685202831089,\"y\":21.8},{\"x\":1685203131095,\"y\":21.8},{\"x\":1685203431195,\"y\":21.8},{\"x\":1685203746138,\"y\":21.8},{\"x\":1685204046168,\"y\":21.8},{\"x\":1685204346182,\"y\":21.7},{\"x\":1685204646203,\"y\":21.8},{\"x\":1685204946241,\"y\":21.9},{\"x\":1685205246243,\"y\":21.9},{\"x\":1685205546262,\"y\":21.9},{\"x\":1685205846296,\"y\":21.9},{\"x\":1685206146312,\"y\":21.9},{\"x\":1685206446335,\"y\":21.9},{\"x\":1685206746375,\"y\":21.9},{\"x\":1685207046395,\"y\":21.9},{\"x\":1685207346549,\"y\":21.9},{\"x\":1685207661544,\"y\":21.9},{\"x\":1685207961647,\"y\":21.9},{\"x\":1685208261700,\"y\":21.9},{\"x\":1685208561766,\"y\":21.9},{\"x\":1685208861855,\"y\":21.9},{\"x\":1685209161943,\"y\":21.9},{\"x\":1685209462007,\"y\":21.9},{\"x\":1685209762097,\"y\":21.9},{\"x\":1685210077101,\"y\":21.9},{\"x\":1685210377152,\"y\":21.9},{\"x\":1685210677231,\"y\":21.9},{\"x\":1685210977296,\"y\":21.9},{\"x\":1685211277406,\"y\":21.9},{\"x\":1685211577537,\"y\":21.9},{\"x\":1685211877620,\"y\":21.9},{\"x\":1685212177660,\"y\":21.9},{\"x\":1685212477797,\"y\":21.9},{\"x\":1685212777837,\"y\":21.9},{\"x\":1685213077881,\"y\":21.9},{\"x\":1685213377932,\"y\":21.9},{\"x\":1685213677997,\"y\":21.9},{\"x\":1685213978205,\"y\":21.9},{\"x\":1685214293200,\"y\":21.9},{\"x\":1685214593270,\"y\":21.9},{\"x\":1685214893375,\"y\":21.9},{\"x\":1685215193482,\"y\":21.9},{\"x\":1685215493529,\"y\":21.8},{\"x\":1685215793531,\"y\":21.9},{\"x\":1685216093643,\"y\":21.9},{\"x\":1685216393738,\"y\":22},{\"x\":1685216693784,\"y\":21.9},{\"x\":1685216993880,\"y\":21.9},{\"x\":1685217293962,\"y\":21.7},{\"x\":1685217594100,\"y\":21.7},{\"x\":1685217894151,\"y\":21.7},{\"x\":1685218194256,\"y\":21.6},{\"x\":1685218494376,\"y\":21.5},{\"x\":1685218794467,\"y\":21.5},{\"x\":1685219094477,\"y\":21.5},{\"x\":1685219394675,\"y\":21.5},{\"x\":1685219709688,\"y\":21.5},{\"x\":1685220009789,\"y\":21.5},{\"x\":1685220309889,\"y\":21.5},{\"x\":1685220609986,\"y\":21.4},{\"x\":1685220910080,\"y\":21.5},{\"x\":1685221210236,\"y\":21.5},{\"x\":1685221510267,\"y\":21.5},{\"x\":1685221810331,\"y\":21.5},{\"x\":1685222110381,\"y\":21.4},{\"x\":1685222410481,\"y\":21.4},{\"x\":1685222710598,\"y\":21.3},{\"x\":1685223010661,\"y\":21.5},{\"x\":1685223310759,\"y\":21.5},{\"x\":1685223610881,\"y\":21.5},{\"x\":1685223911009,\"y\":21.5},{\"x\":1685224211106,\"y\":21.5},{\"x\":1685224511191,\"y\":21.5},{\"x\":1685224811272,\"y\":21.4},{\"x\":1685225111309,\"y\":21.4},{\"x\":1685225411398,\"y\":21.3},{\"x\":1685225711438,\"y\":21.2},{\"x\":1685226011552,\"y\":21.2},{\"x\":1685226311659,\"y\":21.2},{\"x\":1685226611773,\"y\":21.2},{\"x\":1685226911876,\"y\":21.2},{\"x\":1685227211995,\"y\":21.2},{\"x\":1685227512070,\"y\":21.2},{\"x\":1685227812175,\"y\":21.2},{\"x\":1685228112250,\"y\":21.2},{\"x\":1685228412314,\"y\":21},{\"x\":1685228712352,\"y\":21},{\"x\":1685229012470,\"y\":21},{\"x\":1685229312587,\"y\":21},{\"x\":1685229612662,\"y\":21.1},{\"x\":1685229912736,\"y\":21},{\"x\":1685230212847,\"y\":20.9},{\"x\":1685230512904,\"y\":21},{\"x\":1685230813034,\"y\":21},{\"x\":1685231113081,\"y\":21},{\"x\":1685231413122,\"y\":20.8},{\"x\":1685231713183,\"y\":20.8},{\"x\":1685232013342,\"y\":20.8},{\"x\":1685232328321,\"y\":20.8},{\"x\":1685232628419,\"y\":20.8},{\"x\":1685232928528,\"y\":20.8},{\"x\":1685233228604,\"y\":20.8},{\"x\":1685233528687,\"y\":20.8},{\"x\":1685233828743,\"y\":20.8},{\"x\":1685234128809,\"y\":20.8},{\"x\":1685234428883,\"y\":20.8},{\"x\":1685234728963,\"y\":20.8},{\"x\":1685235028977,\"y\":20.8},{\"x\":1685235329064,\"y\":20.8},{\"x\":1685235629163,\"y\":20.8},{\"x\":1685235929251,\"y\":20.8},{\"x\":1685236229341,\"y\":20.8},{\"x\":1685236529423,\"y\":20.7},{\"x\":1685236829513,\"y\":20.7},{\"x\":1685237129612,\"y\":20.8},{\"x\":1685237429697,\"y\":20.7},{\"x\":1685237729771,\"y\":20.7},{\"x\":1685238029832,\"y\":20.5},{\"x\":1685238329902,\"y\":20.5},{\"x\":1685238629974,\"y\":20.5},{\"x\":1685238930096,\"y\":20.5},{\"x\":1685239230144,\"y\":20.6},{\"x\":1685239530225,\"y\":20.6},{\"x\":1685239830331,\"y\":20.6},{\"x\":1685240130366,\"y\":20.6},{\"x\":1685240430509,\"y\":20.5},{\"x\":1685240730597,\"y\":20.5},{\"x\":1685241030697,\"y\":20.4},{\"x\":1685241330716,\"y\":20.4},{\"x\":1685241630792,\"y\":20.4},{\"x\":1685241930839,\"y\":20.4},{\"x\":1685242230957,\"y\":20.4},{\"x\":1685242531061,\"y\":20.4},{\"x\":1685242831116,\"y\":20.4},{\"x\":1685243131245,\"y\":20.4},{\"x\":1685243431358,\"y\":20.4},{\"x\":1685243731422,\"y\":20.3},{\"x\":1685244031484,\"y\":20.3},{\"x\":1685244331554,\"y\":20.2},{\"x\":1685244631614,\"y\":20.3},{\"x\":1685244931700,\"y\":20.3},{\"x\":1685245231790,\"y\":20.3},{\"x\":1685245531908,\"y\":20.3},{\"x\":1685245832059,\"y\":20.3},{\"x\":1685246132107,\"y\":20.3},{\"x\":1685246432206,\"y\":20.3},{\"x\":1685246732334,\"y\":20.3},{\"x\":1685247032445,\"y\":20.3},{\"x\":1685247332472,\"y\":20.2},{\"x\":1685247632538,\"y\":20.2},{\"x\":1685247932668,\"y\":20.2},{\"x\":1685248232731,\"y\":20.2},{\"x\":1685248532806,\"y\":20.2},{\"x\":1685248832930,\"y\":20.2},{\"x\":1685249133015,\"y\":20.2},{\"x\":1685249433093,\"y\":20.2},{\"x\":1685249733189,\"y\":20.2},{\"x\":1685250033291,\"y\":20.1},{\"x\":1685250333378,\"y\":20},{\"x\":1685250633446,\"y\":20},{\"x\":1685250933493,\"y\":20},{\"x\":1685251233569,\"y\":20},{\"x\":1685251533627,\"y\":20},{\"x\":1685251833774,\"y\":20.1},{\"x\":1685252133852,\"y\":20.2},{\"x\":1685252433955,\"y\":20.2},{\"x\":1685252734091,\"y\":20.2},{\"x\":1685253034179,\"y\":20},{\"x\":1685253334257,\"y\":19.9},{\"x\":1685253634325,\"y\":20},{\"x\":1685253934367,\"y\":20.2},{\"x\":1685254234489,\"y\":20.2},{\"x\":1685254534587,\"y\":20.2},{\"x\":1685254834680,\"y\":20.2},{\"x\":1685255134805,\"y\":20.3},{\"x\":1685255434885,\"y\":20.3},{\"x\":1685255735002,\"y\":20.3},{\"x\":1685256035085,\"y\":20.5},{\"x\":1685256335179,\"y\":20.5},{\"x\":1685256635254,\"y\":20.3},{\"x\":1685256935309,\"y\":20.3},{\"x\":1685257235338,\"y\":20.3},{\"x\":1685257535440,\"y\":20.3},{\"x\":1685257835520,\"y\":20.4},{\"x\":1685258135635,\"y\":20.5},{\"x\":1685258435709,\"y\":20.5},{\"x\":1685258735790,\"y\":20.5},{\"x\":1685259035904,\"y\":20.5},{\"x\":1685259335965,\"y\":20.4},{\"x\":1685259636005,\"y\":20.4},{\"x\":1685259936052,\"y\":20.4},{\"x\":1685260236148,\"y\":20.5},{\"x\":1685260536249,\"y\":20.4},{\"x\":1685260836320,\"y\":20.5},{\"x\":1685261136489,\"y\":20.5},{\"x\":1685261436489,\"y\":20.6},{\"x\":1685261736607,\"y\":20.5},{\"x\":1685262036710,\"y\":20.5},{\"x\":1685262336806,\"y\":20.4},{\"x\":1685262636943,\"y\":20.4},{\"x\":1685262936991,\"y\":20.3},{\"x\":1685263237019,\"y\":20.3},{\"x\":1685263537113,\"y\":20.3},{\"x\":1685263837223,\"y\":20.3},{\"x\":1685264137303,\"y\":20.3},{\"x\":1685264437449,\"y\":20.4},{\"x\":1685264737520,\"y\":20.3},{\"x\":1685265037596,\"y\":20.4},{\"x\":1685265337653,\"y\":20.3},{\"x\":1685265637725,\"y\":20.3},{\"x\":1685265937773,\"y\":20.3},{\"x\":1685266237884,\"y\":20.3},{\"x\":1685266537917,\"y\":20.3},{\"x\":1685266837940,\"y\":20.3},{\"x\":1685267138006,\"y\":20.3},{\"x\":1685267438089,\"y\":20.3},{\"x\":1685267738177,\"y\":20.3},{\"x\":1685268038239,\"y\":20.4},{\"x\":1685268338288,\"y\":20.3},{\"x\":1685268638340,\"y\":20.4},{\"x\":1685268938413,\"y\":20.3},{\"x\":1685269238479,\"y\":20.4},{\"x\":1685269538567,\"y\":20.4},{\"x\":1685269838641,\"y\":20.4},{\"x\":1685270138681,\"y\":20.3},{\"x\":1685270438761,\"y\":20.3},{\"x\":1685270738813,\"y\":20.3},{\"x\":1685271038911,\"y\":20.3},{\"x\":1685271338993,\"y\":20},{\"x\":1685271638994,\"y\":20},{\"x\":1685271939107,\"y\":20.2},{\"x\":1685272239155,\"y\":20.3},{\"x\":1685272539230,\"y\":20.3},{\"x\":1685272839299,\"y\":20.3},{\"x\":1685273139339,\"y\":20.3},{\"x\":1685273439382,\"y\":20.2},{\"x\":1685273739450,\"y\":19.9},{\"x\":1685274039516,\"y\":19.9},{\"x\":1685274339570,\"y\":20.2},{\"x\":1685274639651,\"y\":20.3},{\"x\":1685274939715,\"y\":20.5},{\"x\":1685275239718,\"y\":20.5},{\"x\":1685275539821,\"y\":20.6},{\"x\":1685275839883,\"y\":20.5},{\"x\":1685276139936,\"y\":20.3},{\"x\":1685276439971,\"y\":20.3},{\"x\":1685276740019,\"y\":20.3},{\"x\":1685277040061,\"y\":20.2},{\"x\":1685277340154,\"y\":20},{\"x\":1685277640177,\"y\":19.9},{\"x\":1685277940231,\"y\":20},{\"x\":1685278240249,\"y\":20},{\"x\":1685278540299,\"y\":19.9},{\"x\":1685278840376,\"y\":19.9},{\"x\":1685279140399,\"y\":19.5},{\"x\":1685279440422,\"y\":19.7},{\"x\":1685279740489,\"y\":19.8},{\"x\":1685280040542,\"y\":19.8},{\"x\":1685280340547,\"y\":19.5},{\"x\":1685280640612,\"y\":19.5},{\"x\":1685280940692,\"y\":19.5},{\"x\":1685280995978,\"y\":19.5},{\"x\":1685281186232,\"y\":19.5},{\"x\":1685281372604,\"y\":19.5},{\"x\":1685281687601,\"y\":19.5},{\"x\":1685281710226,\"y\":19.5},{\"x\":1685281749591,\"y\":19.5},{\"x\":1685281762017,\"y\":19.5},{\"x\":1685281799605,\"y\":19.5},{\"x\":1685282023556,\"y\":19.5},{\"x\":1685282044430,\"y\":19.5},{\"x\":1685282166915,\"y\":19.5},{\"x\":1685282208131,\"y\":19.5},{\"x\":1685282217923,\"y\":19.5},{\"x\":1685282260379,\"y\":19.5},{\"x\":1685282339652,\"y\":19.5},{\"x\":1685282364558,\"y\":19.5},{\"x\":1685282412077,\"y\":19.5},{\"x\":1685282588188,\"y\":19.5},{\"x\":1685282614967,\"y\":19.5},{\"x\":1685282852605,\"y\":19.5},{\"x\":1685282960718,\"y\":19.5},{\"x\":1685282980565,\"y\":19.5},{\"x\":1685283037810,\"y\":19.5},{\"x\":1685283067001,\"y\":19.5},{\"x\":1685283122144,\"y\":19.5},{\"x\":1685283272482,\"y\":19.5},{\"x\":1685283343031,\"y\":19.5},{\"x\":1685283445211,\"y\":19.5},{\"x\":1685283473050,\"y\":19.5},{\"x\":1685283503945,\"y\":19.4},{\"x\":1685283559122,\"y\":19.5},{\"x\":1685283661335,\"y\":19.5},{\"x\":1685283679196,\"y\":19.5},{\"x\":1685283753141,\"y\":19.5},{\"x\":1685283789923,\"y\":19.4},{\"x\":1685283796674,\"y\":19.4},{\"x\":1685284111596,\"y\":19.5},{\"x\":1685284411624,\"y\":19.5},{\"x\":1685284726634,\"y\":20},{\"x\":1685285041633,\"y\":19.9},{\"x\":1685285341650,\"y\":19.6},{\"x\":1685285641691,\"y\":19.8},{\"x\":1685285941711,\"y\":20},{\"x\":1685286241759,\"y\":20},{\"x\":1685286541841,\"y\":19.7},{\"x\":1685286856821,\"y\":20},{\"x\":1685287156833,\"y\":20.3},{\"x\":1685287456865,\"y\":20.4},{\"x\":1685287756902,\"y\":20.3},{\"x\":1685288056933,\"y\":20.3},{\"x\":1685288357007,\"y\":20.3},{\"x\":1685288671988,\"y\":20.1},{\"x\":1685288972004,\"y\":20.2},{\"x\":1685289272052,\"y\":20},{\"x\":1685289572077,\"y\":20},{\"x\":1685289872089,\"y\":20},{\"x\":1685290172161,\"y\":19.9},{\"x\":1685290487147,\"y\":20},{\"x\":1685290787163,\"y\":20},{\"x\":1685291087218,\"y\":19.9},{\"x\":1685291387247,\"y\":19.9},{\"x\":1685291687274,\"y\":20},{\"x\":1685291987283,\"y\":20.2},{\"x\":1685292287334,\"y\":20},{\"x\":1685292587380,\"y\":19.9},{\"x\":1685292887389,\"y\":19.9},{\"x\":1685293187427,\"y\":19.8},{\"x\":1685293502456,\"y\":19.7},{\"x\":1685293817480,\"y\":19.5},{\"x\":1685294117492,\"y\":19.6},{\"x\":1685294417507,\"y\":19.9},{\"x\":1685294717548,\"y\":20},{\"x\":1685295017585,\"y\":19.9},{\"x\":1685295317622,\"y\":19.6},{\"x\":1685295617633,\"y\":20},{\"x\":1685295917642,\"y\":19.7},{\"x\":1685296217701,\"y\":19.9},{\"x\":1685296532704,\"y\":19.5},{\"x\":1685296832750,\"y\":19.5},{\"x\":1685297132754,\"y\":19.5},{\"x\":1685297432771,\"y\":19.5},{\"x\":1685297732796,\"y\":19.5},{\"x\":1685298032828,\"y\":19.5},{\"x\":1685298332882,\"y\":19.5},{\"x\":1685298647889,\"y\":19.5},{\"x\":1685298947921,\"y\":19.5},{\"x\":1685299247969,\"y\":19.5},{\"x\":1685299547997,\"y\":19.5},{\"x\":1685299848026,\"y\":19.5},{\"x\":1685300163050,\"y\":19.5},{\"x\":1685300463065,\"y\":19.5},{\"x\":1685300763075,\"y\":19.5},{\"x\":1685301063109,\"y\":19.5},{\"x\":1685301363164,\"y\":19.5},{\"x\":1685301678208,\"y\":19.5},{\"x\":1685301993216,\"y\":19.5},{\"x\":1685302293269,\"y\":19.5},{\"x\":1685302593314,\"y\":19.5},{\"x\":1685302908310,\"y\":19.5},{\"x\":1685303208329,\"y\":19.5},{\"x\":1685303508359,\"y\":19.5},{\"x\":1685303808369,\"y\":19.5},{\"x\":1685304108395,\"y\":19.5},{\"x\":1685304408424,\"y\":19.3},{\"x\":1685304708455,\"y\":19.4},{\"x\":1685305008461,\"y\":19.4},{\"x\":1685305308490,\"y\":19.4},{\"x\":1685305608530,\"y\":19.5},{\"x\":1685305908539,\"y\":19.5},{\"x\":1685306208562,\"y\":19.4},{\"x\":1685306508616,\"y\":19.5},{\"x\":1685306823616,\"y\":19.5},{\"x\":1685307123649,\"y\":19.5},{\"x\":1685307423661,\"y\":19.5},{\"x\":1685307723699,\"y\":19.5},{\"x\":1685308023733,\"y\":19.5},{\"x\":1685308323779,\"y\":19.5},{\"x\":1685308638772,\"y\":19.5},{\"x\":1685308938793,\"y\":19.5},{\"x\":1685309238818,\"y\":19.5},{\"x\":1685309538833,\"y\":19.5},{\"x\":1685309838872,\"y\":19.5},{\"x\":1685310138910,\"y\":19.5},{\"x\":1685310453907,\"y\":19.5},{\"x\":1685310768906,\"y\":19.5},{\"x\":1685311068915,\"y\":19.5},{\"x\":1685311368965,\"y\":19.5},{\"x\":1685311668966,\"y\":19.5},{\"x\":1685311968998,\"y\":19.5},{\"x\":1685312269016,\"y\":19.5},{\"x\":1685312569034,\"y\":19.5},{\"x\":1685312869061,\"y\":19.5},{\"x\":1685313169072,\"y\":19.5},{\"x\":1685313484093,\"y\":19.5},{\"x\":1685313784097,\"y\":19.6},{\"x\":1685314084113,\"y\":19.5},{\"x\":1685314384134,\"y\":19.7},{\"x\":1685314684165,\"y\":19.9},{\"x\":1685314984191,\"y\":19.9},{\"x\":1685315299157,\"y\":19.9},{\"x\":1685315599168,\"y\":20},{\"x\":1685315899192,\"y\":19.9},{\"x\":1685316199221,\"y\":20.1},{\"x\":1685316514243,\"y\":20},{\"x\":1685316814264,\"y\":20.2},{\"x\":1685317129218,\"y\":20.2},{\"x\":1685317444217,\"y\":20.2},{\"x\":1685317744298,\"y\":20.3},{\"x\":1685318059259,\"y\":20.3},{\"x\":1685318359275,\"y\":20.3},{\"x\":1685318659355,\"y\":20.3},{\"x\":1685318959404,\"y\":20.3},{\"x\":1685319259419,\"y\":20.2},{\"x\":1685319559443,\"y\":20.3},{\"x\":1685319859535,\"y\":20.3},{\"x\":1685320159597,\"y\":20.3},{\"x\":1685320459678,\"y\":20.3},{\"x\":1685320759851,\"y\":20.3},{\"x\":1685321074857,\"y\":20.2},{\"x\":1685321374890,\"y\":20.3},{\"x\":1685321674930,\"y\":20.3},{\"x\":1685321974980,\"y\":20.3},{\"x\":1685322274993,\"y\":20.3},{\"x\":1685322575024,\"y\":20.3},{\"x\":1685322875122,\"y\":20.3},{\"x\":1685323175196,\"y\":20.3},{\"x\":1685323475244,\"y\":20.3},{\"x\":1685323775309,\"y\":20.4},{\"x\":1685324075375,\"y\":20.3},{\"x\":1685324375402,\"y\":20.4},{\"x\":1685324675425,\"y\":20.5},{\"x\":1685324975485,\"y\":20.5},{\"x\":1685325275523,\"y\":20.5},{\"x\":1685325575582,\"y\":20.5},{\"x\":1685325875647,\"y\":20.5},{\"x\":1685326175713,\"y\":20.5},{\"x\":1685326475768,\"y\":20.4},{\"x\":1685326775825,\"y\":20.4},{\"x\":1685327075908,\"y\":20.4},{\"x\":1685327376002,\"y\":20.4},{\"x\":1685327676015,\"y\":20.4},{\"x\":1685327976059,\"y\":20.4},{\"x\":1685328276184,\"y\":20.4},{\"x\":1685328591156,\"y\":20.4},{\"x\":1685328891332,\"y\":20.4},{\"x\":1685329206306,\"y\":20.4},{\"x\":1685329506397,\"y\":20.4},{\"x\":1685329806440,\"y\":20.3},{\"x\":1685330106549,\"y\":20.3},{\"x\":1685330406587,\"y\":20.3},{\"x\":1685330706687,\"y\":20.2},{\"x\":1685331006741,\"y\":20.3},{\"x\":1685331306759,\"y\":20.3},{\"x\":1685331606822,\"y\":20.2},{\"x\":1685331906872,\"y\":20.2},{\"x\":1685332206974,\"y\":20.2},{\"x\":1685332507043,\"y\":20.3},{\"x\":1685332807216,\"y\":20.2},{\"x\":1685333122210,\"y\":20.2},{\"x\":1685333422298,\"y\":20.2},{\"x\":1685333722396,\"y\":20.2},{\"x\":1685334022464,\"y\":20.2},{\"x\":1685334322536,\"y\":20.2},{\"x\":1685334622591,\"y\":20.2},{\"x\":1685334922644,\"y\":20.2},{\"x\":1685335222714,\"y\":20.1},{\"x\":1685335522841,\"y\":20},{\"x\":1685335822919,\"y\":20},{\"x\":1685336122954,\"y\":20},{\"x\":1685336423087,\"y\":19.9},{\"x\":1685336723138,\"y\":20},{\"x\":1685337023185,\"y\":19.9},{\"x\":1685337323274,\"y\":19.9},{\"x\":1685337623330,\"y\":19.9},{\"x\":1685337923414,\"y\":20},{\"x\":1685338223520,\"y\":19.9},{\"x\":1685338523616,\"y\":20},{\"x\":1685338823724,\"y\":19.9},{\"x\":1685339123797,\"y\":20},{\"x\":1685339423870,\"y\":19.9},{\"x\":1685339723950,\"y\":19.9},{\"x\":1685340024030,\"y\":19.9},{\"x\":1685340324090,\"y\":20},{\"x\":1685340624115,\"y\":20},{\"x\":1685340924164,\"y\":20},{\"x\":1685341224265,\"y\":20},{\"x\":1685341524353,\"y\":20},{\"x\":1685341824453,\"y\":20},{\"x\":1685342124531,\"y\":20.2},{\"x\":1685342424622,\"y\":19.9},{\"x\":1685342724753,\"y\":20},{\"x\":1685343024799,\"y\":20.2},{\"x\":1685343324830,\"y\":20},{\"x\":1685343624890,\"y\":19.9},{\"x\":1685343924971,\"y\":20.2},{\"x\":1685344225070,\"y\":20.2},{\"x\":1685344525149,\"y\":20.3},{\"x\":1685344825216,\"y\":20.2},{\"x\":1685345125344,\"y\":20.2},{\"x\":1685345425362,\"y\":20.3},{\"x\":1685345725441,\"y\":20.3},{\"x\":1685346025536,\"y\":20.3},{\"x\":1685346325591,\"y\":20},{\"x\":1685346625679,\"y\":20.2},{\"x\":1685346925731,\"y\":20.2},{\"x\":1685347225748,\"y\":20.3},{\"x\":1685347525839,\"y\":20.3},{\"x\":1685347825931,\"y\":20.3},{\"x\":1685348126031,\"y\":20.3},{\"x\":1685348426089,\"y\":20.3},{\"x\":1685348726163,\"y\":20.3},{\"x\":1685349026225,\"y\":20.3},{\"x\":1685349326308,\"y\":20.3},{\"x\":1685349626392,\"y\":20},{\"x\":1685349926466,\"y\":20},{\"x\":1685350226553,\"y\":20.2},{\"x\":1685350526651,\"y\":19.9},{\"x\":1685350826811,\"y\":20},{\"x\":1685351141763,\"y\":19.9},{\"x\":1685351441827,\"y\":20.2},{\"x\":1685351741927,\"y\":20.2},{\"x\":1685352042064,\"y\":20.1},{\"x\":1685352342100,\"y\":20.3},{\"x\":1685352642170,\"y\":20},{\"x\":1685352942245,\"y\":19.9},{\"x\":1685353242314,\"y\":19.9},{\"x\":1685353542387,\"y\":20},{\"x\":1685353842461,\"y\":20},{\"x\":1685354142491,\"y\":20},{\"x\":1685354442557,\"y\":19.9},{\"x\":1685354742628,\"y\":20},{\"x\":1685355042729,\"y\":20.3},{\"x\":1685355342838,\"y\":20.2},{\"x\":1685355642921,\"y\":20.3},{\"x\":1685355942977,\"y\":20},{\"x\":1685356243149,\"y\":19.9},{\"x\":1685356558148,\"y\":19.5},{\"x\":1685356858211,\"y\":19.9},{\"x\":1685357158249,\"y\":20},{\"x\":1685357458282,\"y\":19.6},{\"x\":1685357758424,\"y\":20},{\"x\":1685358058471,\"y\":19.5},{\"x\":1685358358569,\"y\":19.5},{\"x\":1685358658635,\"y\":20},{\"x\":1685358958716,\"y\":20.1},{\"x\":1685359258767,\"y\":20},{\"x\":1685359558873,\"y\":19.9},{\"x\":1685359858954,\"y\":20.2},{\"x\":1685360159014,\"y\":20.2},{\"x\":1685360459077,\"y\":20},{\"x\":1685360759117,\"y\":19.9},{\"x\":1685361059194,\"y\":19.8},{\"x\":1685361359285,\"y\":20},{\"x\":1685361659377,\"y\":20},{\"x\":1685361959454,\"y\":20.1},{\"x\":1685362259508,\"y\":20.2},{\"x\":1685362559586,\"y\":19.9},{\"x\":1685362859674,\"y\":20},{\"x\":1685363159756,\"y\":20},{\"x\":1685363459810,\"y\":19.5},{\"x\":1685363759869,\"y\":19.6},{\"x\":1685364059953,\"y\":19.5},{\"x\":1685364360040,\"y\":19.9},{\"x\":1685364660157,\"y\":19.9},{\"x\":1685364960261,\"y\":19.9},{\"x\":1685365260272,\"y\":19.9},{\"x\":1685365560367,\"y\":20},{\"x\":1685365860483,\"y\":19.9},{\"x\":1685366160569,\"y\":19.9},{\"x\":1685366460633,\"y\":19.5},{\"x\":1685366760670,\"y\":19.5},{\"x\":1685367060781,\"y\":19.5},{\"x\":1685367360928,\"y\":19.5},{\"x\":1685367660996,\"y\":19.5},{\"x\":1685367961125,\"y\":19.5},{\"x\":1685368261267,\"y\":19.5},{\"x\":1685368561326,\"y\":19.5},{\"x\":1685368861443,\"y\":19.5},{\"x\":1685369161528,\"y\":19.5},{\"x\":1685369461571,\"y\":19.5},{\"x\":1685369761618,\"y\":19.5},{\"x\":1685370061811,\"y\":19.5},{\"x\":1685370361876,\"y\":19.5},{\"x\":1685370661975,\"y\":19.5},{\"x\":1685370962096,\"y\":19.5},{\"x\":1685371262161,\"y\":19.5},{\"x\":1685371562287,\"y\":19.5},{\"x\":1685371862386,\"y\":19.5},{\"x\":1685372162471,\"y\":19.5},{\"x\":1685372462563,\"y\":19.4},{\"x\":1685372762583,\"y\":19.2},{\"x\":1685373062692,\"y\":19.2},{\"x\":1685373362813,\"y\":19.1},{\"x\":1685373662925,\"y\":19.3},{\"x\":1685373963012,\"y\":19.2},{\"x\":1685374263073,\"y\":19.2},{\"x\":1685374563193,\"y\":19},{\"x\":1685374863306,\"y\":19.2},{\"x\":1685375163412,\"y\":19.3},{\"x\":1685375463446,\"y\":19.4},{\"x\":1685375763533,\"y\":19.4},{\"x\":1685376063611,\"y\":19.2},{\"x\":1685376363690,\"y\":19.1},{\"x\":1685376663795,\"y\":19},{\"x\":1685376963909,\"y\":19.2},{\"x\":1685377263998,\"y\":19.3},{\"x\":1685377564112,\"y\":19.1},{\"x\":1685377864184,\"y\":19.2},{\"x\":1685378164260,\"y\":19.1},{\"x\":1685378464309,\"y\":19},{\"x\":1685378764426,\"y\":19},{\"x\":1685379064528,\"y\":19},{\"x\":1685379364568,\"y\":19.2},{\"x\":1685379664627,\"y\":19.1},{\"x\":1685379964711,\"y\":19},{\"x\":1685380264778,\"y\":19.1},{\"x\":1685380564838,\"y\":19.2},{\"x\":1685380864899,\"y\":19},{\"x\":1685381164922,\"y\":19},{\"x\":1685381464995,\"y\":18.9},{\"x\":1685381765085,\"y\":18.9},{\"x\":1685382065145,\"y\":18.9},{\"x\":1685382365238,\"y\":18.9},{\"x\":1685382665304,\"y\":18.9},{\"x\":1685382965394,\"y\":19},{\"x\":1685383265458,\"y\":19},{\"x\":1685383565523,\"y\":18.9},{\"x\":1685383865573,\"y\":18.9},{\"x\":1685384165635,\"y\":18.7},{\"x\":1685384465670,\"y\":18.7},{\"x\":1685384765740,\"y\":18.7},{\"x\":1685385065812,\"y\":18.9},{\"x\":1685385365909,\"y\":18.8},{\"x\":1685385665940,\"y\":18.7},{\"x\":1685385966015,\"y\":18.9},{\"x\":1685386266095,\"y\":18.9},{\"x\":1685386566159,\"y\":18.8},{\"x\":1685386866204,\"y\":18.5},{\"x\":1685387166270,\"y\":18.5},{\"x\":1685387466325,\"y\":18.7},{\"x\":1685387766371,\"y\":18.5},{\"x\":1685388066417,\"y\":18.7},{\"x\":1685388366441,\"y\":18.7},{\"x\":1685388666495,\"y\":18.7},{\"x\":1685388966561,\"y\":18.4},{\"x\":1685389266600,\"y\":18.4},{\"x\":1685389566607,\"y\":18.4},{\"x\":1685389866660,\"y\":18.4},{\"x\":1685390166677,\"y\":18.5},{\"x\":1685390466746,\"y\":18.5},{\"x\":1685390781720,\"y\":18.6},{\"x\":1685391081770,\"y\":18.6},{\"x\":1685391381808,\"y\":18.4},{\"x\":1685391681836,\"y\":18.4},{\"x\":1685391981922,\"y\":18.4},{\"x\":1685392281964,\"y\":18.4},{\"x\":1685392582032,\"y\":18.4},{\"x\":1685392897029,\"y\":18.4},{\"x\":1685393197037,\"y\":18.4},{\"x\":1685393497068,\"y\":18.4},{\"x\":1685393797107,\"y\":18.4},{\"x\":1685394097146,\"y\":18.4},{\"x\":1685394397195,\"y\":18.4},{\"x\":1685394697240,\"y\":18.4},{\"x\":1685394997269,\"y\":18.4},{\"x\":1685395297315,\"y\":18.4},{\"x\":1685395597344,\"y\":18.4},{\"x\":1685395897378,\"y\":18.4},{\"x\":1685396197391,\"y\":18.4},{\"x\":1685396497411,\"y\":18.4},{\"x\":1685396797418,\"y\":18.3},{\"x\":1685397097514,\"y\":18.4},{\"x\":1685397397526,\"y\":18.2},{\"x\":1685397697553,\"y\":18.1},{\"x\":1685397997603,\"y\":18.1},{\"x\":1685398297622,\"y\":18.1},{\"x\":1685398597700,\"y\":18.1},{\"x\":1685398897737,\"y\":18.2},{\"x\":1685399212765,\"y\":18.2},{\"x\":1685399527768,\"y\":18},{\"x\":1685399827776,\"y\":18.1},{\"x\":1685400127841,\"y\":17.9},{\"x\":1685400442841,\"y\":18},{\"x\":1685400742935,\"y\":17.9},{\"x\":1685401057950,\"y\":18},{\"x\":1685401357960,\"y\":18},{\"x\":1685401657984,\"y\":18.1},{\"x\":1685401957995,\"y\":18},{\"x\":1685402258067,\"y\":18.1},{\"x\":1685402573081,\"y\":18},{\"x\":1685402873096,\"y\":17.9},{\"x\":1685403173117,\"y\":17.9},{\"x\":1685403473129,\"y\":17.9},{\"x\":1685403773148,\"y\":17.9},{\"x\":1685404073175,\"y\":18},{\"x\":1685404373229,\"y\":17.9},{\"x\":1685404688249,\"y\":17.9},{\"x\":1685404988317,\"y\":17.9},{\"x\":1685405288409,\"y\":17.9},{\"x\":1685405588488,\"y\":17.9},{\"x\":1685405888522,\"y\":17.9},{\"x\":1685406188569,\"y\":17.9},{\"x\":1685406488623,\"y\":17.9},{\"x\":1685406788688,\"y\":17.9},{\"x\":1685407088721,\"y\":17.9},{\"x\":1685407388765,\"y\":17.9},{\"x\":1685407688838,\"y\":17.9},{\"x\":1685407988885,\"y\":17.9},{\"x\":1685408288915,\"y\":17.9},{\"x\":1685408588993,\"y\":17.9},{\"x\":1685408889074,\"y\":17.9},{\"x\":1685409189147,\"y\":17.9},{\"x\":1685409489206,\"y\":17.9},{\"x\":1685409789249,\"y\":17.9},{\"x\":1685410089310,\"y\":17.9},{\"x\":1685410389345,\"y\":17.9},{\"x\":1685410704354,\"y\":17.9},{\"x\":1685411004452,\"y\":17.9},{\"x\":1685411304516,\"y\":17.9},{\"x\":1685411604552,\"y\":17.9},{\"x\":1685411904625,\"y\":17.8},{\"x\":1685412204887,\"y\":17.8},{\"x\":1685412519762,\"y\":17.7},{\"x\":1685412819867,\"y\":17.7},{\"x\":1685413119886,\"y\":17.8},{\"x\":1685413419976,\"y\":17.7},{\"x\":1685413719986,\"y\":17.7},{\"x\":1685414020028,\"y\":17.7},{\"x\":1685414320085,\"y\":17.7},{\"x\":1685414620140,\"y\":17.7},{\"x\":1685414920229,\"y\":17.7},{\"x\":1685415220270,\"y\":17.7},{\"x\":1685415520407,\"y\":17.7},{\"x\":1685415820425,\"y\":17.7},{\"x\":1685416120509,\"y\":17.7},{\"x\":1685416420519,\"y\":17.7},{\"x\":1685416720605,\"y\":17.7},{\"x\":1685417020626,\"y\":17.7},{\"x\":1685417320672,\"y\":17.7},{\"x\":1685417620774,\"y\":17.7},{\"x\":1685417920843,\"y\":17.7},{\"x\":1685418220900,\"y\":17.8},{\"x\":1685418520971,\"y\":17.7},{\"x\":1685418821022,\"y\":17.7},{\"x\":1685419121073,\"y\":17.8},{\"x\":1685419421171,\"y\":17.9},{\"x\":1685419721181,\"y\":17.7},{\"x\":1685420021225,\"y\":17.7},{\"x\":1685420321304,\"y\":17.6},{\"x\":1685420621355,\"y\":17.7},{\"x\":1685420921447,\"y\":17.7},{\"x\":1685421221522,\"y\":17.7},{\"x\":1685421521582,\"y\":17.7},{\"x\":1685421821663,\"y\":17.7},{\"x\":1685422121736,\"y\":17.6},{\"x\":1685422421818,\"y\":17.7},{\"x\":1685422721865,\"y\":17.7},{\"x\":1685423021869,\"y\":17.6},{\"x\":1685423321956,\"y\":17.4},{\"x\":1685423622015,\"y\":17.5},{\"x\":1685423922080,\"y\":17.4},{\"x\":1685424222162,\"y\":17.5},{\"x\":1685424522203,\"y\":17.5},{\"x\":1685424822288,\"y\":17.5},{\"x\":1685425122362,\"y\":17.4},{\"x\":1685425422438,\"y\":17.4},{\"x\":1685425722513,\"y\":17.4},{\"x\":1685426022547,\"y\":17.4},{\"x\":1685426322620,\"y\":17.5},{\"x\":1685426622723,\"y\":17.5},{\"x\":1685426922744,\"y\":17.4},{\"x\":1685427222854,\"y\":17.4},{\"x\":1685427522913,\"y\":17.4},{\"x\":1685427823002,\"y\":17.4},{\"x\":1685428123104,\"y\":17.5},{\"x\":1685428423173,\"y\":17.5},{\"x\":1685428723329,\"y\":17.4},{\"x\":1685429038338,\"y\":17.4},{\"x\":1685429338390,\"y\":17.4},{\"x\":1685429638534,\"y\":17.4},{\"x\":1685429938550,\"y\":17.4},{\"x\":1685430238655,\"y\":17.4},{\"x\":1685430538750,\"y\":17.4},{\"x\":1685430838822,\"y\":17.3},{\"x\":1685431139008,\"y\":17.5},{\"x\":1685431439050,\"y\":17.5},{\"x\":1685431739109,\"y\":17.4},{\"x\":1685432039156,\"y\":17.5},{\"x\":1685432339204,\"y\":17.7},{\"x\":1685432639299,\"y\":17.3},{\"x\":1685432939394,\"y\":17.2},{\"x\":1685433239499,\"y\":17.2},{\"x\":1685433539575,\"y\":17.2},{\"x\":1685433839700,\"y\":17.2},{\"x\":1685434139771,\"y\":16.9},{\"x\":1685434439873,\"y\":16.7},{\"x\":1685434739965,\"y\":16.5},{\"x\":1685435040038,\"y\":16.5},{\"x\":1685435340054,\"y\":16.5},{\"x\":1685435640133,\"y\":16.5},{\"x\":1685435940205,\"y\":16.5},{\"x\":1685436240311,\"y\":16.8},{\"x\":1685436540423,\"y\":16.9},{\"x\":1685436840548,\"y\":17.1},{\"x\":1685437140665,\"y\":17},{\"x\":1685437440701,\"y\":16.8},{\"x\":1685437740799,\"y\":16.5},{\"x\":1685438040887,\"y\":16.5},{\"x\":1685438340964,\"y\":16.5},{\"x\":1685438641030,\"y\":17.2},{\"x\":1685438941063,\"y\":17.2},{\"x\":1685439241150,\"y\":17.2},{\"x\":1685439541265,\"y\":17},{\"x\":1685439841366,\"y\":17.2},{\"x\":1685440141451,\"y\":16.5},{\"x\":1685440441506,\"y\":16.5},{\"x\":1685440741607,\"y\":16.5},{\"x\":1685441041684,\"y\":16.5},{\"x\":1685441341786,\"y\":16.5},{\"x\":1685441641830,\"y\":16.5},{\"x\":1685441941863,\"y\":16.5},{\"x\":1685442241939,\"y\":17},{\"x\":1685442542010,\"y\":17.3},{\"x\":1685442842108,\"y\":17.2},{\"x\":1685443142199,\"y\":16.6},{\"x\":1685443442319,\"y\":17.2},{\"x\":1685443742376,\"y\":16.5},{\"x\":1685444042480,\"y\":16.8},{\"x\":1685444342574,\"y\":16.8},{\"x\":1685444642649,\"y\":16.5},{\"x\":1685444942748,\"y\":17.2},{\"x\":1685445242792,\"y\":17.1},{\"x\":1685445542799,\"y\":17.2},{\"x\":1685445842907,\"y\":17.3},{\"x\":1685446143003,\"y\":17.4},{\"x\":1685446443122,\"y\":17.4},{\"x\":1685446743194,\"y\":17.3},{\"x\":1685447043298,\"y\":17.2},{\"x\":1685447343341,\"y\":17.4},{\"x\":1685447643442,\"y\":17.2},{\"x\":1685447943557,\"y\":17.2},{\"x\":1685448243632,\"y\":17.3},{\"x\":1685448543708,\"y\":17.2},{\"x\":1685448843747,\"y\":17.1},{\"x\":1685449143768,\"y\":16.6},{\"x\":1685449443878,\"y\":16.5},{\"x\":1685449743965,\"y\":16.5},{\"x\":1685450044053,\"y\":16.5},{\"x\":1685450344178,\"y\":17.2},{\"x\":1685450644263,\"y\":17.3},{\"x\":1685450944279,\"y\":17.4},{\"x\":1685451244382,\"y\":17.2},{\"x\":1685451544448,\"y\":17.3},{\"x\":1685451844600,\"y\":17.3},{\"x\":1685452144626,\"y\":17.4},{\"x\":1685452444701,\"y\":17.4},{\"x\":1685452744749,\"y\":17.2},{\"x\":1685453044818,\"y\":17.4},{\"x\":1685453344909,\"y\":17.7},{\"x\":1685453645071,\"y\":17.6},{\"x\":1685453945113,\"y\":17.5},{\"x\":1685454245205,\"y\":17.6},{\"x\":1685454545276,\"y\":17.6},{\"x\":1685454845390,\"y\":17.7},{\"x\":1685455145455,\"y\":17.8},{\"x\":1685455445552,\"y\":17.7},{\"x\":1685455745631,\"y\":17.7},{\"x\":1685456045706,\"y\":17.7},{\"x\":1685456345813,\"y\":17.6},{\"x\":1685456645826,\"y\":17.7},{\"x\":1685456945928,\"y\":17.7},{\"x\":1685457246034,\"y\":17.4},{\"x\":1685457546104,\"y\":17.4},{\"x\":1685457846188,\"y\":17.6},{\"x\":1685458146263,\"y\":17.9},{\"x\":1685458446341,\"y\":17.9},{\"x\":1685458746470,\"y\":17.9},{\"x\":1685459046554,\"y\":17.8},{\"x\":1685459346643,\"y\":17.7},{\"x\":1685459646708,\"y\":17.9},{\"x\":1685459946734,\"y\":18},{\"x\":1685460246833,\"y\":17.9},{\"x\":1685460546913,\"y\":17.9},{\"x\":1685460847002,\"y\":17.9},{\"x\":1685461147079,\"y\":17.9},{\"x\":1685461447115,\"y\":17.9},{\"x\":1685461747181,\"y\":17.9},{\"x\":1685462047303,\"y\":17.9},{\"x\":1685462347360,\"y\":17.9},{\"x\":1685462647457,\"y\":17.9},{\"x\":1685462947533,\"y\":17.9},{\"x\":1685463247622,\"y\":17.9},{\"x\":1685463437946,\"y\":17.9},{\"x\":1685463752894,\"y\":17.9},{\"x\":1685464067891,\"y\":17.9},{\"x\":1685464367932,\"y\":17.9},{\"x\":1685464668016,\"y\":18.2},{\"x\":1685464968034,\"y\":18.2},{\"x\":1685465268109,\"y\":17.9},{\"x\":1685465568127,\"y\":17.9},{\"x\":1685465868179,\"y\":18},{\"x\":1685466168200,\"y\":18},{\"x\":1685466468292,\"y\":17.9},{\"x\":1685466783247,\"y\":17.9},{\"x\":1685467083281,\"y\":18},{\"x\":1685467383324,\"y\":18},{\"x\":1685467683356,\"y\":17.9},{\"x\":1685467983393,\"y\":17.9},{\"x\":1685468283508,\"y\":18},{\"x\":1685468598507,\"y\":17.9},{\"x\":1685468898532,\"y\":17.9},{\"x\":1685469198568,\"y\":17.9},{\"x\":1685469498602,\"y\":17.9},{\"x\":1685469798635,\"y\":17.9},{\"x\":1685470098653,\"y\":17.8},{\"x\":1685470398701,\"y\":17.8},{\"x\":1685470698741,\"y\":17.8},{\"x\":1685470998770,\"y\":17.7},{\"x\":1685471298800,\"y\":17.8},{\"x\":1685471598821,\"y\":17.9},{\"x\":1685471898877,\"y\":17.7},{\"x\":1685472198886,\"y\":17.7},{\"x\":1685472498957,\"y\":17.7},{\"x\":1685472798992,\"y\":17.7},{\"x\":1685473099049,\"y\":17.9},{\"x\":1685473414059,\"y\":17.9},{\"x\":1685473714086,\"y\":17.7},{\"x\":1685474014164,\"y\":17.6},{\"x\":1685474314238,\"y\":17.4},{\"x\":1685474629191,\"y\":17.5},{\"x\":1685474929212,\"y\":17.6},{\"x\":1685475229261,\"y\":17.7},{\"x\":1685475529275,\"y\":17.4},{\"x\":1685475829340,\"y\":17.4},{\"x\":1685476129417,\"y\":17.4},{\"x\":1685476429433,\"y\":17.4},{\"x\":1685476729449,\"y\":17.3},{\"x\":1685477029470,\"y\":17.2},{\"x\":1685477329512,\"y\":17.2},{\"x\":1685477629544,\"y\":17.3},{\"x\":1685477929562,\"y\":17.4},{\"x\":1685478229615,\"y\":17.3},{\"x\":1685478529641,\"y\":17},{\"x\":1685478829667,\"y\":16.5},{\"x\":1685479129749,\"y\":16.5},{\"x\":1685479429791,\"y\":16.5},{\"x\":1685479729807,\"y\":16.5},{\"x\":1685480029843,\"y\":16.5},{\"x\":1685480329850,\"y\":16.5},{\"x\":1685480629899,\"y\":16.7},{\"x\":1685480929954,\"y\":16.5},{\"x\":1685481244998,\"y\":16.5},{\"x\":1685481545012,\"y\":16.5},{\"x\":1685481845043,\"y\":16.5},{\"x\":1685482145093,\"y\":16.5},{\"x\":1685482445120,\"y\":16.5},{\"x\":1685482745153,\"y\":16.5},{\"x\":1685483045222,\"y\":16.5},{\"x\":1685483345235,\"y\":16.5},{\"x\":1685483645258,\"y\":16.5},{\"x\":1685483945299,\"y\":16.5},{\"x\":1685484245340,\"y\":16.5},{\"x\":1685484545382,\"y\":16.5},{\"x\":1685484845385,\"y\":16.5},{\"x\":1685485145425,\"y\":16.5},{\"x\":1685485445459,\"y\":16.5},{\"x\":1685485745479,\"y\":16.5},{\"x\":1685486045546,\"y\":16.5},{\"x\":1685486345575,\"y\":16.5},{\"x\":1685486645633,\"y\":16.5},{\"x\":1685486945725,\"y\":16.5},{\"x\":1685487260693,\"y\":16.5},{\"x\":1685487560707,\"y\":16.5},{\"x\":1685487860814,\"y\":16.5},{\"x\":1685488175808,\"y\":16.5},{\"x\":1685488475825,\"y\":16.5},{\"x\":1685488775853,\"y\":16.5},{\"x\":1685489075914,\"y\":16.5},{\"x\":1685489375930,\"y\":16.5},{\"x\":1685489675982,\"y\":16.5},{\"x\":1685489976039,\"y\":16.5},{\"x\":1685490276048,\"y\":16.5},{\"x\":1685490576126,\"y\":16.5},{\"x\":1685490876150,\"y\":16.5},{\"x\":1685491176154,\"y\":16.5},{\"x\":1685491476262,\"y\":16.5},{\"x\":1685491791218,\"y\":16.5},{\"x\":1685492091237,\"y\":16.5},{\"x\":1685492391276,\"y\":16.5},{\"x\":1685492691341,\"y\":16.5},{\"x\":1685492991352,\"y\":16.5},{\"x\":1685493291416,\"y\":16.5},{\"x\":1685493591464,\"y\":16.5},{\"x\":1685493891476,\"y\":16.2},{\"x\":1685494191553,\"y\":16.5},{\"x\":1685494491555,\"y\":16.5},{\"x\":1685494806655,\"y\":16.5},{\"x\":1685495121636,\"y\":16.5},{\"x\":1685495436647,\"y\":16.5},{\"x\":1685495736703,\"y\":16.5},{\"x\":1685496036726,\"y\":16.4},{\"x\":1685496336779,\"y\":16.5},{\"x\":1685496636854,\"y\":16.4},{\"x\":1685496951873,\"y\":16.4},{\"x\":1685497251911,\"y\":16.5},{\"x\":1685497566911,\"y\":16.5},{\"x\":1685497866915,\"y\":16.5},{\"x\":1685498166972,\"y\":16.2},{\"x\":1685498466989,\"y\":16.2},{\"x\":1685498767043,\"y\":16.4},{\"x\":1685499067095,\"y\":16.2},{\"x\":1685499367129,\"y\":16.5},{\"x\":1685499667180,\"y\":16.4},{\"x\":1685499967184,\"y\":16.5},{\"x\":1685500282215,\"y\":16.5},{\"x\":1685500582236,\"y\":16.5},{\"x\":1685500882309,\"y\":16.5},{\"x\":1685501197336,\"y\":16.4},{\"x\":1685501497371,\"y\":16.2},{\"x\":1685501797405,\"y\":16.3},{\"x\":1685502097446,\"y\":16.1},{\"x\":1685502397471,\"y\":16},{\"x\":1685502697518,\"y\":15.7},{\"x\":1685502997526,\"y\":16},{\"x\":1685503297549,\"y\":16},{\"x\":1685503597589,\"y\":16.2},{\"x\":1685503897624,\"y\":16.2},{\"x\":1685504197687,\"y\":16},{\"x\":1685504497728,\"y\":15.9},{\"x\":1685504797753,\"y\":16},{\"x\":1685505097809,\"y\":16},{\"x\":1685505397837,\"y\":16.2},{\"x\":1685505697885,\"y\":16.4},{\"x\":1685505997899,\"y\":16.3},{\"x\":1685506297916,\"y\":16.1},{\"x\":1685506597958,\"y\":16},{\"x\":1685506897987,\"y\":16},{\"x\":1685507198004,\"y\":15.8},{\"x\":1685507498078,\"y\":15.9},{\"x\":1685507798110,\"y\":16},{\"x\":1685508098131,\"y\":15.8},{\"x\":1685508398152,\"y\":16},{\"x\":1685508698194,\"y\":15.9},{\"x\":1685508998213,\"y\":15.8},{\"x\":1685509298220,\"y\":15.8},{\"x\":1685509598290,\"y\":15.9},{\"x\":1685509898323,\"y\":15.7},{\"x\":1685510198331,\"y\":15.7},{\"x\":1685510498370,\"y\":15.7},{\"x\":1685510798393,\"y\":15.5},{\"x\":1685511098454,\"y\":15.8},{\"x\":1685511398491,\"y\":15.7},{\"x\":1685511698520,\"y\":15.9},{\"x\":1685512013528,\"y\":15.9},{\"x\":1685512313605,\"y\":15.8},{\"x\":1685512628564,\"y\":15.7},{\"x\":1685512928630,\"y\":15.5},{\"x\":1685513228650,\"y\":15.5},{\"x\":1685513528663,\"y\":15.5},{\"x\":1685513828703,\"y\":15.6},{\"x\":1685514143711,\"y\":15.7},{\"x\":1685514443729,\"y\":15.7},{\"x\":1685514743749,\"y\":15.8},{\"x\":1685515043937,\"y\":15.7},{\"x\":1685515358896,\"y\":15.5},{\"x\":1685515658956,\"y\":15.6},{\"x\":1685515959007,\"y\":15.7},{\"x\":1685516259087,\"y\":15.6},{\"x\":1685516559112,\"y\":15.9},{\"x\":1685516859194,\"y\":16},{\"x\":1685517174175,\"y\":15.9},{\"x\":1685517474229,\"y\":15.8},{\"x\":1685517774323,\"y\":15.7},{\"x\":1685518074366,\"y\":15.7},{\"x\":1685518374438,\"y\":15.7},{\"x\":1685518674561,\"y\":15.9},{\"x\":1685518974586,\"y\":15.7},{\"x\":1685519274602,\"y\":16},{\"x\":1685519574700,\"y\":15.7},{\"x\":1685519889719,\"y\":15.8},{\"x\":1685520189753,\"y\":15.9},{\"x\":1685520489831,\"y\":15.8},{\"x\":1685520789861,\"y\":15.9},{\"x\":1685521089913,\"y\":16},{\"x\":1685521390016,\"y\":15.7},{\"x\":1685521690069,\"y\":15.5},{\"x\":1685521990135,\"y\":15.5},{\"x\":1685522290216,\"y\":15.5},{\"x\":1685522590301,\"y\":15.5},{\"x\":1685522905260,\"y\":15.7},{\"x\":1685523205311,\"y\":15.7},{\"x\":1685523505402,\"y\":15.7},{\"x\":1685523805456,\"y\":15.6},{\"x\":1685524105543,\"y\":15.9},{\"x\":1685524405592,\"y\":15.7},{\"x\":1685524705754,\"y\":15.5},{\"x\":1685525005757,\"y\":15.5},{\"x\":1685525305830,\"y\":15.7},{\"x\":1685525605894,\"y\":15.5},{\"x\":1685525905934,\"y\":15.7},{\"x\":1685526205944,\"y\":15.7},{\"x\":1685526505986,\"y\":15.5},{\"x\":1685526806078,\"y\":15.6},{\"x\":1685527106180,\"y\":15.8},{\"x\":1685527406287,\"y\":15.5},{\"x\":1685527706314,\"y\":15.6},{\"x\":1685528006417,\"y\":15.7},{\"x\":1685528306469,\"y\":15.8},{\"x\":1685528606541,\"y\":15.9},{\"x\":1685528906576,\"y\":15.7},{\"x\":1685529206598,\"y\":15.7},{\"x\":1685529506705,\"y\":15.7},{\"x\":1685529806799,\"y\":15.5},{\"x\":1685530106900,\"y\":15.7},{\"x\":1685530406997,\"y\":15.9},{\"x\":1685530707031,\"y\":15.9},{\"x\":1685531007135,\"y\":15.7},{\"x\":1685531307217,\"y\":15.9},{\"x\":1685531607277,\"y\":15.7},{\"x\":1685531907312,\"y\":15.9},{\"x\":1685532207333,\"y\":16},{\"x\":1685532507449,\"y\":15.9},{\"x\":1685532807538,\"y\":16},{\"x\":1685533107607,\"y\":15.9},{\"x\":1685533407716,\"y\":16.1},{\"x\":1685533707801,\"y\":16.4},{\"x\":1685534007891,\"y\":16.5},{\"x\":1685534307936,\"y\":17.9},{\"x\":1685534607976,\"y\":18.5},{\"x\":1685534908043,\"y\":19},{\"x\":1685535208090,\"y\":19.4},{\"x\":1685535508186,\"y\":19.5},{\"x\":1685535808331,\"y\":19.5},{\"x\":1685536108352,\"y\":19.5},{\"x\":1685536408438,\"y\":19.9},{\"x\":1685536708630,\"y\":20.2},{\"x\":1685537023666,\"y\":20.3},{\"x\":1685537323705,\"y\":20.5},{\"x\":1685537623753,\"y\":20.4},{\"x\":1685537923799,\"y\":20.4},{\"x\":1685538223965,\"y\":20.5},{\"x\":1685538538965,\"y\":20.4},{\"x\":1685538839060,\"y\":20.5},{\"x\":1685539139180,\"y\":20.5},{\"x\":1685539439233,\"y\":20.5},{\"x\":1685539739375,\"y\":20.4},{\"x\":1685540039424,\"y\":20.6},{\"x\":1685540339520,\"y\":20.4},{\"x\":1685540639601,\"y\":20.6},{\"x\":1685540939668,\"y\":20.5},{\"x\":1685541239749,\"y\":20.5},{\"x\":1685541539840,\"y\":20.8},{\"x\":1685541839913,\"y\":20.5},{\"x\":1685542139995,\"y\":20.4},{\"x\":1685542440177,\"y\":20.8},{\"x\":1685542740192,\"y\":20.4},{\"x\":1685543040283,\"y\":20.8},{\"x\":1685543340389,\"y\":20.5},{\"x\":1685543640471,\"y\":20.8},{\"x\":1685543940473,\"y\":20.5},{\"x\":1685544240621,\"y\":20.8},{\"x\":1685544540685,\"y\":20.5},{\"x\":1685544840799,\"y\":20.7},{\"x\":1685545140901,\"y\":20.5},{\"x\":1685545441001,\"y\":20.8},{\"x\":1685545741065,\"y\":20.6},{\"x\":1685546041186,\"y\":20.5},{\"x\":1685546341243,\"y\":20.7},{\"x\":1685546641292,\"y\":20.4},{\"x\":1685546941376,\"y\":20.8},{\"x\":1685547241438,\"y\":20.5},{\"x\":1685547541565,\"y\":20.8},{\"x\":1685547841634,\"y\":20.5},{\"x\":1685548141711,\"y\":20.8},{\"x\":1685548441854,\"y\":20.6},{\"x\":1685548741902,\"y\":20.5},{\"x\":1685549042002,\"y\":20.8},{\"x\":1685549342099,\"y\":20.5},{\"x\":1685549642156,\"y\":20.8},{\"x\":1685549942238,\"y\":20.7},{\"x\":1685550242287,\"y\":20.5},{\"x\":1685550542321,\"y\":20.8},{\"x\":1685550842435,\"y\":20.7},{\"x\":1685551142532,\"y\":20.5},{\"x\":1685551442679,\"y\":20.8},{\"x\":1685551742742,\"y\":20.8},{\"x\":1685552042824,\"y\":20.7},{\"x\":1685552342904,\"y\":20.6},{\"x\":1685552643024,\"y\":20.7},{\"x\":1685552943102,\"y\":20.7},{\"x\":1685553243197,\"y\":20.7},{\"x\":1685553543262,\"y\":20.7},{\"x\":1685553843353,\"y\":20.8},{\"x\":1685554143366,\"y\":20.8},{\"x\":1685554443531,\"y\":20.8},{\"x\":1685554743589,\"y\":20.8},{\"x\":1685555043690,\"y\":20.8},{\"x\":1685555343799,\"y\":20.8},{\"x\":1685555643872,\"y\":20.8},{\"x\":1685555943930,\"y\":20.8},{\"x\":1685556244056,\"y\":20.8},{\"x\":1685556544148,\"y\":20.8},{\"x\":1685556844209,\"y\":20.8},{\"x\":1685557144344,\"y\":20.7},{\"x\":1685557444363,\"y\":20.8},{\"x\":1685557744405,\"y\":20.7},{\"x\":1685558044539,\"y\":20.7},{\"x\":1685558344605,\"y\":20.7},{\"x\":1685558644713,\"y\":20.7},{\"x\":1685558944785,\"y\":20.7},{\"x\":1685559244901,\"y\":20.6},{\"x\":1685559544945,\"y\":20.6},{\"x\":1685559845042,\"y\":20.5},{\"x\":1685560145143,\"y\":20.5},{\"x\":1685560445241,\"y\":20.6},{\"x\":1685560745257,\"y\":20.5},{\"x\":1685561045328,\"y\":20.5},{\"x\":1685561345445,\"y\":20.5},{\"x\":1685561645525,\"y\":20.6},{\"x\":1685561945646,\"y\":20.8},{\"x\":1685562245696,\"y\":20.7},{\"x\":1685562545795,\"y\":20.6},{\"x\":1685562845871,\"y\":20.7},{\"x\":1685563145971,\"y\":20.8},{\"x\":1685563446094,\"y\":20.7},{\"x\":1685563746173,\"y\":20.5},{\"x\":1685564046283,\"y\":20.8},{\"x\":1685564346314,\"y\":20.8},{\"x\":1685564646386,\"y\":20.7},{\"x\":1685564946460,\"y\":20.6},{\"x\":1685565246561,\"y\":20.5},{\"x\":1685565546656,\"y\":20.7},{\"x\":1685565846745,\"y\":20.8},{\"x\":1685566146812,\"y\":20.6},{\"x\":1685566446933,\"y\":20.7},{\"x\":1685566746982,\"y\":20.7},{\"x\":1685567047084,\"y\":20.5},{\"x\":1685567347176,\"y\":20.8},{\"x\":1685567647243,\"y\":20.5},{\"x\":1685567947275,\"y\":20.8},{\"x\":1685568247414,\"y\":20.8},{\"x\":1685568547487,\"y\":20.8},{\"x\":1685568847582,\"y\":20.6},{\"x\":1685569147698,\"y\":20.6},{\"x\":1685569447750,\"y\":20.5},{\"x\":1685569747835,\"y\":20.8},{\"x\":1685570047948,\"y\":20.7},{\"x\":1685570348041,\"y\":20.5},{\"x\":1685570648135,\"y\":20.7},{\"x\":1685570948186,\"y\":20.7},{\"x\":1685571248285,\"y\":20.5},{\"x\":1685571548397,\"y\":20.7},{\"x\":1685571848461,\"y\":20.6},{\"x\":1685572148552,\"y\":20.7},{\"x\":1685572448646,\"y\":20.8},{\"x\":1685572748724,\"y\":20.7},{\"x\":1685573048812,\"y\":20.4},{\"x\":1685573348924,\"y\":20.8},{\"x\":1685573648998,\"y\":20.7},{\"x\":1685573949035,\"y\":20.5},{\"x\":1685574249162,\"y\":20.7},{\"x\":1685574564170,\"y\":20.5},{\"x\":1685574864211,\"y\":20.3},{\"x\":1685575164295,\"y\":20.3},{\"x\":1685575464392,\"y\":20.3},{\"x\":1685575764473,\"y\":20.2},{\"x\":1685576064520,\"y\":20},{\"x\":1685576364561,\"y\":19.9},{\"x\":1685576664649,\"y\":19.6},{\"x\":1685576964764,\"y\":19.5},{\"x\":1685577264783,\"y\":19.5},{\"x\":1685577564825,\"y\":19.5},{\"x\":1685577864898,\"y\":19.5},{\"x\":1685578165022,\"y\":19.5},{\"x\":1685578465041,\"y\":19.5},{\"x\":1685578765123,\"y\":19.5},{\"x\":1685579065190,\"y\":19.5},{\"x\":1685579365242,\"y\":19.5},{\"x\":1685579665270,\"y\":19.5},{\"x\":1685579965342,\"y\":19.5},{\"x\":1685580265419,\"y\":19.5},{\"x\":1685580565474,\"y\":19.5},{\"x\":1685580865528,\"y\":19.5},{\"x\":1685581165617,\"y\":19.5},{\"x\":1685581465707,\"y\":19.5},{\"x\":1685581765780,\"y\":19.5},{\"x\":1685582065871,\"y\":19.5},{\"x\":1685582365914,\"y\":19.5},{\"x\":1685582665994,\"y\":19.5},{\"x\":1685582966029,\"y\":19.5},{\"x\":1685583266054,\"y\":19.5},{\"x\":1685583566137,\"y\":19.5},{\"x\":1685583866167,\"y\":19.5},{\"x\":1685584166249,\"y\":19.5},{\"x\":1685584466332,\"y\":19.5},{\"x\":1685584766387,\"y\":19.5},{\"x\":1685585066444,\"y\":19.5},{\"x\":1685585366558,\"y\":19.5},{\"x\":1685585666591,\"y\":19.5},{\"x\":1685585966705,\"y\":19.5},{\"x\":1685586281700,\"y\":19.5},{\"x\":1685586581763,\"y\":19.5},{\"x\":1685586881831,\"y\":19.5},{\"x\":1685587181935,\"y\":19.5},{\"x\":1685587481999,\"y\":19.4},{\"x\":1685587782043,\"y\":19.4},{\"x\":1685588082116,\"y\":19.4},{\"x\":1685588382188,\"y\":19.4},{\"x\":1685588682255,\"y\":19.4},{\"x\":1685588982296,\"y\":19.4},{\"x\":1685589282341,\"y\":19.3},{\"x\":1685589582405,\"y\":19.4},{\"x\":1685589882472,\"y\":19.4},{\"x\":1685590182540,\"y\":19.3},{\"x\":1685590482618,\"y\":19.3},{\"x\":1685590782722,\"y\":19.3},{\"x\":1685591082771,\"y\":19.3},{\"x\":1685591382819,\"y\":19.2},{\"x\":1685591682931,\"y\":19.2},{\"x\":1685591982967,\"y\":19.2},{\"x\":1685592282986,\"y\":19.2},{\"x\":1685592583082,\"y\":19.2},{\"x\":1685592883165,\"y\":19.2},{\"x\":1685593183195,\"y\":19.2},{\"x\":1685593483277,\"y\":19.1},{\"x\":1685593783380,\"y\":19.2},{\"x\":1685594083450,\"y\":19.2},{\"x\":1685594383491,\"y\":19.2},{\"x\":1685594683592,\"y\":19.1},{\"x\":1685594983618,\"y\":19.1},{\"x\":1685595283711,\"y\":19.1},{\"x\":1685595583730,\"y\":19.1},{\"x\":1685595883817,\"y\":19},{\"x\":1685596183896,\"y\":19},{\"x\":1685596483953,\"y\":19},{\"x\":1685596784061,\"y\":19},{\"x\":1685597084124,\"y\":19},{\"x\":1685597384139,\"y\":19.1},{\"x\":1685597684213,\"y\":19},{\"x\":1685597984240,\"y\":19},{\"x\":1685598284312,\"y\":19},{\"x\":1685598584338,\"y\":19},{\"x\":1685598884381,\"y\":19},{\"x\":1685599184436,\"y\":19},{\"x\":1685599499407,\"y\":19},{\"x\":1685599799490,\"y\":18.9},{\"x\":1685600099528,\"y\":19},{\"x\":1685600399539,\"y\":19},{\"x\":1685600699637,\"y\":19},{\"x\":1685600999644,\"y\":19},{\"x\":1685601299689,\"y\":19},{\"x\":1685601599758,\"y\":19},{\"x\":1685601899826,\"y\":19},{\"x\":1685602199848,\"y\":18.9},{\"x\":1685602499869,\"y\":18.9},{\"x\":1685602799939,\"y\":19},{\"x\":1685603099979,\"y\":18.9},{\"x\":1685603414978,\"y\":18.9},{\"x\":1685603715053,\"y\":19},{\"x\":1685604030039,\"y\":19},{\"x\":1685604330117,\"y\":19},{\"x\":1685604630167,\"y\":19},{\"x\":1685604930199,\"y\":19},{\"x\":1685605230238,\"y\":18.9},{\"x\":1685605530260,\"y\":19},{\"x\":1685605830318,\"y\":19},{\"x\":1685606130319,\"y\":19},{\"x\":1685606430335,\"y\":19},{\"x\":1685606730393,\"y\":19},{\"x\":1685607030458,\"y\":19},{\"x\":1685607330485,\"y\":19.5},{\"x\":1685607630516,\"y\":20.3},{\"x\":1685607930568,\"y\":20.6},{\"x\":1685608230576,\"y\":20.4},{\"x\":1685608530643,\"y\":20.5},{\"x\":1685608830684,\"y\":20.5},{\"x\":1685609130723,\"y\":20.5},{\"x\":1685609430778,\"y\":20.5},{\"x\":1685609745753,\"y\":20.7},{\"x\":1685610045798,\"y\":20.4},{\"x\":1685610345834,\"y\":20.7},{\"x\":1685610645876,\"y\":20.4},{\"x\":1685610945921,\"y\":20.8},{\"x\":1685611245935,\"y\":20.5},{\"x\":1685611545963,\"y\":20.8},{\"x\":1685611845989,\"y\":20.6},{\"x\":1685612146117,\"y\":20.5},{\"x\":1685612446189,\"y\":20.8},{\"x\":1685612746312,\"y\":20.6},{\"x\":1685613046346,\"y\":20.5},{\"x\":1685613346425,\"y\":20.8},{\"x\":1685613646580,\"y\":20.6},{\"x\":1685613961556,\"y\":20.6},{\"x\":1685614261648,\"y\":20.7},{\"x\":1685614576669,\"y\":20.7},{\"x\":1685614876695,\"y\":20.5},{\"x\":1685615176733,\"y\":20.4},{\"x\":1685615476808,\"y\":20.7},{\"x\":1685615776863,\"y\":20.7},{\"x\":1685616076917,\"y\":20.5},{\"x\":1685616376987,\"y\":20.4},{\"x\":1685616677033,\"y\":20.3},{\"x\":1685616977113,\"y\":20.3},{\"x\":1685617277203,\"y\":20.3},{\"x\":1685617577236,\"y\":20.3},{\"x\":1685617877275,\"y\":20.2},{\"x\":1685618177324,\"y\":20.3},{\"x\":1685618477408,\"y\":20.2},{\"x\":1685618777447,\"y\":20.3},{\"x\":1685619077601,\"y\":20.2},{\"x\":1685619377646,\"y\":20.2},{\"x\":1685619677730,\"y\":20.1},{\"x\":1685619977761,\"y\":20.2},{\"x\":1685620017473,\"y\":20.1},{\"x\":1685620080667,\"y\":20.1}],[{\"x\":1685015251039,\"y\":19.18},{\"x\":1685015551015,\"y\":19.18},{\"x\":1685015851010,\"y\":19.18},{\"x\":1685016151028,\"y\":19.09},{\"x\":1685016451042,\"y\":18.75},{\"x\":1685016751019,\"y\":18.66},{\"x\":1685017051028,\"y\":18.75},{\"x\":1685017351047,\"y\":18.89},{\"x\":1685017651031,\"y\":18.8},{\"x\":1685017951098,\"y\":18.83},{\"x\":1685018251018,\"y\":18.3},{\"x\":1685018551020,\"y\":18.3},{\"x\":1685018851032,\"y\":18.36},{\"x\":1685019151038,\"y\":18.36},{\"x\":1685019451035,\"y\":19.14},{\"x\":1685019751031,\"y\":19.04},{\"x\":1685020051038,\"y\":19.64},{\"x\":1685020351040,\"y\":19.42},{\"x\":1685020651063,\"y\":19.88},{\"x\":1685020951079,\"y\":19.73},{\"x\":1685021251036,\"y\":19.73},{\"x\":1685021551137,\"y\":19.54},{\"x\":1685021851047,\"y\":19.21},{\"x\":1685022151057,\"y\":19.13},{\"x\":1685022451042,\"y\":19.21},{\"x\":1685022751080,\"y\":19.1},{\"x\":1685023051043,\"y\":18.46},{\"x\":1685023351058,\"y\":18.46},{\"x\":1685023651118,\"y\":18.47},{\"x\":1685023951188,\"y\":18.48},{\"x\":1685024251055,\"y\":18.48},{\"x\":1685024551051,\"y\":18.44},{\"x\":1685024851052,\"y\":18.44},{\"x\":1685025151135,\"y\":18.73},{\"x\":1685025451076,\"y\":18.73},{\"x\":1685025751061,\"y\":18.73},{\"x\":1685026051071,\"y\":19.02},{\"x\":1685026351158,\"y\":19.05},{\"x\":1685026651115,\"y\":19.03},{\"x\":1685026951071,\"y\":19.03},{\"x\":1685027251066,\"y\":19.03},{\"x\":1685027551148,\"y\":18.62},{\"x\":1685027851058,\"y\":18.62},{\"x\":1685028151059,\"y\":18.62},{\"x\":1685028451112,\"y\":19},{\"x\":1685028751146,\"y\":18.91},{\"x\":1685029051091,\"y\":18.1},{\"x\":1685029351104,\"y\":18.12},{\"x\":1685029651134,\"y\":18.51},{\"x\":1685029951350,\"y\":18.12},{\"x\":1685030251085,\"y\":18.51},{\"x\":1685030551087,\"y\":18.42},{\"x\":1685030851113,\"y\":18.46},{\"x\":1685031151160,\"y\":18.37},{\"x\":1685031451109,\"y\":18.33},{\"x\":1685031751080,\"y\":18.21},{\"x\":1685032051082,\"y\":18.3},{\"x\":1685032351518,\"y\":18.3},{\"x\":1685032651206,\"y\":17.89},{\"x\":1685032951081,\"y\":17.8},{\"x\":1685033251086,\"y\":17.8},{\"x\":1685033551096,\"y\":17.8},{\"x\":1685033851075,\"y\":17.8},{\"x\":1685034151086,\"y\":17.78},{\"x\":1685034451139,\"y\":17.63},{\"x\":1685034751066,\"y\":17.63},{\"x\":1685035051075,\"y\":17.63},{\"x\":1685035351084,\"y\":17.54},{\"x\":1685035651091,\"y\":17.54},{\"x\":1685035951102,\"y\":17.55},{\"x\":1685036251109,\"y\":17.28},{\"x\":1685036551080,\"y\":17.54},{\"x\":1685036851085,\"y\":17.25},{\"x\":1685037151113,\"y\":17.2},{\"x\":1685037451085,\"y\":16.99},{\"x\":1685037751124,\"y\":16.99},{\"x\":1685038051100,\"y\":16.99},{\"x\":1685038351547,\"y\":17.1},{\"x\":1685038651116,\"y\":17.02},{\"x\":1685038951187,\"y\":16.94},{\"x\":1685039251190,\"y\":16.88},{\"x\":1685039551190,\"y\":16.85},{\"x\":1685039851181,\"y\":16.85},{\"x\":1685040151138,\"y\":16.82},{\"x\":1685040451132,\"y\":16.79},{\"x\":1685040751183,\"y\":16.79},{\"x\":1685041051160,\"y\":16.79},{\"x\":1685041351171,\"y\":16.7},{\"x\":1685041651173,\"y\":16.44},{\"x\":1685041951215,\"y\":16.44},{\"x\":1685042251220,\"y\":16.48},{\"x\":1685042551262,\"y\":16.52},{\"x\":1685042851204,\"y\":16.48},{\"x\":1685043151252,\"y\":16.52},{\"x\":1685043451209,\"y\":16.4},{\"x\":1685043751246,\"y\":16.26},{\"x\":1685044051210,\"y\":16.26},{\"x\":1685044351522,\"y\":16.26},{\"x\":1685044651215,\"y\":16.17},{\"x\":1685044951218,\"y\":16.17},{\"x\":1685045251281,\"y\":15.98},{\"x\":1685045551345,\"y\":15.98},{\"x\":1685045851232,\"y\":16.01},{\"x\":1685046151240,\"y\":15.98},{\"x\":1685046451276,\"y\":15.75},{\"x\":1685046751262,\"y\":15.75},{\"x\":1685047051266,\"y\":15.75},{\"x\":1685047351285,\"y\":15.68},{\"x\":1685047651267,\"y\":15.68},{\"x\":1685047951728,\"y\":15.49},{\"x\":1685048251321,\"y\":15.5},{\"x\":1685048551265,\"y\":15.49},{\"x\":1685048851352,\"y\":15.35},{\"x\":1685049151302,\"y\":15.35},{\"x\":1685049451264,\"y\":14.98},{\"x\":1685049751268,\"y\":14.98},{\"x\":1685050051322,\"y\":14.97},{\"x\":1685050351309,\"y\":14.97},{\"x\":1685050651266,\"y\":14.75},{\"x\":1685050951264,\"y\":14.75},{\"x\":1685051251276,\"y\":14.82},{\"x\":1685051551291,\"y\":14.73},{\"x\":1685051851384,\"y\":14.57},{\"x\":1685052151274,\"y\":14.41},{\"x\":1685052451281,\"y\":14.41},{\"x\":1685052751280,\"y\":14.35},{\"x\":1685053051279,\"y\":14.49},{\"x\":1685053351326,\"y\":14.29},{\"x\":1685053651310,\"y\":14.3},{\"x\":1685053951323,\"y\":14.29},{\"x\":1685054251377,\"y\":13.95},{\"x\":1685054551303,\"y\":13.95},{\"x\":1685054851307,\"y\":13.95},{\"x\":1685055151655,\"y\":13.71},{\"x\":1685055451302,\"y\":13.71},{\"x\":1685055751335,\"y\":13.58},{\"x\":1685056051330,\"y\":13.55},{\"x\":1685056351435,\"y\":13.58},{\"x\":1685056651370,\"y\":13.33},{\"x\":1685056951348,\"y\":13.58},{\"x\":1685057251335,\"y\":13.23},{\"x\":1685057551642,\"y\":13.23},{\"x\":1685057851342,\"y\":13.23},{\"x\":1685058151360,\"y\":12.98},{\"x\":1685058451395,\"y\":13},{\"x\":1685058751511,\"y\":13.01},{\"x\":1685059051391,\"y\":13.01},{\"x\":1685059351339,\"y\":12.9},{\"x\":1685059651359,\"y\":12.9},{\"x\":1685059951438,\"y\":12.48},{\"x\":1685060251356,\"y\":12.48},{\"x\":1685060551372,\"y\":12.48},{\"x\":1685060851371,\"y\":12.32},{\"x\":1685061151374,\"y\":12.32},{\"x\":1685061451412,\"y\":11.72},{\"x\":1685061751394,\"y\":11.63},{\"x\":1685062051381,\"y\":11.72},{\"x\":1685062352047,\"y\":11.55},{\"x\":1685062651444,\"y\":11.36},{\"x\":1685062951373,\"y\":11.46},{\"x\":1685063251410,\"y\":11.36},{\"x\":1685063551479,\"y\":11.37},{\"x\":1685063851380,\"y\":11.36},{\"x\":1685064151391,\"y\":11.44},{\"x\":1685064451471,\"y\":11.34},{\"x\":1685064751589,\"y\":11.32},{\"x\":1685065051396,\"y\":11.26},{\"x\":1685065351399,\"y\":11.22},{\"x\":1685065651409,\"y\":11.39},{\"x\":1685065952272,\"y\":11.33},{\"x\":1685066251396,\"y\":11.21},{\"x\":1685066551403,\"y\":11.21},{\"x\":1685066851417,\"y\":11.34},{\"x\":1685067151442,\"y\":11.28},{\"x\":1685067451399,\"y\":11.27},{\"x\":1685067751460,\"y\":11.06},{\"x\":1685068051420,\"y\":11.27},{\"x\":1685068351485,\"y\":11.02},{\"x\":1685068651412,\"y\":11.02},{\"x\":1685068951402,\"y\":10.91},{\"x\":1685069251450,\"y\":11},{\"x\":1685069551482,\"y\":11},{\"x\":1685069851414,\"y\":10.88},{\"x\":1685070151414,\"y\":10.66},{\"x\":1685070451408,\"y\":10.66},{\"x\":1685070752331,\"y\":10.76},{\"x\":1685071051424,\"y\":10.76},{\"x\":1685071351428,\"y\":10.76},{\"x\":1685071651498,\"y\":10.55},{\"x\":1685071951451,\"y\":10.55},{\"x\":1685072251483,\"y\":10.55},{\"x\":1685072551428,\"y\":10.55},{\"x\":1685072851477,\"y\":10.34},{\"x\":1685073151490,\"y\":10.34},{\"x\":1685073451401,\"y\":10.34},{\"x\":1685073751433,\"y\":10.08},{\"x\":1685074051433,\"y\":10.08},{\"x\":1685074351659,\"y\":9.98},{\"x\":1685074651481,\"y\":9.98},{\"x\":1685074951442,\"y\":9.98},{\"x\":1685075251486,\"y\":9.76},{\"x\":1685075553145,\"y\":9.76},{\"x\":1685075851454,\"y\":9.76},{\"x\":1685076151559,\"y\":9.76},{\"x\":1685076451455,\"y\":9.76},{\"x\":1685076751637,\"y\":9.76},{\"x\":1685077051562,\"y\":10.23},{\"x\":1685077351466,\"y\":10.23},{\"x\":1685077651495,\"y\":10.23},{\"x\":1685077951596,\"y\":10.15},{\"x\":1685078251465,\"y\":10.36},{\"x\":1685078551490,\"y\":10.36},{\"x\":1685078851486,\"y\":10.82},{\"x\":1685079152413,\"y\":10.82},{\"x\":1685079451481,\"y\":11.06},{\"x\":1685079751484,\"y\":11.06},{\"x\":1685080051515,\"y\":11.26},{\"x\":1685080353153,\"y\":11.08},{\"x\":1685080651466,\"y\":11.37},{\"x\":1685080951559,\"y\":11.85},{\"x\":1685081251496,\"y\":12.07},{\"x\":1685081552662,\"y\":11.54},{\"x\":1685081851505,\"y\":11.54},{\"x\":1685082151498,\"y\":12.9},{\"x\":1685082451574,\"y\":11.98},{\"x\":1685082752768,\"y\":12.05},{\"x\":1685083051516,\"y\":12.05},{\"x\":1685083351518,\"y\":12.54},{\"x\":1685083651522,\"y\":12.54},{\"x\":1685083951825,\"y\":13.01},{\"x\":1685084251526,\"y\":13.01},{\"x\":1685084551531,\"y\":13.84},{\"x\":1685084851537,\"y\":14.15},{\"x\":1685085154315,\"y\":14.2},{\"x\":1685085451540,\"y\":14.2},{\"x\":1685085751599,\"y\":14.74},{\"x\":1685086051532,\"y\":14.65},{\"x\":1685086352440,\"y\":14.65},{\"x\":1685086651538,\"y\":15.31},{\"x\":1685086951544,\"y\":15.31},{\"x\":1685087251583,\"y\":15.43},{\"x\":1685087552469,\"y\":15.61},{\"x\":1685087851550,\"y\":15.43},{\"x\":1685088151549,\"y\":15.75},{\"x\":1685088451547,\"y\":15.75},{\"x\":1685088752994,\"y\":16.44},{\"x\":1685089051545,\"y\":16.44},{\"x\":1685089351589,\"y\":16.84},{\"x\":1685089651598,\"y\":16.44},{\"x\":1685089952825,\"y\":16.84},{\"x\":1685090251561,\"y\":17.35},{\"x\":1685090551636,\"y\":17.66},{\"x\":1685090851564,\"y\":17.56},{\"x\":1685091152016,\"y\":17.66},{\"x\":1685091451594,\"y\":17.93},{\"x\":1685091751567,\"y\":18.04},{\"x\":1685092051581,\"y\":17.93},{\"x\":1685092352469,\"y\":18.08},{\"x\":1685092651613,\"y\":18.17},{\"x\":1685092951598,\"y\":18.25},{\"x\":1685093251572,\"y\":18.23},{\"x\":1685093551684,\"y\":18.43},{\"x\":1685093851596,\"y\":18.5},{\"x\":1685094151578,\"y\":18.5},{\"x\":1685094451581,\"y\":18.85},{\"x\":1685094753849,\"y\":18.85},{\"x\":1685095051634,\"y\":19.17},{\"x\":1685095351592,\"y\":19.09},{\"x\":1685095651582,\"y\":19.37},{\"x\":1685095952262,\"y\":19.37},{\"x\":1685096251665,\"y\":19.46},{\"x\":1685096551653,\"y\":19.46},{\"x\":1685096851597,\"y\":19.46},{\"x\":1685097151894,\"y\":19.32},{\"x\":1685097451605,\"y\":19.59},{\"x\":1685097751603,\"y\":19.85},{\"x\":1685098051603,\"y\":19.75},{\"x\":1685098351825,\"y\":19.65},{\"x\":1685098651611,\"y\":19.82},{\"x\":1685098951612,\"y\":19.65},{\"x\":1685099251620,\"y\":19.57},{\"x\":1685099554557,\"y\":19.84},{\"x\":1685099851611,\"y\":19.98},{\"x\":1685100151610,\"y\":19.98},{\"x\":1685100451674,\"y\":19.9},{\"x\":1685100751631,\"y\":19.9},{\"x\":1685101051671,\"y\":20.45},{\"x\":1685101351650,\"y\":20.45},{\"x\":1685101651625,\"y\":20.45},{\"x\":1685101952592,\"y\":19.74},{\"x\":1685102251628,\"y\":19.74},{\"x\":1685102551620,\"y\":19.74},{\"x\":1685102851630,\"y\":19.84},{\"x\":1685103152663,\"y\":20.53},{\"x\":1685103451625,\"y\":20.53},{\"x\":1685103751638,\"y\":20.37},{\"x\":1685104051653,\"y\":20.37},{\"x\":1685104351690,\"y\":20.42},{\"x\":1685104651625,\"y\":20.92},{\"x\":1685104951669,\"y\":21.05},{\"x\":1685105251635,\"y\":20.92},{\"x\":1685106270209,\"y\":21.51},{\"x\":1685106570210,\"y\":21.28},{\"x\":1685106870262,\"y\":21.31},{\"x\":1685107170259,\"y\":20.87},{\"x\":1685107470202,\"y\":20.87},{\"x\":1685107770205,\"y\":20.87},{\"x\":1685108070201,\"y\":20.24},{\"x\":1685108370268,\"y\":20.24},{\"x\":1685108670211,\"y\":20.81},{\"x\":1685108970215,\"y\":20.61},{\"x\":1685109270264,\"y\":21.02},{\"x\":1685109570225,\"y\":21.02},{\"x\":1685109870210,\"y\":21.35},{\"x\":1685110170276,\"y\":21.42},{\"x\":1685110470215,\"y\":21.08},{\"x\":1685110770218,\"y\":21.08},{\"x\":1685111070267,\"y\":21.3},{\"x\":1685111370237,\"y\":21.32},{\"x\":1685111670242,\"y\":21.21},{\"x\":1685111970278,\"y\":21.56},{\"x\":1685112270243,\"y\":21.43},{\"x\":1685112570286,\"y\":20.62},{\"x\":1685112870312,\"y\":20.61},{\"x\":1685113170279,\"y\":20.61},{\"x\":1685113470259,\"y\":20.52},{\"x\":1685113770274,\"y\":21.03},{\"x\":1685114070330,\"y\":20.96},{\"x\":1685114370346,\"y\":20.62},{\"x\":1685114670290,\"y\":20.62},{\"x\":1685114970303,\"y\":20.62},{\"x\":1685115270293,\"y\":20.57},{\"x\":1685115570345,\"y\":20.81},{\"x\":1685115870301,\"y\":20.81},{\"x\":1685116170341,\"y\":20.85},{\"x\":1685116470425,\"y\":20.35},{\"x\":1685116770319,\"y\":20.73},{\"x\":1685117070306,\"y\":20.35},{\"x\":1685117370344,\"y\":20.75},{\"x\":1685117670330,\"y\":20.75},{\"x\":1685117970336,\"y\":20.75},{\"x\":1685118270330,\"y\":20.45},{\"x\":1685118570325,\"y\":20.45},{\"x\":1685118870362,\"y\":19.95},{\"x\":1685119170359,\"y\":19.95},{\"x\":1685119470369,\"y\":19.95},{\"x\":1685119770365,\"y\":19.92},{\"x\":1685120070363,\"y\":20.06},{\"x\":1685120370385,\"y\":20.17},{\"x\":1685120670359,\"y\":20.17},{\"x\":1685120970383,\"y\":20.08},{\"x\":1685121270373,\"y\":19.95},{\"x\":1685121570376,\"y\":19.85},{\"x\":1685121870394,\"y\":19.88},{\"x\":1685122170385,\"y\":19.88},{\"x\":1685122470467,\"y\":19},{\"x\":1685122770415,\"y\":19},{\"x\":1685123070412,\"y\":19},{\"x\":1685123370409,\"y\":18.86},{\"x\":1685123670402,\"y\":18.86},{\"x\":1685123970418,\"y\":18.86},{\"x\":1685124270399,\"y\":18.86},{\"x\":1685124570415,\"y\":18.79},{\"x\":1685124870446,\"y\":18.79},{\"x\":1685125170457,\"y\":18.85},{\"x\":1685125470494,\"y\":18.85},{\"x\":1685125770424,\"y\":18.85},{\"x\":1685126070436,\"y\":18.71},{\"x\":1685126370464,\"y\":18.71},{\"x\":1685126670446,\"y\":18.67},{\"x\":1685126970458,\"y\":18.67},{\"x\":1685127270730,\"y\":17.76},{\"x\":1685127570540,\"y\":17.84},{\"x\":1685127870479,\"y\":17.42},{\"x\":1685128170488,\"y\":17.42},{\"x\":1685128470446,\"y\":17.32},{\"x\":1685128770711,\"y\":17.38},{\"x\":1685129070493,\"y\":17.55},{\"x\":1685129370460,\"y\":17.46},{\"x\":1685129670456,\"y\":17.51},{\"x\":1685129970444,\"y\":16.98},{\"x\":1685130270452,\"y\":16.94},{\"x\":1685130570463,\"y\":16.94},{\"x\":1685130870454,\"y\":16.63},{\"x\":1685131170558,\"y\":16.63},{\"x\":1685131470459,\"y\":16.63},{\"x\":1685131770480,\"y\":15.83},{\"x\":1685132070539,\"y\":15.61},{\"x\":1685132370654,\"y\":15.61},{\"x\":1685132670510,\"y\":15.17},{\"x\":1685132970470,\"y\":15.17},{\"x\":1685133270483,\"y\":15.17},{\"x\":1685133570534,\"y\":15.33},{\"x\":1685133870537,\"y\":15.17},{\"x\":1685134170481,\"y\":15.33},{\"x\":1685134470522,\"y\":15.17},{\"x\":1685134770619,\"y\":15.17},{\"x\":1685135070468,\"y\":15.17},{\"x\":1685135370539,\"y\":14.12},{\"x\":1685135670470,\"y\":14.12},{\"x\":1685135970588,\"y\":14.12},{\"x\":1685136270506,\"y\":13.94},{\"x\":1685136570482,\"y\":13.94},{\"x\":1685136870483,\"y\":13.88},{\"x\":1685137170591,\"y\":13.53},{\"x\":1685137470482,\"y\":13.29},{\"x\":1685137770513,\"y\":13.29},{\"x\":1685138070565,\"y\":13.24},{\"x\":1685138370612,\"y\":13.3},{\"x\":1685138670513,\"y\":12.65},{\"x\":1685138970488,\"y\":13.03},{\"x\":1685139270567,\"y\":12.44},{\"x\":1685139570520,\"y\":12.44},{\"x\":1685139870495,\"y\":12.47},{\"x\":1685140170514,\"y\":12.41},{\"x\":1685140470469,\"y\":12.41},{\"x\":1685140770566,\"y\":12.32},{\"x\":1685141070532,\"y\":11.91},{\"x\":1685141370505,\"y\":11.91},{\"x\":1685141670535,\"y\":11.91},{\"x\":1685141970572,\"y\":11.87},{\"x\":1685142270528,\"y\":11.87},{\"x\":1685142570539,\"y\":11.49},{\"x\":1685142870540,\"y\":11.49},{\"x\":1685143170721,\"y\":11.69},{\"x\":1685143470532,\"y\":11.69},{\"x\":1685143770537,\"y\":11.69},{\"x\":1685144070587,\"y\":11.38},{\"x\":1685144370585,\"y\":11.29},{\"x\":1685144670544,\"y\":11.38},{\"x\":1685144970592,\"y\":11.66},{\"x\":1685145270578,\"y\":11.66},{\"x\":1685145570616,\"y\":11.66},{\"x\":1685145870578,\"y\":11.54},{\"x\":1685146170575,\"y\":11.3},{\"x\":1685146470586,\"y\":11.26},{\"x\":1685146770618,\"y\":10.97},{\"x\":1685147070575,\"y\":10.97},{\"x\":1685147370627,\"y\":11.06},{\"x\":1685147670593,\"y\":10.78},{\"x\":1685147970600,\"y\":11.06},{\"x\":1685148270605,\"y\":10.65},{\"x\":1685148570644,\"y\":10.63},{\"x\":1685148870584,\"y\":10.63},{\"x\":1685149170590,\"y\":10.63},{\"x\":1685149470587,\"y\":10.44},{\"x\":1685149770583,\"y\":10.44},{\"x\":1685150070580,\"y\":10.1},{\"x\":1685150370584,\"y\":10.1},{\"x\":1685150670582,\"y\":10.03},{\"x\":1685150970594,\"y\":10.03},{\"x\":1685151270632,\"y\":10.08},{\"x\":1685151570591,\"y\":10.08},{\"x\":1685151870605,\"y\":9.61},{\"x\":1685152170611,\"y\":9.61},{\"x\":1685152470611,\"y\":9.42},{\"x\":1685152770613,\"y\":9.42},{\"x\":1685153070628,\"y\":9.48},{\"x\":1685153370683,\"y\":9.39},{\"x\":1685153670664,\"y\":9.33},{\"x\":1685153970636,\"y\":9.33},{\"x\":1685154270611,\"y\":9.33},{\"x\":1685154570650,\"y\":9.14},{\"x\":1685154870623,\"y\":9.14},{\"x\":1685155170939,\"y\":9.05},{\"x\":1685155470674,\"y\":8.95},{\"x\":1685155770628,\"y\":8.95},{\"x\":1685156070642,\"y\":8.86},{\"x\":1685156370678,\"y\":8.95},{\"x\":1685156670691,\"y\":8.95},{\"x\":1685156970647,\"y\":8.95},{\"x\":1685157270649,\"y\":8.48},{\"x\":1685157570728,\"y\":8.51},{\"x\":1685157870653,\"y\":8.67},{\"x\":1685158170653,\"y\":8.67},{\"x\":1685158470652,\"y\":8.47},{\"x\":1685158770755,\"y\":8.29},{\"x\":1685159070671,\"y\":8.29},{\"x\":1685159370691,\"y\":8.19},{\"x\":1685159670661,\"y\":8.19},{\"x\":1685159971379,\"y\":8.08},{\"x\":1685160270711,\"y\":8.04},{\"x\":1685160570739,\"y\":8.22},{\"x\":1685160870655,\"y\":8.22},{\"x\":1685161170787,\"y\":8.04},{\"x\":1685161470703,\"y\":8.24},{\"x\":1685161770668,\"y\":8.04},{\"x\":1685162070717,\"y\":8.02},{\"x\":1685162371170,\"y\":7.93},{\"x\":1685162670708,\"y\":8.02},{\"x\":1685162970719,\"y\":8.55},{\"x\":1685163270676,\"y\":8.55},{\"x\":1685163571126,\"y\":8.55},{\"x\":1685163870678,\"y\":8.77},{\"x\":1685164170681,\"y\":8.69},{\"x\":1685164470773,\"y\":9.46},{\"x\":1685164771057,\"y\":9.68},{\"x\":1685165070675,\"y\":9.97},{\"x\":1685165370677,\"y\":9.83},{\"x\":1685165670713,\"y\":10.29},{\"x\":1685165970916,\"y\":10.29},{\"x\":1685166270681,\"y\":10.47},{\"x\":1685166570684,\"y\":10.47},{\"x\":1685166870680,\"y\":11},{\"x\":1685167171095,\"y\":10.85},{\"x\":1685167470678,\"y\":11.53},{\"x\":1685167770689,\"y\":12.14},{\"x\":1685168070683,\"y\":12.14},{\"x\":1685168370787,\"y\":11.68},{\"x\":1685168670739,\"y\":11.68},{\"x\":1685168970743,\"y\":11.98},{\"x\":1685169270696,\"y\":11.98},{\"x\":1685169570699,\"y\":11.98},{\"x\":1685169870784,\"y\":13.66},{\"x\":1685170170701,\"y\":13.66},{\"x\":1685170470695,\"y\":13.66},{\"x\":1685170770757,\"y\":13.66},{\"x\":1685171070684,\"y\":13.98},{\"x\":1685171370697,\"y\":13.98},{\"x\":1685171670698,\"y\":14.38},{\"x\":1685171971138,\"y\":14.38},{\"x\":1685172270759,\"y\":13.85},{\"x\":1685172570699,\"y\":13.85},{\"x\":1685172870711,\"y\":13.85},{\"x\":1685173170778,\"y\":15.4},{\"x\":1685173470740,\"y\":15.5},{\"x\":1685173770702,\"y\":15.18},{\"x\":1685174070719,\"y\":15.49},{\"x\":1685174371094,\"y\":15.8},{\"x\":1685174670695,\"y\":15.49},{\"x\":1685174970800,\"y\":16.03},{\"x\":1685175270722,\"y\":15.86},{\"x\":1685175570763,\"y\":15.86},{\"x\":1685175870697,\"y\":16.35},{\"x\":1685176170707,\"y\":16.48},{\"x\":1685176470754,\"y\":16.94},{\"x\":1685176770748,\"y\":16.94},{\"x\":1685177070739,\"y\":17.05},{\"x\":1685177370721,\"y\":17.05},{\"x\":1685177670735,\"y\":17.05},{\"x\":1685177971115,\"y\":17.66},{\"x\":1685178270740,\"y\":17.66},{\"x\":1685178570747,\"y\":17.64},{\"x\":1685178870774,\"y\":18.01},{\"x\":1685179171072,\"y\":17.92},{\"x\":1685179470822,\"y\":18.19},{\"x\":1685179770758,\"y\":18.1},{\"x\":1685180070762,\"y\":18.19},{\"x\":1685180370805,\"y\":18.1},{\"x\":1685180670780,\"y\":18.51},{\"x\":1685180970809,\"y\":18.65},{\"x\":1685181270793,\"y\":18.72},{\"x\":1685181570900,\"y\":19.19},{\"x\":1685181870801,\"y\":19.1},{\"x\":1685182170811,\"y\":19.19},{\"x\":1685182470860,\"y\":19.44},{\"x\":1685182771048,\"y\":19.67},{\"x\":1685183070826,\"y\":19.44},{\"x\":1685183370877,\"y\":19.67},{\"x\":1685183670882,\"y\":19.85},{\"x\":1685183970939,\"y\":19.85},{\"x\":1685184270841,\"y\":19.85},{\"x\":1685184570853,\"y\":19.96},{\"x\":1685184870900,\"y\":20.18},{\"x\":1685185170967,\"y\":20.47},{\"x\":1685185470866,\"y\":20.18},{\"x\":1685185770871,\"y\":20.65},{\"x\":1685186070870,\"y\":20.65},{\"x\":1685186370892,\"y\":20.6},{\"x\":1685186670893,\"y\":20.57},{\"x\":1685186970904,\"y\":20.71},{\"x\":1685187270868,\"y\":20.71},{\"x\":1685187572989,\"y\":20.99},{\"x\":1685187870872,\"y\":20.99},{\"x\":1685188170877,\"y\":20.99},{\"x\":1685188470913,\"y\":21.13},{\"x\":1685188771470,\"y\":21.2},{\"x\":1685189070899,\"y\":21.12},{\"x\":1685189370894,\"y\":21.2},{\"x\":1685189670921,\"y\":21.41},{\"x\":1685189971610,\"y\":21.41},{\"x\":1685190270898,\"y\":21.41},{\"x\":1685190570940,\"y\":21.32},{\"x\":1685190870918,\"y\":21.32},{\"x\":1685191171751,\"y\":21.78},{\"x\":1685191470923,\"y\":21.68},{\"x\":1685191770922,\"y\":21.68},{\"x\":1685192070981,\"y\":22.17},{\"x\":1685192371026,\"y\":22.17},{\"x\":1685192670934,\"y\":22.17},{\"x\":1685192970954,\"y\":22.46},{\"x\":1685193270953,\"y\":22.46},{\"x\":1685193571076,\"y\":21.99},{\"x\":1685193870942,\"y\":21.91},{\"x\":1685194171071,\"y\":21.99},{\"x\":1685194470972,\"y\":21.91},{\"x\":1685194770981,\"y\":21.91},{\"x\":1685195070960,\"y\":23.06},{\"x\":1685195370975,\"y\":22.73},{\"x\":1685195670969,\"y\":23.06},{\"x\":1685195971032,\"y\":22.82},{\"x\":1685196270973,\"y\":22.82},{\"x\":1685196570982,\"y\":22.82},{\"x\":1685196870986,\"y\":21.82},{\"x\":1685197171084,\"y\":21.93},{\"x\":1685197470984,\"y\":22.31},{\"x\":1685197770988,\"y\":22.31},{\"x\":1685198070990,\"y\":22.31},{\"x\":1685198371020,\"y\":22.31},{\"x\":1685198671053,\"y\":22.37},{\"x\":1685198970993,\"y\":22.23},{\"x\":1685199271011,\"y\":22.37},{\"x\":1685199571046,\"y\":22.48},{\"x\":1685199871115,\"y\":21.98},{\"x\":1685200171006,\"y\":21.98},{\"x\":1685200471008,\"y\":21.98},{\"x\":1685200771192,\"y\":22.9},{\"x\":1685201071014,\"y\":22.9},{\"x\":1685201371067,\"y\":22.11},{\"x\":1685201671018,\"y\":22.11},{\"x\":1685201971372,\"y\":22.11},{\"x\":1685202271010,\"y\":21.91},{\"x\":1685202571020,\"y\":21.91},{\"x\":1685202871094,\"y\":22.37},{\"x\":1685203171957,\"y\":22.04},{\"x\":1685203471024,\"y\":21.92},{\"x\":1685203771087,\"y\":22.72},{\"x\":1685204071079,\"y\":22.72},{\"x\":1685204372043,\"y\":22.63},{\"x\":1685204671023,\"y\":22.63},{\"x\":1685204971067,\"y\":22.65},{\"x\":1685205271028,\"y\":22.65},{\"x\":1685205571490,\"y\":22.29},{\"x\":1685205871024,\"y\":21.93},{\"x\":1685206171037,\"y\":21.93},{\"x\":1685206471023,\"y\":21.53},{\"x\":1685206771057,\"y\":21.53},{\"x\":1685207071049,\"y\":20.84},{\"x\":1685207371039,\"y\":20.84},{\"x\":1685207671107,\"y\":20.92},{\"x\":1685207971068,\"y\":20.84},{\"x\":1685208271098,\"y\":22.62},{\"x\":1685208571110,\"y\":22.62},{\"x\":1685208871086,\"y\":22.62},{\"x\":1685209171774,\"y\":20.91},{\"x\":1685209471092,\"y\":20.82},{\"x\":1685209771092,\"y\":19.71},{\"x\":1685210071129,\"y\":19.73},{\"x\":1685210371211,\"y\":19.73},{\"x\":1685210671121,\"y\":19.24},{\"x\":1685210971142,\"y\":21.59},{\"x\":1685211271109,\"y\":21.59},{\"x\":1685211573673,\"y\":21.59},{\"x\":1685211871103,\"y\":21.59},{\"x\":1685212171150,\"y\":21.56},{\"x\":1685212471136,\"y\":21.47},{\"x\":1685212771652,\"y\":20.46},{\"x\":1685213071195,\"y\":20.46},{\"x\":1685213371188,\"y\":19.23},{\"x\":1685213671169,\"y\":19.15},{\"x\":1685213971280,\"y\":19.23},{\"x\":1685214271228,\"y\":19.41},{\"x\":1685214571150,\"y\":19.41},{\"x\":1685214871151,\"y\":19.24},{\"x\":1685215171157,\"y\":19.21},{\"x\":1685215471219,\"y\":19.21},{\"x\":1685215771223,\"y\":18.87},{\"x\":1685216071239,\"y\":18.72},{\"x\":1685216371187,\"y\":18.87},{\"x\":1685216671220,\"y\":16.48},{\"x\":1685216971167,\"y\":16.48},{\"x\":1685217271190,\"y\":16.48},{\"x\":1685217571385,\"y\":16.48},{\"x\":1685217871202,\"y\":16.48},{\"x\":1685218171176,\"y\":16.48},{\"x\":1685218471193,\"y\":17.13},{\"x\":1685218772083,\"y\":17.04},{\"x\":1685219071242,\"y\":16.94},{\"x\":1685219371182,\"y\":16.94},{\"x\":1685219671182,\"y\":16.94},{\"x\":1685219971327,\"y\":17.29},{\"x\":1685220271195,\"y\":17.28},{\"x\":1685220571204,\"y\":17.28},{\"x\":1685220871265,\"y\":16.43},{\"x\":1685221171213,\"y\":16.43},{\"x\":1685221471206,\"y\":16.43},{\"x\":1685221771244,\"y\":16.32},{\"x\":1685222071214,\"y\":16.32},{\"x\":1685222371240,\"y\":16.22},{\"x\":1685222671231,\"y\":15.63},{\"x\":1685222971227,\"y\":15.63},{\"x\":1685223271215,\"y\":15.63},{\"x\":1685223571642,\"y\":15.63},{\"x\":1685223871216,\"y\":14.69},{\"x\":1685224171238,\"y\":14.69},{\"x\":1685224471255,\"y\":15.05},{\"x\":1685224771718,\"y\":15.05},{\"x\":1685225071254,\"y\":15},{\"x\":1685225371225,\"y\":14.91},{\"x\":1685225671230,\"y\":14.91},{\"x\":1685225971278,\"y\":15.03},{\"x\":1685226271229,\"y\":14.76},{\"x\":1685226571268,\"y\":14.62},{\"x\":1685226871228,\"y\":14.62},{\"x\":1685227171310,\"y\":14.58},{\"x\":1685227471236,\"y\":14.47},{\"x\":1685227771233,\"y\":14.47},{\"x\":1685228071264,\"y\":14.56},{\"x\":1685228371438,\"y\":14.56},{\"x\":1685228671307,\"y\":14.37},{\"x\":1685228971287,\"y\":14.26},{\"x\":1685229271247,\"y\":14.17},{\"x\":1685229571283,\"y\":14.26},{\"x\":1685229871252,\"y\":14.08},{\"x\":1685230171240,\"y\":14.08},{\"x\":1685230471249,\"y\":13.81},{\"x\":1685230771547,\"y\":13.9},{\"x\":1685231071282,\"y\":13.88},{\"x\":1685231371247,\"y\":13.88},{\"x\":1685231671245,\"y\":13.57},{\"x\":1685231971299,\"y\":13.8},{\"x\":1685232271267,\"y\":13.8},{\"x\":1685232571258,\"y\":13.67},{\"x\":1685232871256,\"y\":13.67},{\"x\":1685233172128,\"y\":13.34},{\"x\":1685233471266,\"y\":13.26},{\"x\":1685233771304,\"y\":13.55},{\"x\":1685234071280,\"y\":13.5},{\"x\":1685234371267,\"y\":13.55},{\"x\":1685234671308,\"y\":13.47},{\"x\":1685234971294,\"y\":13.47},{\"x\":1685235271259,\"y\":13.47},{\"x\":1685235573201,\"y\":13.47},{\"x\":1685235871295,\"y\":13.51},{\"x\":1685236171262,\"y\":13.65},{\"x\":1685236471308,\"y\":13.36},{\"x\":1685236772640,\"y\":13.36},{\"x\":1685237071263,\"y\":13.55},{\"x\":1685237371273,\"y\":13.46},{\"x\":1685237671265,\"y\":13.46},{\"x\":1685237972838,\"y\":13.08},{\"x\":1685238271265,\"y\":13.08},{\"x\":1685238571268,\"y\":13.08},{\"x\":1685238871275,\"y\":13.28},{\"x\":1685239172611,\"y\":13.41},{\"x\":1685239471288,\"y\":13.33},{\"x\":1685239771274,\"y\":13.31},{\"x\":1685240071325,\"y\":13.15},{\"x\":1685240372227,\"y\":13.07},{\"x\":1685240671318,\"y\":13.07},{\"x\":1685240971329,\"y\":13.07},{\"x\":1685241271288,\"y\":13.07},{\"x\":1685241571466,\"y\":13.07},{\"x\":1685241871279,\"y\":12.98},{\"x\":1685242171369,\"y\":13.28},{\"x\":1685242471346,\"y\":13.28},{\"x\":1685242771307,\"y\":13.28},{\"x\":1685243071278,\"y\":13.28},{\"x\":1685243371329,\"y\":13.28},{\"x\":1685243671340,\"y\":13.34},{\"x\":1685243971443,\"y\":13.34},{\"x\":1685244271334,\"y\":13.34},{\"x\":1685244571292,\"y\":13.34},{\"x\":1685244871333,\"y\":13.47},{\"x\":1685245171537,\"y\":13.69},{\"x\":1685245471361,\"y\":13.69},{\"x\":1685245771298,\"y\":13.69},{\"x\":1685246071304,\"y\":13.6},{\"x\":1685246371537,\"y\":13.65},{\"x\":1685246671321,\"y\":13.56},{\"x\":1685246971335,\"y\":13.65},{\"x\":1685247271382,\"y\":13.48},{\"x\":1685247571470,\"y\":13.48},{\"x\":1685247871384,\"y\":13.28},{\"x\":1685248171411,\"y\":13.28},{\"x\":1685248471353,\"y\":13.24},{\"x\":1685248771403,\"y\":13.24},{\"x\":1685249071402,\"y\":13.23},{\"x\":1685249371376,\"y\":13.23},{\"x\":1685249671393,\"y\":13.23},{\"x\":1685249971493,\"y\":13.17},{\"x\":1685250271426,\"y\":13.08},{\"x\":1685250571467,\"y\":13.11},{\"x\":1685250871415,\"y\":13.14},{\"x\":1685251171439,\"y\":13.14},{\"x\":1685251471413,\"y\":13.14},{\"x\":1685251771409,\"y\":13.14},{\"x\":1685252071420,\"y\":13.19},{\"x\":1685252371615,\"y\":13.25},{\"x\":1685252671425,\"y\":13.17},{\"x\":1685252971434,\"y\":13.18},{\"x\":1685253271482,\"y\":13.38},{\"x\":1685253571596,\"y\":13.3},{\"x\":1685253871453,\"y\":13.38},{\"x\":1685254171462,\"y\":13.45},{\"x\":1685254471470,\"y\":13.51},{\"x\":1685254771721,\"y\":13.58},{\"x\":1685255071473,\"y\":13.58},{\"x\":1685255371478,\"y\":13.58},{\"x\":1685255671578,\"y\":13.78},{\"x\":1685255971776,\"y\":13.54},{\"x\":1685256271512,\"y\":13.78},{\"x\":1685256571498,\"y\":13.76},{\"x\":1685256871502,\"y\":13.87},{\"x\":1685257171620,\"y\":13.87},{\"x\":1685257471509,\"y\":14.11},{\"x\":1685257771511,\"y\":14.36},{\"x\":1685258071508,\"y\":14.36},{\"x\":1685258371764,\"y\":14.39},{\"x\":1685258671517,\"y\":14.56},{\"x\":1685258971516,\"y\":14.56},{\"x\":1685259271515,\"y\":14.92},{\"x\":1685259571742,\"y\":14.92},{\"x\":1685259871562,\"y\":15.01},{\"x\":1685260171531,\"y\":15.01},{\"x\":1685260471534,\"y\":15.01},{\"x\":1685260771696,\"y\":14.85},{\"x\":1685261071539,\"y\":15.04},{\"x\":1685261371589,\"y\":15.04},{\"x\":1685261671538,\"y\":15.4},{\"x\":1685261971867,\"y\":15.59},{\"x\":1685262271550,\"y\":15.59},{\"x\":1685262571548,\"y\":15.59},{\"x\":1685262871567,\"y\":15.46},{\"x\":1685263171794,\"y\":15.73},{\"x\":1685263471562,\"y\":15.69},{\"x\":1685263771602,\"y\":15.85},{\"x\":1685264071565,\"y\":15.71},{\"x\":1685264371846,\"y\":15.71},{\"x\":1685264671596,\"y\":15.72},{\"x\":1685264971615,\"y\":15.73},{\"x\":1685265271570,\"y\":15.73},{\"x\":1685265571596,\"y\":15.65},{\"x\":1685265871585,\"y\":15.47},{\"x\":1685266171591,\"y\":15.47},{\"x\":1685266471584,\"y\":15.48},{\"x\":1685266771720,\"y\":15.72},{\"x\":1685267071581,\"y\":15.72},{\"x\":1685267371587,\"y\":15.9},{\"x\":1685267671638,\"y\":16.17},{\"x\":1685267971896,\"y\":16.17},{\"x\":1685268271589,\"y\":15.98},{\"x\":1685268571603,\"y\":16.37},{\"x\":1685268871604,\"y\":16.37},{\"x\":1685269171897,\"y\":16.65},{\"x\":1685269471607,\"y\":16.58},{\"x\":1685269771613,\"y\":16.58},{\"x\":1685270071662,\"y\":16.41},{\"x\":1685270371882,\"y\":16.41},{\"x\":1685270671617,\"y\":16.41},{\"x\":1685270971621,\"y\":16.28},{\"x\":1685271271686,\"y\":16.19},{\"x\":1685271571713,\"y\":16.22},{\"x\":1685271871621,\"y\":16.15},{\"x\":1685272171630,\"y\":16.22},{\"x\":1685272471630,\"y\":15.92},{\"x\":1685272771817,\"y\":15.96},{\"x\":1685273071635,\"y\":15.96},{\"x\":1685273371650,\"y\":16.12},{\"x\":1685273671673,\"y\":15.95},{\"x\":1685273971701,\"y\":16.31},{\"x\":1685274271656,\"y\":16.31},{\"x\":1685274571692,\"y\":16.32},{\"x\":1685274871686,\"y\":16.32},{\"x\":1685275171921,\"y\":16.73},{\"x\":1685275471665,\"y\":16.65},{\"x\":1685275771698,\"y\":16.73},{\"x\":1685276071659,\"y\":16.22},{\"x\":1685276371756,\"y\":16.22},{\"x\":1685276671662,\"y\":16.48},{\"x\":1685276971675,\"y\":15.89},{\"x\":1685277271661,\"y\":15.89},{\"x\":1685277571970,\"y\":15.63},{\"x\":1685277871714,\"y\":15.68},{\"x\":1685278171675,\"y\":15.63},{\"x\":1685278471667,\"y\":15.56},{\"x\":1685278771920,\"y\":15.65},{\"x\":1685279071689,\"y\":15.87},{\"x\":1685279371671,\"y\":15.87},{\"x\":1685279671675,\"y\":15.87},{\"x\":1685279972052,\"y\":15.71},{\"x\":1685280271668,\"y\":15.71},{\"x\":1685280571670,\"y\":16.04},{\"x\":1685280871676,\"y\":16.04},{\"x\":1685281671888,\"y\":15.97},{\"x\":1685284096032,\"y\":15.5},{\"x\":1685284395921,\"y\":15.5},{\"x\":1685284695933,\"y\":15.5},{\"x\":1685284995930,\"y\":15.47},{\"x\":1685285295935,\"y\":15.47},{\"x\":1685285595944,\"y\":15.8},{\"x\":1685285895939,\"y\":15.8},{\"x\":1685286195949,\"y\":15.51},{\"x\":1685286495951,\"y\":15.51},{\"x\":1685286796009,\"y\":15.36},{\"x\":1685287095965,\"y\":15.27},{\"x\":1685287395953,\"y\":15.27},{\"x\":1685287695970,\"y\":15.36},{\"x\":1685287995955,\"y\":15.36},{\"x\":1685288295961,\"y\":15.34},{\"x\":1685288595996,\"y\":14.97},{\"x\":1685288895968,\"y\":14.97},{\"x\":1685289196133,\"y\":14.79},{\"x\":1685289495999,\"y\":14.79},{\"x\":1685289795972,\"y\":14.69},{\"x\":1685290095973,\"y\":14.69},{\"x\":1685290395973,\"y\":14.69},{\"x\":1685290695981,\"y\":14.79},{\"x\":1685290996003,\"y\":14.79},{\"x\":1685291295989,\"y\":14.79},{\"x\":1685291595985,\"y\":14.77},{\"x\":1685291896008,\"y\":14.5},{\"x\":1685292196008,\"y\":14.5},{\"x\":1685292496041,\"y\":14.55},{\"x\":1685292796005,\"y\":14.47},{\"x\":1685293096013,\"y\":14.55},{\"x\":1685293396082,\"y\":14.49},{\"x\":1685293696011,\"y\":14.45},{\"x\":1685293996026,\"y\":14.65},{\"x\":1685294296016,\"y\":14.65},{\"x\":1685294596031,\"y\":14.43},{\"x\":1685294896026,\"y\":14.43},{\"x\":1685295196031,\"y\":14.42},{\"x\":1685295496032,\"y\":14.4},{\"x\":1685295796073,\"y\":14.53},{\"x\":1685296096031,\"y\":14.53},{\"x\":1685296396105,\"y\":14.32},{\"x\":1685296696030,\"y\":14.24},{\"x\":1685296996047,\"y\":14.24},{\"x\":1685297296080,\"y\":14.48},{\"x\":1685297596038,\"y\":14.4},{\"x\":1685297896041,\"y\":14.48},{\"x\":1685298196059,\"y\":14.08},{\"x\":1685298496041,\"y\":14.08},{\"x\":1685298796049,\"y\":14.33},{\"x\":1685299096058,\"y\":14},{\"x\":1685299396061,\"y\":13.59},{\"x\":1685299696048,\"y\":13.56},{\"x\":1685299996049,\"y\":13.56},{\"x\":1685300296111,\"y\":13.8},{\"x\":1685300596061,\"y\":13.71},{\"x\":1685300896068,\"y\":13.71},{\"x\":1685301196056,\"y\":13.49},{\"x\":1685301496064,\"y\":13.49},{\"x\":1685301796076,\"y\":13.49},{\"x\":1685302096103,\"y\":13.4},{\"x\":1685302396063,\"y\":13.46},{\"x\":1685302696077,\"y\":13.34},{\"x\":1685302996145,\"y\":13.04},{\"x\":1685303296067,\"y\":12.95},{\"x\":1685303596073,\"y\":13.04},{\"x\":1685303896072,\"y\":12.69},{\"x\":1685304196079,\"y\":12.67},{\"x\":1685304496103,\"y\":12.49},{\"x\":1685304796082,\"y\":12.49},{\"x\":1685305096071,\"y\":12.49},{\"x\":1685305396094,\"y\":12.49},{\"x\":1685305696124,\"y\":12.2},{\"x\":1685305996075,\"y\":12.14},{\"x\":1685306296121,\"y\":11.8},{\"x\":1685306596072,\"y\":11.8},{\"x\":1685306896081,\"y\":11.8},{\"x\":1685307196124,\"y\":11.34},{\"x\":1685307496091,\"y\":11.34},{\"x\":1685307797175,\"y\":11.28},{\"x\":1685308096131,\"y\":11.3},{\"x\":1685308396095,\"y\":11.28},{\"x\":1685308696095,\"y\":11.3},{\"x\":1685308996098,\"y\":11.22},{\"x\":1685309296119,\"y\":11},{\"x\":1685309596104,\"y\":11},{\"x\":1685309896135,\"y\":10.5},{\"x\":1685310196123,\"y\":10.5},{\"x\":1685310496134,\"y\":10.5},{\"x\":1685310796104,\"y\":10.45},{\"x\":1685311096115,\"y\":10.45},{\"x\":1685311396137,\"y\":10.24},{\"x\":1685311696121,\"y\":10.24},{\"x\":1685311996202,\"y\":10.21},{\"x\":1685312296125,\"y\":10.21},{\"x\":1685312596146,\"y\":10.21},{\"x\":1685312896148,\"y\":9.93},{\"x\":1685313196132,\"y\":10.07},{\"x\":1685313496203,\"y\":9.89},{\"x\":1685313796150,\"y\":9.83},{\"x\":1685314096147,\"y\":9.75},{\"x\":1685314396195,\"y\":9.81},{\"x\":1685314696202,\"y\":9.81},{\"x\":1685314996169,\"y\":9.68},{\"x\":1685315296183,\"y\":9.65},{\"x\":1685315596155,\"y\":9.37},{\"x\":1685315896207,\"y\":9.43},{\"x\":1685316196167,\"y\":9.43},{\"x\":1685316496181,\"y\":9.32},{\"x\":1685316796216,\"y\":9.22},{\"x\":1685317096164,\"y\":9.22},{\"x\":1685317396213,\"y\":8.84},{\"x\":1685317696170,\"y\":8.76},{\"x\":1685317996185,\"y\":8.81},{\"x\":1685318296172,\"y\":8.72},{\"x\":1685318596191,\"y\":8.81},{\"x\":1685318896205,\"y\":8.99},{\"x\":1685319196165,\"y\":8.99},{\"x\":1685319496187,\"y\":8.9},{\"x\":1685319796169,\"y\":8.9},{\"x\":1685320096204,\"y\":8.99},{\"x\":1685320396170,\"y\":8.7},{\"x\":1685320696174,\"y\":8.65},{\"x\":1685320996175,\"y\":8.65},{\"x\":1685321296205,\"y\":8.41},{\"x\":1685321596177,\"y\":8.41},{\"x\":1685321896203,\"y\":8.41},{\"x\":1685322196246,\"y\":8.09},{\"x\":1685322496225,\"y\":7.88},{\"x\":1685322796181,\"y\":7.88},{\"x\":1685323096176,\"y\":7.73},{\"x\":1685323396175,\"y\":7.73},{\"x\":1685323696184,\"y\":7.67},{\"x\":1685323996186,\"y\":7.67},{\"x\":1685324296221,\"y\":7.83},{\"x\":1685324596190,\"y\":7.74},{\"x\":1685324896222,\"y\":7.83},{\"x\":1685325196203,\"y\":7.74},{\"x\":1685325496188,\"y\":7.32},{\"x\":1685325796536,\"y\":7.32},{\"x\":1685326096185,\"y\":7.32},{\"x\":1685326396231,\"y\":7.41},{\"x\":1685326696191,\"y\":7.41},{\"x\":1685326996194,\"y\":7.41},{\"x\":1685327296195,\"y\":7.29},{\"x\":1685327596190,\"y\":7.3},{\"x\":1685327896227,\"y\":7.33},{\"x\":1685328196279,\"y\":7.33},{\"x\":1685328496276,\"y\":7.33},{\"x\":1685328796240,\"y\":7.2},{\"x\":1685329096191,\"y\":7.2},{\"x\":1685329396192,\"y\":7.11},{\"x\":1685329696248,\"y\":7.2},{\"x\":1685329996247,\"y\":7.2},{\"x\":1685330296206,\"y\":7.2},{\"x\":1685330596222,\"y\":7.11},{\"x\":1685330896241,\"y\":7.37},{\"x\":1685331196223,\"y\":7.37},{\"x\":1685331496227,\"y\":7.37},{\"x\":1685331796287,\"y\":7.54},{\"x\":1685332096241,\"y\":7.54},{\"x\":1685332396262,\"y\":7.25},{\"x\":1685332696288,\"y\":7.34},{\"x\":1685332996282,\"y\":7.25},{\"x\":1685333296250,\"y\":7.34},{\"x\":1685333596314,\"y\":7.3},{\"x\":1685333896248,\"y\":7.3},{\"x\":1685334196250,\"y\":7.22},{\"x\":1685334496287,\"y\":7.3},{\"x\":1685334796262,\"y\":7.3},{\"x\":1685335096310,\"y\":7.34},{\"x\":1685335396322,\"y\":7.34},{\"x\":1685335696328,\"y\":7.35},{\"x\":1685335996277,\"y\":7.35},{\"x\":1685336296341,\"y\":7.46},{\"x\":1685336596305,\"y\":7.46},{\"x\":1685336896288,\"y\":7.46},{\"x\":1685337196337,\"y\":7.77},{\"x\":1685337496307,\"y\":7.68},{\"x\":1685337796344,\"y\":7.77},{\"x\":1685338096310,\"y\":7.7},{\"x\":1685338396309,\"y\":7.7},{\"x\":1685338696313,\"y\":7.87},{\"x\":1685338996338,\"y\":7.91},{\"x\":1685339296305,\"y\":8.24},{\"x\":1685339596306,\"y\":8.24},{\"x\":1685339896363,\"y\":8.87},{\"x\":1685340196325,\"y\":8.81},{\"x\":1685340496328,\"y\":8.87},{\"x\":1685340796324,\"y\":8.93},{\"x\":1685341096323,\"y\":8.93},{\"x\":1685341396403,\"y\":9.54},{\"x\":1685341696331,\"y\":9.46},{\"x\":1685341996329,\"y\":9.48},{\"x\":1685342296334,\"y\":9.53},{\"x\":1685342596385,\"y\":9.94},{\"x\":1685342896330,\"y\":9.94},{\"x\":1685343196338,\"y\":10.15},{\"x\":1685343496342,\"y\":10.74},{\"x\":1685343796354,\"y\":10.74},{\"x\":1685344096339,\"y\":11.17},{\"x\":1685344396346,\"y\":11.17},{\"x\":1685344697407,\"y\":11.29},{\"x\":1685344996353,\"y\":11.26},{\"x\":1685345296424,\"y\":11.74},{\"x\":1685345596406,\"y\":11.65},{\"x\":1685345896351,\"y\":11.65},{\"x\":1685346196416,\"y\":12.07},{\"x\":1685346496410,\"y\":12.16},{\"x\":1685346796378,\"y\":12.07},{\"x\":1685347096406,\"y\":12.03},{\"x\":1685347396359,\"y\":12.03},{\"x\":1685347696378,\"y\":12.03},{\"x\":1685347996394,\"y\":12.1},{\"x\":1685348296366,\"y\":12.1},{\"x\":1685348596360,\"y\":12.02},{\"x\":1685348896369,\"y\":12.44},{\"x\":1685349196480,\"y\":12.49},{\"x\":1685349496365,\"y\":12.49},{\"x\":1685349796376,\"y\":12.49},{\"x\":1685350096394,\"y\":12.97},{\"x\":1685350396369,\"y\":12.97},{\"x\":1685350696451,\"y\":13.05},{\"x\":1685350996421,\"y\":13.19},{\"x\":1685351296370,\"y\":13.05},{\"x\":1685351596436,\"y\":13.25},{\"x\":1685351896375,\"y\":13.25},{\"x\":1685352196381,\"y\":13.25},{\"x\":1685352496379,\"y\":13.28},{\"x\":1685352796390,\"y\":13.28},{\"x\":1685353096503,\"y\":13.97},{\"x\":1685353396412,\"y\":13.97},{\"x\":1685353696392,\"y\":13.97},{\"x\":1685353996429,\"y\":14.15},{\"x\":1685354296437,\"y\":14.15},{\"x\":1685354596450,\"y\":14.15},{\"x\":1685354896393,\"y\":14.3},{\"x\":1685355196401,\"y\":14.3},{\"x\":1685355496440,\"y\":14.36},{\"x\":1685355796402,\"y\":14.36},{\"x\":1685356096449,\"y\":14.32},{\"x\":1685356396391,\"y\":14.24},{\"x\":1685356696401,\"y\":14.32},{\"x\":1685356996401,\"y\":14.61},{\"x\":1685357296408,\"y\":14.61},{\"x\":1685357596403,\"y\":15.01},{\"x\":1685357896429,\"y\":15.59},{\"x\":1685358196393,\"y\":15.59},{\"x\":1685358496468,\"y\":15.59},{\"x\":1685358796424,\"y\":16},{\"x\":1685359096400,\"y\":16},{\"x\":1685359396448,\"y\":16.11},{\"x\":1685359696410,\"y\":16.08},{\"x\":1685359996404,\"y\":16.08},{\"x\":1685360296411,\"y\":16.35},{\"x\":1685360596442,\"y\":16.35},{\"x\":1685360896460,\"y\":16.44},{\"x\":1685361196442,\"y\":16.44},{\"x\":1685361496409,\"y\":16.44},{\"x\":1685361796427,\"y\":16.52},{\"x\":1685362096416,\"y\":16.62},{\"x\":1685362396413,\"y\":16.67},{\"x\":1685362696411,\"y\":16.68},{\"x\":1685362996461,\"y\":16.79},{\"x\":1685363296411,\"y\":16.85},{\"x\":1685363596420,\"y\":16.79},{\"x\":1685363896467,\"y\":16.76},{\"x\":1685364196435,\"y\":16.76},{\"x\":1685364496434,\"y\":16.76},{\"x\":1685364796430,\"y\":16.68},{\"x\":1685365096423,\"y\":17.1},{\"x\":1685365396432,\"y\":17.1},{\"x\":1685365696497,\"y\":17.46},{\"x\":1685365996459,\"y\":17.44},{\"x\":1685366296423,\"y\":17.46},{\"x\":1685366596445,\"y\":17.44},{\"x\":1685366896437,\"y\":17.38},{\"x\":1685367196433,\"y\":17.38},{\"x\":1685367496432,\"y\":17.38},{\"x\":1685367796435,\"y\":17.27},{\"x\":1685368096426,\"y\":17.27},{\"x\":1685368396429,\"y\":17.26},{\"x\":1685368696436,\"y\":17.26},{\"x\":1685368996438,\"y\":17.44},{\"x\":1685369296428,\"y\":17.32},{\"x\":1685369596501,\"y\":17.5},{\"x\":1685369896441,\"y\":17.42},{\"x\":1685370196509,\"y\":17.5},{\"x\":1685370496485,\"y\":17.7},{\"x\":1685370796502,\"y\":17.7},{\"x\":1685371096455,\"y\":17.7},{\"x\":1685371396510,\"y\":17.39},{\"x\":1685371696485,\"y\":17.39},{\"x\":1685371996502,\"y\":17.64},{\"x\":1685372296476,\"y\":17.31},{\"x\":1685372596474,\"y\":17.78},{\"x\":1685372896482,\"y\":17.78},{\"x\":1685373196484,\"y\":17.78},{\"x\":1685373496572,\"y\":17.86},{\"x\":1685373796514,\"y\":17.78},{\"x\":1685374096510,\"y\":17.81},{\"x\":1685374396544,\"y\":17.86},{\"x\":1685374696519,\"y\":17.78},{\"x\":1685374996566,\"y\":17.78},{\"x\":1685375296542,\"y\":17.89},{\"x\":1685375596519,\"y\":17.89},{\"x\":1685375896531,\"y\":17.89},{\"x\":1685376196570,\"y\":17.89},{\"x\":1685376496581,\"y\":18.04},{\"x\":1685376796589,\"y\":18.04},{\"x\":1685377096547,\"y\":17.74},{\"x\":1685377396613,\"y\":17.74},{\"x\":1685377696596,\"y\":17.6},{\"x\":1685377996542,\"y\":17.51},{\"x\":1685378296565,\"y\":17.51},{\"x\":1685378596624,\"y\":17.51},{\"x\":1685378896627,\"y\":17.42},{\"x\":1685379196643,\"y\":17.2},{\"x\":1685379496586,\"y\":17.42},{\"x\":1685379796637,\"y\":17.09},{\"x\":1685380096596,\"y\":17.09},{\"x\":1685380396635,\"y\":17.11},{\"x\":1685380696607,\"y\":17.04},{\"x\":1685380996713,\"y\":17.08},{\"x\":1685381296598,\"y\":16.99},{\"x\":1685381596615,\"y\":17.08},{\"x\":1685381896608,\"y\":17.03},{\"x\":1685382196647,\"y\":17.03},{\"x\":1685382496621,\"y\":16.09},{\"x\":1685382796622,\"y\":16.25},{\"x\":1685383096636,\"y\":16.25},{\"x\":1685383396656,\"y\":16.25},{\"x\":1685383696677,\"y\":16.1},{\"x\":1685383996640,\"y\":16.09},{\"x\":1685384296634,\"y\":16.09},{\"x\":1685384596702,\"y\":16.09},{\"x\":1685384896755,\"y\":15.72},{\"x\":1685385196657,\"y\":15.7},{\"x\":1685385496648,\"y\":15.72},{\"x\":1685385796721,\"y\":14.94},{\"x\":1685386096661,\"y\":14.89},{\"x\":1685386396764,\"y\":14.48},{\"x\":1685386696644,\"y\":14.48},{\"x\":1685386996711,\"y\":14.39},{\"x\":1685387296665,\"y\":14.47},{\"x\":1685387596687,\"y\":14.47},{\"x\":1685387896719,\"y\":14.45},{\"x\":1685388196746,\"y\":14.37},{\"x\":1685388496677,\"y\":14.36},{\"x\":1685388796728,\"y\":14.16},{\"x\":1685389096728,\"y\":14.95},{\"x\":1685389396772,\"y\":15.89},{\"x\":1685389696700,\"y\":15.8},{\"x\":1685389996694,\"y\":15.8},{\"x\":1685390296741,\"y\":13.52},{\"x\":1685390596769,\"y\":13.52},{\"x\":1685390896706,\"y\":13.52},{\"x\":1685391196708,\"y\":12.38},{\"x\":1685391496778,\"y\":12.52},{\"x\":1685391796848,\"y\":12.52},{\"x\":1685392096805,\"y\":12.37},{\"x\":1685392396723,\"y\":12.01},{\"x\":1685392696723,\"y\":12.01},{\"x\":1685392996799,\"y\":11.33},{\"x\":1685393296731,\"y\":11.58},{\"x\":1685393596725,\"y\":11.33},{\"x\":1685393896759,\"y\":11.64},{\"x\":1685394196778,\"y\":11.64},{\"x\":1685394496727,\"y\":11.63},{\"x\":1685394796838,\"y\":11},{\"x\":1685395096733,\"y\":10.92},{\"x\":1685395396756,\"y\":10.84},{\"x\":1685395696744,\"y\":10.81},{\"x\":1685395996738,\"y\":10.81},{\"x\":1685396296745,\"y\":10.81},{\"x\":1685396596863,\"y\":9.92},{\"x\":1685396896748,\"y\":9.92},{\"x\":1685397196744,\"y\":9.92},{\"x\":1685397496752,\"y\":9.84},{\"x\":1685397797031,\"y\":9.29},{\"x\":1685398096745,\"y\":9.59},{\"x\":1685398396778,\"y\":9.29},{\"x\":1685398696753,\"y\":9.67},{\"x\":1685398996882,\"y\":9.8},{\"x\":1685399296749,\"y\":9.67},{\"x\":1685399596872,\"y\":9.64},{\"x\":1685399896856,\"y\":9.29},{\"x\":1685400196853,\"y\":9.29},{\"x\":1685400496758,\"y\":9.29},{\"x\":1685400796806,\"y\":8.88},{\"x\":1685401096749,\"y\":8.88},{\"x\":1685401396853,\"y\":8.88},{\"x\":1685401696823,\"y\":8.87},{\"x\":1685401996749,\"y\":8.93},{\"x\":1685402296834,\"y\":8.83},{\"x\":1685402596862,\"y\":8.83},{\"x\":1685402896766,\"y\":8.83},{\"x\":1685403196764,\"y\":8.69},{\"x\":1685403496778,\"y\":8.79},{\"x\":1685403796902,\"y\":8.76},{\"x\":1685404096775,\"y\":8.76},{\"x\":1685404396829,\"y\":8.45},{\"x\":1685404696790,\"y\":8.45},{\"x\":1685404996816,\"y\":8.36},{\"x\":1685405296883,\"y\":8.71},{\"x\":1685405596870,\"y\":8.7},{\"x\":1685405896793,\"y\":8.63},{\"x\":1685406196983,\"y\":8.7},{\"x\":1685406496794,\"y\":8.47},{\"x\":1685406796799,\"y\":8.47},{\"x\":1685407096806,\"y\":8.41},{\"x\":1685407396889,\"y\":8.27},{\"x\":1685407696814,\"y\":8.19},{\"x\":1685407996818,\"y\":8.27},{\"x\":1685408296823,\"y\":8.19},{\"x\":1685408596913,\"y\":8.19},{\"x\":1685408896867,\"y\":7.82},{\"x\":1685409196827,\"y\":7.82},{\"x\":1685409496885,\"y\":7.78},{\"x\":1685409796923,\"y\":7.69},{\"x\":1685410096833,\"y\":7.69},{\"x\":1685410396863,\"y\":7.68},{\"x\":1685410696853,\"y\":7.69},{\"x\":1685410996931,\"y\":7.59},{\"x\":1685411296850,\"y\":7.59},{\"x\":1685411596860,\"y\":7.59},{\"x\":1685411896863,\"y\":7.34},{\"x\":1685412197065,\"y\":7.41},{\"x\":1685412496865,\"y\":7.34},{\"x\":1685412796871,\"y\":7.34},{\"x\":1685413096914,\"y\":7.41},{\"x\":1685413396986,\"y\":7.41},{\"x\":1685413696881,\"y\":7.41},{\"x\":1685413996886,\"y\":7.35},{\"x\":1685414296923,\"y\":7.82},{\"x\":1685414597013,\"y\":7.95},{\"x\":1685414896976,\"y\":7.95},{\"x\":1685415196895,\"y\":7.95},{\"x\":1685415496889,\"y\":7.95},{\"x\":1685415796967,\"y\":8.17},{\"x\":1685416096899,\"y\":8.17},{\"x\":1685416396952,\"y\":8.17},{\"x\":1685416696900,\"y\":8.09},{\"x\":1685416997056,\"y\":8.23},{\"x\":1685417296914,\"y\":8.21},{\"x\":1685417596918,\"y\":8.23},{\"x\":1685417896941,\"y\":8.11},{\"x\":1685418197072,\"y\":8.11},{\"x\":1685418496972,\"y\":7.78},{\"x\":1685418796976,\"y\":7.78},{\"x\":1685419096917,\"y\":7.78},{\"x\":1685419397122,\"y\":7.76},{\"x\":1685419696925,\"y\":7.7},{\"x\":1685419996981,\"y\":7.76},{\"x\":1685420296927,\"y\":7.98},{\"x\":1685420597159,\"y\":7.68},{\"x\":1685420896981,\"y\":8.25},{\"x\":1685421196938,\"y\":8.25},{\"x\":1685421496968,\"y\":9.25},{\"x\":1685421797058,\"y\":8.95},{\"x\":1685422096935,\"y\":8.95},{\"x\":1685422397004,\"y\":9.27},{\"x\":1685422696956,\"y\":9.27},{\"x\":1685422997188,\"y\":9.29},{\"x\":1685423296955,\"y\":9.29},{\"x\":1685423597001,\"y\":9.11},{\"x\":1685423896956,\"y\":9.02},{\"x\":1685424197012,\"y\":9.02},{\"x\":1685424497000,\"y\":9.11},{\"x\":1685424796971,\"y\":9.02},{\"x\":1685425096964,\"y\":10.04},{\"x\":1685425397109,\"y\":10.04},{\"x\":1685425696964,\"y\":10.02},{\"x\":1685425996974,\"y\":9.99},{\"x\":1685426296965,\"y\":10.2},{\"x\":1685426597100,\"y\":10.28},{\"x\":1685426896989,\"y\":10.33},{\"x\":1685427196988,\"y\":10.33},{\"x\":1685427497031,\"y\":10.45},{\"x\":1685427797156,\"y\":10.38},{\"x\":1685428097083,\"y\":10.94},{\"x\":1685428396991,\"y\":10.94},{\"x\":1685428696996,\"y\":10.82},{\"x\":1685428997105,\"y\":11.01},{\"x\":1685429296992,\"y\":11.01},{\"x\":1685429597030,\"y\":11.01},{\"x\":1685429896994,\"y\":11.04},{\"x\":1685430197076,\"y\":11.04},{\"x\":1685430497010,\"y\":11.39},{\"x\":1685430796999,\"y\":11.6},{\"x\":1685431097007,\"y\":11.55},{\"x\":1685431397249,\"y\":11.81},{\"x\":1685431697002,\"y\":11.81},{\"x\":1685431997013,\"y\":11.81},{\"x\":1685432297009,\"y\":11.82},{\"x\":1685432597030,\"y\":11.82},{\"x\":1685432897132,\"y\":12.15},{\"x\":1685433197016,\"y\":12.07},{\"x\":1685433497009,\"y\":12.15},{\"x\":1685433797139,\"y\":12.16},{\"x\":1685434097016,\"y\":12.19},{\"x\":1685434397064,\"y\":12.22},{\"x\":1685434697027,\"y\":12.14},{\"x\":1685434997238,\"y\":12.14},{\"x\":1685435297077,\"y\":12.32},{\"x\":1685435597014,\"y\":12.33},{\"x\":1685435897071,\"y\":12.41},{\"x\":1685436197195,\"y\":12.35},{\"x\":1685436497013,\"y\":12.35},{\"x\":1685436797015,\"y\":12.32},{\"x\":1685437097050,\"y\":12.32},{\"x\":1685437397237,\"y\":12.33},{\"x\":1685437697030,\"y\":12.33},{\"x\":1685437997069,\"y\":12.27},{\"x\":1685438297034,\"y\":12.19},{\"x\":1685438597203,\"y\":12.17},{\"x\":1685438897033,\"y\":12.34},{\"x\":1685439197064,\"y\":12.31},{\"x\":1685439497033,\"y\":12.32},{\"x\":1685439797107,\"y\":12.31},{\"x\":1685440097051,\"y\":13.11},{\"x\":1685440397038,\"y\":13.21},{\"x\":1685440697073,\"y\":13.37},{\"x\":1685440997179,\"y\":13.19},{\"x\":1685441297048,\"y\":13.28},{\"x\":1685441597041,\"y\":13.19},{\"x\":1685441897039,\"y\":13.29},{\"x\":1685442197094,\"y\":13.29},{\"x\":1685442497030,\"y\":13.33},{\"x\":1685442797045,\"y\":13.33},{\"x\":1685443097030,\"y\":13.2},{\"x\":1685443397349,\"y\":13.31},{\"x\":1685443697027,\"y\":13.31},{\"x\":1685443997092,\"y\":13.23},{\"x\":1685444297033,\"y\":13.77},{\"x\":1685444597073,\"y\":13.68},{\"x\":1685444897078,\"y\":16.19},{\"x\":1685445197034,\"y\":16.19},{\"x\":1685445497068,\"y\":16.19},{\"x\":1685445797209,\"y\":16.19},{\"x\":1685446097058,\"y\":16.19},{\"x\":1685446397051,\"y\":17.22},{\"x\":1685446697059,\"y\":17.22},{\"x\":1685446997166,\"y\":18.41},{\"x\":1685447297115,\"y\":18.41},{\"x\":1685447597112,\"y\":18.41},{\"x\":1685447897089,\"y\":18.41},{\"x\":1685448197159,\"y\":12.74},{\"x\":1685448497098,\"y\":12.72},{\"x\":1685448797127,\"y\":12.89},{\"x\":1685449097105,\"y\":12.97},{\"x\":1685449397373,\"y\":13.15},{\"x\":1685449697109,\"y\":13.03},{\"x\":1685449997136,\"y\":13.06},{\"x\":1685450297128,\"y\":13.06},{\"x\":1685450597387,\"y\":13.06},{\"x\":1685450897183,\"y\":13.21},{\"x\":1685451197144,\"y\":13.21},{\"x\":1685451497142,\"y\":13.33},{\"x\":1685451797185,\"y\":13.33},{\"x\":1685452097148,\"y\":13.57},{\"x\":1685452397249,\"y\":13.64},{\"x\":1685452697161,\"y\":13.64},{\"x\":1685452997474,\"y\":13.64},{\"x\":1685453297206,\"y\":13.97},{\"x\":1685453597173,\"y\":13.69},{\"x\":1685453897183,\"y\":13.94},{\"x\":1685454197215,\"y\":13.94},{\"x\":1685454497192,\"y\":14.16},{\"x\":1685454797260,\"y\":14.48},{\"x\":1685455097202,\"y\":14.67},{\"x\":1685455397476,\"y\":14.67},{\"x\":1685455697209,\"y\":14.86},{\"x\":1685455997240,\"y\":15.03},{\"x\":1685456297262,\"y\":15.18},{\"x\":1685456597249,\"y\":15.18},{\"x\":1685456897228,\"y\":15.09},{\"x\":1685457197269,\"y\":15.55},{\"x\":1685457497236,\"y\":15.55},{\"x\":1685457797465,\"y\":15.55},{\"x\":1685458097286,\"y\":16.03},{\"x\":1685458397242,\"y\":16.03},{\"x\":1685458697267,\"y\":16.03},{\"x\":1685458997286,\"y\":15.84},{\"x\":1685459297248,\"y\":15.84},{\"x\":1685459597268,\"y\":16.22},{\"x\":1685459897263,\"y\":16.22},{\"x\":1685460197357,\"y\":16.94},{\"x\":1685460497301,\"y\":16.94},{\"x\":1685460797266,\"y\":16.94},{\"x\":1685461097305,\"y\":16.95},{\"x\":1685461397544,\"y\":17.05},{\"x\":1685461697280,\"y\":16.95},{\"x\":1685461997291,\"y\":16.97},{\"x\":1685462297287,\"y\":16.97},{\"x\":1685462597465,\"y\":17.35},{\"x\":1685462897303,\"y\":17.35},{\"x\":1685463197366,\"y\":17.2},{\"x\":1685463737229,\"y\":17.2},{\"x\":1685464037277,\"y\":17.14},{\"x\":1685464337245,\"y\":17.14},{\"x\":1685464637256,\"y\":17.03},{\"x\":1685464937262,\"y\":17.14},{\"x\":1685465237316,\"y\":17.08},{\"x\":1685465537294,\"y\":17.08},{\"x\":1685465837334,\"y\":17.31},{\"x\":1685466137337,\"y\":17.31},{\"x\":1685466437310,\"y\":17.03},{\"x\":1685466737354,\"y\":17.04},{\"x\":1685467037407,\"y\":16.96},{\"x\":1685467337326,\"y\":17.16},{\"x\":1685467637328,\"y\":17.13},{\"x\":1685467937333,\"y\":16.81},{\"x\":1685468237339,\"y\":16.48},{\"x\":1685468537360,\"y\":16.48},{\"x\":1685468837368,\"y\":16.47},{\"x\":1685469137413,\"y\":15.9},{\"x\":1685469437370,\"y\":16.04},{\"x\":1685469737372,\"y\":15.9},{\"x\":1685470037427,\"y\":15.82},{\"x\":1685470337392,\"y\":15.82},{\"x\":1685470637406,\"y\":15.82},{\"x\":1685470937407,\"y\":15.69},{\"x\":1685471237416,\"y\":15.83},{\"x\":1685471537421,\"y\":15.83},{\"x\":1685471837423,\"y\":15.95},{\"x\":1685472137555,\"y\":15.16},{\"x\":1685472437434,\"y\":15.08},{\"x\":1685472737439,\"y\":14.87},{\"x\":1685473037727,\"y\":14.79},{\"x\":1685473337523,\"y\":15.1},{\"x\":1685473637452,\"y\":14.79},{\"x\":1685473937500,\"y\":14.51},{\"x\":1685474237504,\"y\":14.29},{\"x\":1685474537449,\"y\":14.29},{\"x\":1685474837471,\"y\":14.21},{\"x\":1685475137479,\"y\":14.06},{\"x\":1685475437511,\"y\":13.27},{\"x\":1685475737497,\"y\":13.27},{\"x\":1685476037496,\"y\":13.19},{\"x\":1685476337572,\"y\":13.61},{\"x\":1685476637511,\"y\":13.49},{\"x\":1685476937513,\"y\":13.47},{\"x\":1685477237528,\"y\":13.42},{\"x\":1685477537526,\"y\":13.44},{\"x\":1685477837530,\"y\":13.44},{\"x\":1685478137582,\"y\":13.03},{\"x\":1685478437532,\"y\":13.14},{\"x\":1685478737565,\"y\":12.83},{\"x\":1685479037545,\"y\":12.91},{\"x\":1685479337555,\"y\":12.59},{\"x\":1685479637566,\"y\":12.56},{\"x\":1685479937595,\"y\":12.5},{\"x\":1685480237565,\"y\":12.61},{\"x\":1685480537635,\"y\":12.43},{\"x\":1685480837564,\"y\":12.43},{\"x\":1685481137578,\"y\":12.27},{\"x\":1685481437654,\"y\":12.32},{\"x\":1685481737583,\"y\":12.24},{\"x\":1685482037634,\"y\":12.13},{\"x\":1685482337596,\"y\":12.13},{\"x\":1685482637588,\"y\":12.04},{\"x\":1685482937605,\"y\":11.88},{\"x\":1685483237606,\"y\":11.88},{\"x\":1685483537617,\"y\":11.87},{\"x\":1685483837694,\"y\":11.87},{\"x\":1685484137652,\"y\":11.83},{\"x\":1685484437620,\"y\":11.55},{\"x\":1685484737629,\"y\":11.83},{\"x\":1685485037682,\"y\":11.43},{\"x\":1685485337673,\"y\":11.43},{\"x\":1685485637637,\"y\":11.43},{\"x\":1685485937698,\"y\":11.4},{\"x\":1685486237666,\"y\":11.4},{\"x\":1685486537695,\"y\":11.4},{\"x\":1685486837701,\"y\":11.34},{\"x\":1685487137670,\"y\":11.34},{\"x\":1685487437690,\"y\":11.34},{\"x\":1685487737711,\"y\":11.08},{\"x\":1685488037671,\"y\":11.08},{\"x\":1685488337728,\"y\":11.08},{\"x\":1685488637752,\"y\":11.06},{\"x\":1685488937679,\"y\":11.06},{\"x\":1685489237735,\"y\":11.04},{\"x\":1685489537691,\"y\":10.96},{\"x\":1685489837848,\"y\":11.05},{\"x\":1685490137701,\"y\":11.05},{\"x\":1685490437705,\"y\":10.95},{\"x\":1685490737730,\"y\":10.86},{\"x\":1685491037756,\"y\":10.8},{\"x\":1685491337708,\"y\":10.8},{\"x\":1685491637705,\"y\":10.8},{\"x\":1685491937711,\"y\":10.71},{\"x\":1685492237764,\"y\":10.8},{\"x\":1685492537769,\"y\":10.8},{\"x\":1685492837718,\"y\":10.8},{\"x\":1685493137753,\"y\":10.8},{\"x\":1685493437930,\"y\":10.87},{\"x\":1685493737724,\"y\":10.87},{\"x\":1685494037777,\"y\":10.61},{\"x\":1685494337747,\"y\":10.52},{\"x\":1685494637852,\"y\":10.61},{\"x\":1685494937737,\"y\":10.61},{\"x\":1685495237777,\"y\":10.63},{\"x\":1685495537775,\"y\":10.62},{\"x\":1685495837812,\"y\":10.51},{\"x\":1685496137755,\"y\":10.43},{\"x\":1685496437796,\"y\":10.49},{\"x\":1685496737809,\"y\":10.49},{\"x\":1685497037761,\"y\":10.49},{\"x\":1685497337761,\"y\":10.23},{\"x\":1685497637765,\"y\":10.23},{\"x\":1685497937814,\"y\":10.31},{\"x\":1685498237807,\"y\":10.31},{\"x\":1685498537788,\"y\":10.31},{\"x\":1685498837775,\"y\":10.23},{\"x\":1685499137789,\"y\":10.23},{\"x\":1685499437805,\"y\":10.23},{\"x\":1685499737817,\"y\":10.31},{\"x\":1685500037780,\"y\":10.31},{\"x\":1685500337844,\"y\":10.14},{\"x\":1685500637891,\"y\":10.14},{\"x\":1685500937793,\"y\":10.14},{\"x\":1685501237793,\"y\":10.14},{\"x\":1685501537807,\"y\":10.05},{\"x\":1685501837925,\"y\":10.05},{\"x\":1685502137801,\"y\":10.05},{\"x\":1685502437811,\"y\":9.97},{\"x\":1685502737895,\"y\":10.14},{\"x\":1685503037900,\"y\":10.16},{\"x\":1685503337808,\"y\":10.16},{\"x\":1685503637807,\"y\":10.08},{\"x\":1685503937812,\"y\":9.94},{\"x\":1685504237850,\"y\":9.94},{\"x\":1685504537822,\"y\":9.99},{\"x\":1685504837872,\"y\":9.96},{\"x\":1685505137866,\"y\":9.98},{\"x\":1685505438032,\"y\":9.98},{\"x\":1685505737834,\"y\":9.98},{\"x\":1685506037879,\"y\":9.81},{\"x\":1685506337861,\"y\":9.81},{\"x\":1685506637914,\"y\":9.81},{\"x\":1685506937872,\"y\":9.81},{\"x\":1685507237889,\"y\":9.81},{\"x\":1685507537838,\"y\":9.81},{\"x\":1685507837888,\"y\":9.81},{\"x\":1685508137887,\"y\":9.82},{\"x\":1685508437832,\"y\":9.73},{\"x\":1685508737867,\"y\":9.82},{\"x\":1685509037974,\"y\":9.62},{\"x\":1685509337857,\"y\":9.82},{\"x\":1685509637848,\"y\":9.62},{\"x\":1685509937889,\"y\":9.62},{\"x\":1685510237947,\"y\":9.62},{\"x\":1685510537889,\"y\":9.65},{\"x\":1685510837852,\"y\":9.65},{\"x\":1685511137855,\"y\":9.65},{\"x\":1685511437965,\"y\":9.57},{\"x\":1685511737859,\"y\":9.57},{\"x\":1685512037848,\"y\":9.54},{\"x\":1685512337852,\"y\":9.55},{\"x\":1685512638025,\"y\":9.58},{\"x\":1685512937857,\"y\":9.57},{\"x\":1685513237850,\"y\":9.58},{\"x\":1685513537848,\"y\":9.57},{\"x\":1685513837893,\"y\":9.57},{\"x\":1685514137857,\"y\":9.75},{\"x\":1685514437855,\"y\":9.75},{\"x\":1685514737977,\"y\":9.84},{\"x\":1685515037889,\"y\":9.84},{\"x\":1685515337874,\"y\":9.84},{\"x\":1685515637865,\"y\":9.84},{\"x\":1685515937961,\"y\":9.84},{\"x\":1685516238059,\"y\":9.84},{\"x\":1685516537872,\"y\":9.75},{\"x\":1685516837969,\"y\":10.01},{\"x\":1685517137902,\"y\":10.01},{\"x\":1685517437997,\"y\":10.01},{\"x\":1685517737887,\"y\":10.03},{\"x\":1685518037974,\"y\":10.03},{\"x\":1685518337895,\"y\":9.92},{\"x\":1685518637932,\"y\":9.92},{\"x\":1685518937904,\"y\":9.96},{\"x\":1685519238002,\"y\":10.1},{\"x\":1685519537959,\"y\":10.1},{\"x\":1685519837988,\"y\":10.1},{\"x\":1685520137912,\"y\":10.1},{\"x\":1685520437906,\"y\":10.37},{\"x\":1685520737913,\"y\":10.33},{\"x\":1685521038006,\"y\":10.33},{\"x\":1685521337908,\"y\":10.33},{\"x\":1685521637915,\"y\":10.33},{\"x\":1685521937963,\"y\":10.25},{\"x\":1685522238080,\"y\":10.54},{\"x\":1685522537924,\"y\":10.54},{\"x\":1685522837919,\"y\":10.54},{\"x\":1685523137944,\"y\":10.5},{\"x\":1685523438045,\"y\":10.6},{\"x\":1685523737930,\"y\":10.58},{\"x\":1685524037929,\"y\":10.52},{\"x\":1685524337974,\"y\":10.82},{\"x\":1685524638007,\"y\":10.82},{\"x\":1685524937934,\"y\":10.82},{\"x\":1685525237935,\"y\":10.95},{\"x\":1685525537943,\"y\":10.99},{\"x\":1685525838078,\"y\":11.08},{\"x\":1685526137951,\"y\":11.08},{\"x\":1685526437950,\"y\":10.99},{\"x\":1685526737944,\"y\":11.07},{\"x\":1685527038721,\"y\":11.07},{\"x\":1685527338012,\"y\":11.11},{\"x\":1685527637961,\"y\":11.11},{\"x\":1685527937952,\"y\":11.07},{\"x\":1685528238080,\"y\":11.16},{\"x\":1685528537978,\"y\":11.03},{\"x\":1685528837960,\"y\":11.12},{\"x\":1685529137967,\"y\":11.08},{\"x\":1685529438131,\"y\":11.61},{\"x\":1685529738016,\"y\":11.61},{\"x\":1685530037967,\"y\":11.61},{\"x\":1685530337963,\"y\":11.61},{\"x\":1685530638067,\"y\":11.61},{\"x\":1685530937995,\"y\":11.61},{\"x\":1685531237967,\"y\":11.61},{\"x\":1685531537979,\"y\":12.07},{\"x\":1685531838029,\"y\":11.73},{\"x\":1685532137972,\"y\":11.72},{\"x\":1685532437974,\"y\":11.73},{\"x\":1685532738070,\"y\":11.43},{\"x\":1685533038022,\"y\":11.92},{\"x\":1685533338038,\"y\":16.07},{\"x\":1685533637982,\"y\":16.07},{\"x\":1685533938045,\"y\":16.07},{\"x\":1685534238057,\"y\":16.07},{\"x\":1685534537980,\"y\":16.07},{\"x\":1685534838036,\"y\":12.17},{\"x\":1685535137993,\"y\":12.17},{\"x\":1685535438136,\"y\":12.17},{\"x\":1685535737984,\"y\":12.17},{\"x\":1685536037986,\"y\":12.17},{\"x\":1685536337985,\"y\":12.17},{\"x\":1685536638024,\"y\":11.9},{\"x\":1685536938025,\"y\":15.91},{\"x\":1685537237999,\"y\":15.91},{\"x\":1685537538002,\"y\":15.91},{\"x\":1685537838106,\"y\":15.91},{\"x\":1685538138007,\"y\":15.82},{\"x\":1685538438058,\"y\":13.28},{\"x\":1685538738048,\"y\":13.28},{\"x\":1685539038104,\"y\":13.28},{\"x\":1685539338007,\"y\":13.2},{\"x\":1685539638019,\"y\":13.2},{\"x\":1685539938003,\"y\":13.33},{\"x\":1685540238052,\"y\":13.2},{\"x\":1685540538039,\"y\":16.75},{\"x\":1685540838017,\"y\":16.67},{\"x\":1685541138064,\"y\":16.75},{\"x\":1685541438054,\"y\":16.67},{\"x\":1685541738229,\"y\":17.3},{\"x\":1685542038012,\"y\":17.22},{\"x\":1685542338042,\"y\":17.3},{\"x\":1685542638126,\"y\":18.38},{\"x\":1685542938012,\"y\":18.38},{\"x\":1685543238056,\"y\":18.97},{\"x\":1685543538110,\"y\":17.86},{\"x\":1685543838052,\"y\":17.86},{\"x\":1685544138125,\"y\":17.78},{\"x\":1685544438027,\"y\":17.78},{\"x\":1685544738110,\"y\":14.96},{\"x\":1685545038075,\"y\":14.94},{\"x\":1685545338027,\"y\":14.94},{\"x\":1685545638075,\"y\":15.63},{\"x\":1685545938022,\"y\":15.63},{\"x\":1685546238174,\"y\":16},{\"x\":1685546538079,\"y\":15.83},{\"x\":1685546838020,\"y\":15.81},{\"x\":1685547138129,\"y\":14.9},{\"x\":1685547438095,\"y\":14.83},{\"x\":1685547738125,\"y\":14.9},{\"x\":1685548038046,\"y\":14.83},{\"x\":1685548338056,\"y\":14.9},{\"x\":1685548638087,\"y\":14.85},{\"x\":1685548938038,\"y\":14.85},{\"x\":1685549238040,\"y\":14.85},{\"x\":1685549538033,\"y\":14.97},{\"x\":1685549838168,\"y\":14.97},{\"x\":1685550138087,\"y\":15.03},{\"x\":1685550438030,\"y\":14.97},{\"x\":1685550738120,\"y\":14.3},{\"x\":1685551038139,\"y\":14.3},{\"x\":1685551338037,\"y\":13.6},{\"x\":1685551638033,\"y\":13.49},{\"x\":1685551938041,\"y\":13.47},{\"x\":1685552238174,\"y\":13.61},{\"x\":1685552538043,\"y\":13.61},{\"x\":1685552838043,\"y\":13.52},{\"x\":1685553138067,\"y\":13.52},{\"x\":1685553438546,\"y\":13.6},{\"x\":1685553738055,\"y\":13.52},{\"x\":1685554038140,\"y\":13.59},{\"x\":1685554338045,\"y\":13.51},{\"x\":1685554638134,\"y\":13.16},{\"x\":1685554938070,\"y\":13.16},{\"x\":1685555238057,\"y\":13.18},{\"x\":1685555538064,\"y\":13.07},{\"x\":1685555838093,\"y\":13.07},{\"x\":1685556138085,\"y\":13.14},{\"x\":1685556438188,\"y\":13.11},{\"x\":1685556738092,\"y\":13.12},{\"x\":1685557038192,\"y\":13.01},{\"x\":1685557338095,\"y\":13.01},{\"x\":1685557638182,\"y\":12.89},{\"x\":1685557938150,\"y\":12.8},{\"x\":1685558238172,\"y\":12.61},{\"x\":1685558538110,\"y\":12.8},{\"x\":1685558838201,\"y\":12.58},{\"x\":1685559138123,\"y\":12.58},{\"x\":1685559438206,\"y\":12.59},{\"x\":1685559738189,\"y\":12.57},{\"x\":1685560038129,\"y\":12.57},{\"x\":1685560338155,\"y\":12.57},{\"x\":1685560638404,\"y\":12.29},{\"x\":1685560938173,\"y\":12.29},{\"x\":1685561238169,\"y\":12.29},{\"x\":1685561538183,\"y\":12.17},{\"x\":1685561838237,\"y\":12.17},{\"x\":1685562138233,\"y\":12.21},{\"x\":1685562438249,\"y\":12.18},{\"x\":1685562738211,\"y\":12.18},{\"x\":1685563038228,\"y\":12.09},{\"x\":1685563338214,\"y\":12.07},{\"x\":1685563638217,\"y\":12.07},{\"x\":1685563938231,\"y\":11.8},{\"x\":1685564238362,\"y\":11.86},{\"x\":1685564538381,\"y\":11.83},{\"x\":1685564838277,\"y\":11.83},{\"x\":1685565138242,\"y\":11.79},{\"x\":1685565438280,\"y\":11.7},{\"x\":1685565738251,\"y\":11.53},{\"x\":1685566038259,\"y\":11.53},{\"x\":1685566338265,\"y\":11.53},{\"x\":1685566638303,\"y\":11.43},{\"x\":1685566938268,\"y\":11.43},{\"x\":1685567238323,\"y\":11.33},{\"x\":1685567538343,\"y\":11.39},{\"x\":1685567838304,\"y\":11.31},{\"x\":1685568138281,\"y\":11.39},{\"x\":1685568438336,\"y\":11.31},{\"x\":1685568738284,\"y\":11.31},{\"x\":1685569038329,\"y\":11.31},{\"x\":1685569338310,\"y\":11.25},{\"x\":1685569638302,\"y\":11.25},{\"x\":1685569938300,\"y\":11.25},{\"x\":1685570238298,\"y\":10.97},{\"x\":1685570538297,\"y\":10.97},{\"x\":1685570838294,\"y\":10.97},{\"x\":1685571138323,\"y\":10.78},{\"x\":1685571438384,\"y\":10.78},{\"x\":1685571738313,\"y\":10.78},{\"x\":1685572038419,\"y\":10.8},{\"x\":1685572338315,\"y\":10.8},{\"x\":1685572638324,\"y\":10.8},{\"x\":1685572938327,\"y\":10.44},{\"x\":1685573238329,\"y\":10.44},{\"x\":1685573538333,\"y\":10.44},{\"x\":1685573838369,\"y\":10.44},{\"x\":1685574138332,\"y\":10.44},{\"x\":1685574438328,\"y\":10.43},{\"x\":1685574738335,\"y\":10.43},{\"x\":1685575038372,\"y\":10.42},{\"x\":1685575338334,\"y\":10.43},{\"x\":1685575638376,\"y\":10.44},{\"x\":1685575938389,\"y\":10.48},{\"x\":1685576238419,\"y\":10.39},{\"x\":1685576538344,\"y\":10.11},{\"x\":1685576838359,\"y\":10.2},{\"x\":1685577138380,\"y\":10.29},{\"x\":1685577438375,\"y\":10.29},{\"x\":1685577738413,\"y\":10.3},{\"x\":1685578038361,\"y\":10.29},{\"x\":1685578338371,\"y\":10.3},{\"x\":1685578638503,\"y\":10.18},{\"x\":1685578938376,\"y\":10.18},{\"x\":1685579238383,\"y\":10.09},{\"x\":1685579538380,\"y\":10.17},{\"x\":1685579838438,\"y\":10.13},{\"x\":1685580138376,\"y\":9.88},{\"x\":1685580438398,\"y\":9.88},{\"x\":1685580738405,\"y\":9.94},{\"x\":1685581038616,\"y\":9.94},{\"x\":1685581338395,\"y\":9.94},{\"x\":1685581638435,\"y\":9.94},{\"x\":1685581938409,\"y\":9.88},{\"x\":1685582238480,\"y\":9.86},{\"x\":1685582538402,\"y\":9.86},{\"x\":1685582838405,\"y\":9.86},{\"x\":1685583138415,\"y\":9.86},{\"x\":1685583438613,\"y\":9.86},{\"x\":1685583738415,\"y\":9.88},{\"x\":1685584038410,\"y\":9.88},{\"x\":1685584338448,\"y\":9.79},{\"x\":1685584638454,\"y\":9.88},{\"x\":1685584938414,\"y\":9.88},{\"x\":1685585238420,\"y\":9.98},{\"x\":1685585538423,\"y\":9.89},{\"x\":1685585838440,\"y\":9.89},{\"x\":1685586138428,\"y\":9.84},{\"x\":1685586438423,\"y\":9.84},{\"x\":1685586738424,\"y\":9.79},{\"x\":1685587038648,\"y\":9.93},{\"x\":1685587338431,\"y\":9.79},{\"x\":1685587638450,\"y\":9.79},{\"x\":1685587938475,\"y\":9.88},{\"x\":1685588238559,\"y\":9.88},{\"x\":1685588538441,\"y\":9.88},{\"x\":1685588838584,\"y\":9.67},{\"x\":1685589138428,\"y\":9.58},{\"x\":1685589438536,\"y\":9.67},{\"x\":1685589738473,\"y\":9.67},{\"x\":1685590038433,\"y\":9.67},{\"x\":1685590338478,\"y\":9.67},{\"x\":1685590638761,\"y\":9.42},{\"x\":1685590938425,\"y\":9.42},{\"x\":1685591238442,\"y\":9.42},{\"x\":1685591538439,\"y\":9.31},{\"x\":1685591838504,\"y\":9.31},{\"x\":1685592138436,\"y\":9.34},{\"x\":1685592438435,\"y\":9.34},{\"x\":1685592738487,\"y\":9.38},{\"x\":1685593038723,\"y\":9.38},{\"x\":1685593338444,\"y\":9.38},{\"x\":1685593638424,\"y\":9.34},{\"x\":1685593938457,\"y\":9.34},{\"x\":1685594238655,\"y\":9.86},{\"x\":1685594538452,\"y\":9.86},{\"x\":1685594838449,\"y\":9.86},{\"x\":1685595138489,\"y\":9.84},{\"x\":1685595438547,\"y\":9.84},{\"x\":1685595738460,\"y\":9.78},{\"x\":1685596038492,\"y\":9.85},{\"x\":1685596338571,\"y\":9.85},{\"x\":1685596638545,\"y\":9.85},{\"x\":1685596938457,\"y\":9.76},{\"x\":1685597238477,\"y\":9.85},{\"x\":1685597538472,\"y\":9.8},{\"x\":1685597838494,\"y\":9.83},{\"x\":1685598138501,\"y\":9.86},{\"x\":1685598438509,\"y\":10.14},{\"x\":1685598738535,\"y\":10.14},{\"x\":1685599038819,\"y\":10.14},{\"x\":1685599338484,\"y\":10.07},{\"x\":1685599638529,\"y\":10.07},{\"x\":1685599938480,\"y\":10.14},{\"x\":1685600238698,\"y\":10.23},{\"x\":1685600538482,\"y\":10.1},{\"x\":1685600838527,\"y\":10.43},{\"x\":1685601138487,\"y\":10.43},{\"x\":1685601438740,\"y\":10.43},{\"x\":1685601738547,\"y\":10.45},{\"x\":1685602038498,\"y\":10.36},{\"x\":1685602338488,\"y\":10.43},{\"x\":1685602638682,\"y\":10.46},{\"x\":1685602938581,\"y\":10.46},{\"x\":1685603238508,\"y\":10.47},{\"x\":1685603538503,\"y\":10.47},{\"x\":1685603838572,\"y\":10.47},{\"x\":1685604138511,\"y\":10.47},{\"x\":1685604438579,\"y\":10.6},{\"x\":1685604738515,\"y\":10.51},{\"x\":1685605038560,\"y\":10.55},{\"x\":1685605338556,\"y\":11.07},{\"x\":1685605638563,\"y\":11.07},{\"x\":1685605938519,\"y\":11.07},{\"x\":1685606238594,\"y\":11.05},{\"x\":1685606538513,\"y\":11.12},{\"x\":1685606838518,\"y\":11.15},{\"x\":1685607138518,\"y\":11.15},{\"x\":1685607438879,\"y\":11.19},{\"x\":1685607738543,\"y\":11.19},{\"x\":1685608038530,\"y\":11.42},{\"x\":1685608338527,\"y\":11.33},{\"x\":1685608638628,\"y\":11.33},{\"x\":1685608938572,\"y\":11.54},{\"x\":1685609238526,\"y\":11.54},{\"x\":1685609538568,\"y\":11.54},{\"x\":1685609838674,\"y\":11.73},{\"x\":1685610138530,\"y\":11.54},{\"x\":1685610438525,\"y\":11.73},{\"x\":1685610738604,\"y\":11.81},{\"x\":1685611038666,\"y\":11.81},{\"x\":1685611338564,\"y\":11.72},{\"x\":1685611638584,\"y\":12.09},{\"x\":1685611938537,\"y\":12.09},{\"x\":1685612238596,\"y\":12.09},{\"x\":1685612538547,\"y\":12.11},{\"x\":1685612838540,\"y\":12.11},{\"x\":1685613138539,\"y\":12.07},{\"x\":1685613438767,\"y\":12.53},{\"x\":1685613738611,\"y\":12.64},{\"x\":1685614038551,\"y\":12.51},{\"x\":1685614338551,\"y\":12.64},{\"x\":1685614638741,\"y\":12.64},{\"x\":1685614938559,\"y\":12.64},{\"x\":1685615238587,\"y\":13.05},{\"x\":1685615538593,\"y\":13.15},{\"x\":1685615838836,\"y\":13.07},{\"x\":1685616138551,\"y\":13.07},{\"x\":1685616438549,\"y\":13.17},{\"x\":1685616738558,\"y\":13.26},{\"x\":1685617038760,\"y\":13.26},{\"x\":1685617338558,\"y\":13.36},{\"x\":1685617638534,\"y\":13.36},{\"x\":1685617938635,\"y\":13.73},{\"x\":1685618238566,\"y\":13.71},{\"x\":1685618538558,\"y\":13.71},{\"x\":1685618838593,\"y\":13.75},{\"x\":1685619138565,\"y\":13.75},{\"x\":1685619438673,\"y\":13.93},{\"x\":1685619738585,\"y\":13.93}]],\"labels\":[\"\"]}]","payloadType":"json","x":150,"y":180,"wires":[["3eb08d4843164efc"]]},{"id":"41e847ff22249c0e","type":"ui_group","name":"Temperature","tab":"1d985094b1a81b0c","order":5,"disp":true,"width":"24","collapse":false,"className":""},{"id":"1d985094b1a81b0c","type":"ui_tab","name":"Wide View","icon":"dashboard","disabled":false,"hidden":false}] ``` :: ### 3. Using sliders and persisting the current value Sliders are a really useful user-interface element. Where you need to control the speed of a piece of machinery, having the ability to use a slider rather than manually typing in a value is a much better fit for shop-floor HMIs. When using sliders in your dashboards, it's important to consider how you will persist the state of the slider. If you don't persist the state, you will find that a redeploy of your dashboard will set the slider back to the default value. That would also change the speed of your machine. :video{ariaLabel="An example of a slider in a HMI" autoPlay="true" height="164" loop="true" muted="true" playsInline="true" preload="none" width="540"} To retain the current value of the slider we can use Node-RED's context. Each time the slider value is updated, we store the value in context. Each time we deploy the flow, we can now load the value back from context. If you'd like to view this slider and the flow which makes it work on your own Node-RED, you can import the flow below. ::render-flow{:height='300'} ```json [{"id":"05340e7a133098a6","type":"ui_slider","z":"0e6be5088cecccc1","name":"","label":"{{msg.payload}}","tooltip":"","group":"41e847ff22249c0e","order":1,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":0,"max":"20","step":1,"className":"","x":230,"y":300,"wires":[["945db61c94fc0704","ad7819da2cfbcd4e"]]},{"id":"849d76d146ff8c12","type":"inject","z":"0e6be5088cecccc1","name":"Inject on deploy","props":[],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":160,"y":240,"wires":[["aeedb3c60965c1af"]]},{"id":"945db61c94fc0704","type":"change","z":"0e6be5088cecccc1","name":"Set global-slider-value = msg.payload","rules":[{"t":"set","p":"slider-value","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":300,"wires":[[]]},{"id":"aeedb3c60965c1af","type":"change","z":"0e6be5088cecccc1","name":"Set msg.payload = global.slider-value","rules":[{"t":"set","p":"payload","pt":"msg","to":"slider-value","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":240,"wires":[["05340e7a133098a6"]]},{"id":"ad7819da2cfbcd4e","type":"ui_text","z":"0e6be5088cecccc1","group":"41e847ff22249c0e","order":2,"width":0,"height":0,"name":"","label":"","format":"The current value is {{msg.payload}} meters per minute","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":350,"y":340,"wires":[]},{"id":"41e847ff22249c0e","type":"ui_group","name":"Meters per Minute","tab":"466e33abb95e4dd4","order":5,"disp":true,"width":"10","collapse":false,"className":""},{"id":"466e33abb95e4dd4","type":"ui_tab","name":"Sliders","icon":"dashboard","order":3,"disabled":false,"hidden":false}] ``` :: We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please [get in touch](mailto\:contact@flowfuse.com). You can also read some of our previous Node-RED tips using the links below. [Node-RED Tips - Subflows, Link Nodes, and the Range Node](https://flowfuse.com/blog/2023/04/3-quick-node-red-tips-6/):br[Node-RED Tips - Importing, Exporting, and Grouping Flows](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-5/):br[Node-RED Tips - Smooth, Catch, and Maths](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-4/):br[Node-RED Tips - Exec, Filter, and Debug](https://flowfuse.com/blog/2023/03/3-quick-node-red-tips-3/):br[Node-RED Tips - Deploying, Debugging, and Delaying](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-2/):br[Node-RED Tips - Wiring Shortcuts](https://flowfuse.com/blog/2023/02/3-quick-node-red-tips-1/) # Community News June 2023 Welcome to the FlowFuse newsletter for June 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. ## New Release This week we released FlowFuse 1.8, featuring high availability for Node-RED and DevOps software delivery pipelines. Both these features were in high demand from our community and will make it easier to reliably deliver Node-RED for business critical applications. Read about the details of FlowFuse 1.8 in our [release announcement](https://flowfuse.com/blog/2023/06/flowforge-1-8-released/). ## Upcoming events ### Building Node-RED Applications for Scalability and High Availability Our June webinar will focus on the new FlowFuse 1.8 feature of running high availability Node-RED applications. Marian Demme, FlowFuse Product Manager, will lead this session and share practical insights and best practices to show how FlowFuse can unlock the true potential of Node-RED in large-scale deployments. [Sign-up today](https://flowfuse.com/webinars/2023/building-scalable-ha-node-red/) to join us on June 22. ### Build an Edge-to-Cloud Solution with the MING Stack On June 27, FlowFuse is doing a webinar with our friends at InfluxDB. A great opportunity to see how easy it is to use Node-RED and InfluxDB to send data from the edge to the cloud.  [Sign-up today](https://www.influxdata.com/resources/build-an-edge-to-cloud-solution-with-the-ming-stack/?utm_source=partner&utm_medium=referral&utm_campaign=2023-06-27_Webinar_FlowFuse-NodeRED&utm_term=speaker){rel=""nofollow""}. ## From our Blog - [Bringing High Availability to Node-RED](https://flowfuse.com/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](https://flowfuse.com/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](https://flowfuse.com/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. - [Persisting chart data in Node-RED Dashboards](https://flowfuse.com/blog/2023/05/persisting-chart-data-in-node-red/) - How to store data from the Node-RED Dasbhaord chart node. - [Node-RED Community Survey Results](https://flowfuse.com/blog/2023/05/node-red-community-survey-results/) - A quick summary of the Node-RED Community Survey results. - [FlowFuse 1.7 Now Available with Remote Node-RED Editor Access](https://flowfuse.com/blog/2023/05/flowforge-1-7-released/) ## From the Community Gerrit Riessen has published a list of [Pros and Cons for using Node-RED](https://gorenje.medium.com/fourteen-for-fourteen-against-why-i-love-hate-and-connect-with-node-23797f9466ec){rel=""nofollow""}. It is a pretty comprehensive list so check it out. FlowFuse is working to address some of the cons in Gerrit's list, specifically software delivery pipelines and the ability to deploy out to many end points. ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - [DevOps Engineer](https://boards.greenhouse.io/flowfuse/jobs/4796271004){rel=""nofollow""} - [Sales Representative](https://boards.greenhouse.io/flowfuse/jobs/4843566004){rel=""nofollow""} # The Next Step in Data Visualization - Announcing the Successor to the Node-RED Dashboard For the past several years, the Node-RED Dashboard has been an indispensable tool for many Node-RED users. It has offered a seamless way to create live dashboards, enabling the quick and intuitive creation of user interfaces for Node-RED flows. However, as the saying goes, "all good things must come to an end." We at FlowFuse have identified a significant need for a modern, interactive data visualization and dashboard solution. Having evaluated a wide range of options, we've decided to embark on an exciting journey: the creation of what we hope becomes the official successor to the Node-RED Dashboard. ## The Problem at Hand The original Node-RED Dashboard is based on Angular v1, which is no longer maintained. Although small patches have been and will continue to be applied on a "best can do" basis, there will be no major feature upgrades. The lack of ongoing maintenance and updates has the potential to lead to underlying security breakages, a risk we are not comfortable taking. We have recognized the need to innovate and adapt, which is why we are creating a completely new project to replace the existing Node-RED Dashboard. ## The Solution The successor to the Node-RED Dashboard will be a completely new project, published as a separate package. This new project will take the reins from the old dashboard and guide us into the future with the support and blessing of the existing dashboard maintainers. The Node-RED community can rest assured that the project will stay under the Apache 2.0 licence and keep intact the core principles of open-source and community-driven development. ## Community Involvement and Open Source Contribution We believe in the power of the community, and we want your feedback. If there are features you'd like to see or improvements you think can be made, we invite you to open a [Github issue](https://github.com/FlowFuse/node-red-dashboard/issues/new/choose){rel=""nofollow""}. Your insights and suggestions will be invaluable in shaping the future of this project. Contributions from the community are not just welcome, but highly encouraged. We believe that the strength of a project is proportional to the strength of its community. In our commitment to transparency and collaboration, we will be documenting all major decision-making processes and sharing the details here in our blog as they become available. By working closely with additional Node-RED key figures like Dave Conway-Jones, and by making our development process as open and collaborative as possible, we aim to ensure that the successor to the Node-RED Dashboard lives up to the high standards set by the original, while also introducing innovative features and enhancements. ## Join the Team Are you a developer looking for a new challenge? We are searching for a freelancer to help us with the development of the first version of the new dashboard. This is a 2-3 month project that provides an exciting opportunity to contribute to the future of data visualization and Node-RED. If you're interested, [we'd love to hear from you.](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} ## Charting the Course As we embark on this new journey, we are excited about the potential of the successor to the Node-RED Dashboard. The road ahead will be filled with challenges, but we are confident that with the help of the community, and a dedicated team, we will create a tool that surpasses its predecessor in every way. Join us on this exciting journey as we innovate, create, and redefine the future of data visualization with the successor to the Node-RED Dashboard. # FlowFuse now offers High Availability Node-RED FlowFuse 1.8 introduces two key features that allow organizations to reliably deploy Node-RED applications into production. In 1.8, it is now possible to run Node-RED applications with high availability so the application is more scalable and more fault tolerant. FlowFuse 1.8 also introduces software deliver pipelines, so development teams can now set up dev/test/production environments for their Node-RED applications. ## More reliable and scalable Node-RED applications FlowFuse now makes it possible to deploy business critical applications built in Node-RED that are reliable and scalable. The new 1.8 features allows a Node-RED instance to be deployed in high availability mode, meaning two instances of the same Node-RED flows are available behind a load balancer. This allows for increased traffic to be automatically distributed across the two Node-RED instances. This means your Node-RED applications can handle more traffic and experience less downtime. For more details, please see our [documentation](https://flowfuse.com/docs/user/high-availability). Additionally, we're pleased to offer a 30-day premium trial license for self-managed installs on Kubernetes. To avail of this offer, book a demo at [flowforge.com/book-demo](https://flowfuse.com/book-demo). High Availability is our first [preview feature](https://flowfuse.com/handbook/engineering/product/versioning/#preview-features), and your feedback is crucial. We encourage you to try out HA in your Node-RED instances and share your experiences with us. Your feedback will help us refine this feature and make it even better. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: mbDkjKhVwIw --- :: ## DevOps Pipelines FlowFuse 1.8 introduces the concept of Pipelines to better organize your Node-RED development. Development team can now set up different staging environments for different steps in the development cycle, ex. test, development and production. Node-RED instances can be pushed along a pipeline as they move along the development process. This allows for a better organized and predictable development process for your team. The new Pipelines feature builds upon the Staged Development support that we introduced in[FlowFuse Version 1.4](https://flowfuse.com/blog/2023/02/flowforge-1-4-0-released/). We highly recommend that development teams avoid developing their flows directly in production instances. This approach fosters a more reliable and robust development process, reducing the risks associated with production environment modifications. Instead, start your development in a dedicated development or test instance and then deploy your Node-RED instance to production once they have been thoroughly tested and reviewed. ::lite-youtube --- params: rel=0 style: "width: 704px; height: 100%;" title: YouTube video player videoid: Pbql22f3vqY --- :: ## User interface for Device Agent In our previous release, we introduced [Editor Access for Devices](https://flowfuse.com/blog/2023/05/flowforge-1-7-released/). Now, the FlowFuse Device Agent now comes with its very own User Interface (UI) for configuration. Imagine this: Your industrial equipment arrives with the Device Agent preinstalled. In the past, you might have faced challenges in configuring and connecting your device with FlowFuse, particularly if you had no direct shell access. But not any more. With the newly introduced UI, you can easily set up and connect your device with FlowFuse without needing to access the command line interface directly. This simplifies the process significantly and saves you time. For more details, see our [documentation](https://flowfuse.com/docs/device-agent/introduction/). ## Node-RED 3.1 Beta 3 Available FlowFuse Cloud is a great place to try out the new Node-RED features, with FlowFuse Cloud now including the [Node-RED 3.1.0-beta.3](https://discourse.nodered.org/t/node-red-3-1-0-beta-3-released/78716){rel=""nofollow""}. If you want to try this version you can [duplicate your instance](https://flowfuse.com/docs/user/instance-settings/) and [upgrade your stack](https://flowfuse.com/docs/user/changestack/). ## Ongoing Topics ### SOC2 Certification We're making great strides on our journey towards SOC2 certification, striving to meet the highest industry standards for security and privacy. While we're not quite ready to disclose specific milestones, be assured that everything is progressing smoothly. As we continue working diligently towards our target, we promise to keep you informed every step of the way. Our unwavering commitment to deliver secure and private services to our customers and partners remains our foremost priority. Stay tuned for more updates! ## What's next? We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute: - **Roadmap Overview**: Check out our [Product Roadmap Page](https://flowfuse.com/changelog/) to see what we're planning for future updates. - **Entire Roadmap**: Visit our [Roadmap on GitHub](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""} to follow our progress and contribute your ideas. - **Feedback**: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any [issues on GitHub](https://github.com/FlowFuse/flowfuse/issues/new/choose){rel=""nofollow""}. Together, we can make FlowFuse better with each release! ## Bug Fixes When editing a device in developer mode via the tunnel/proxy connection, a list of team projects are not presented in the "Target" field of the project-link nodes. [#2228](https://github.com/FlowFuse/flowfuse/issues/2228){rel=""nofollow""} If a user invites an external user to their team with an sso-enable email domain, when that user registers and logs in, they are not added to the team they were invited to and must be re-invited. [#2232](https://github.com/FlowFuse/flowfuse/issues/2232){rel=""nofollow""} No warning given if tying to start device editor when NR is not running [#2233](https://github.com/FlowFuse/flowfuse/issues/2233){rel=""nofollow""} Stuck on the form Create a new Application & Instance after using an already known instance name [#2221](https://github.com/FlowFuse/flowfuse/issues/2221){rel=""nofollow""} If the device agent finds itself in Developer mode, it stops pulling snapshots from the platform [#97](https://github.com/FlowFuse/device-agent/issues/97){rel=""nofollow""} Accessing the Admin Settings General page resets the Platform Statstics token [#2140](https://github.com/FlowFuse/flowfuse/issues/2140){rel=""nofollow""} Selection of Team and Instance in nr-tools-plugin not possible [#15](https://github.com/FlowFuse/nr-tools-plugin/issues/15){rel=""nofollow""} HOME env var not set within Node-RED process [#117](https://github.com/FlowFuse/flowforge-nr-launcher/issues/117){rel=""nofollow""} ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.8. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. Additionally you can go the the [community forum](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""} if you have any feedback or feature requests. # Use any npm module in Node-RED (2026) Node-RED has [an incredibly rich resource of integrations available](https://flows.nodered.org/search?type=node), but sometimes you need that little bit of extra functionality, or access to a Node.js module that doesn't have it's own custom nodes in Node-RED. **We can easily import any npm module within the built-in Node-RED function nodes.** Historically in Node-RED, you would have needed to manually `npm install` modules from the command line, but now that it's so easy to run Node-RED in the Cloud, where you don't have easy access to those tools, what are the other options available? ## Function Node - Setup ![Location of the "add" button in order to import an npm module intoa function node](https://flowfuse.com/blog/2023/06/images/npmimport-add.jpg "Location of the 'add' button in order to import an npm module intoa function node") :cta-image{alt="Walk through your FlowFuse setup with our team - book a demo" cta="demo" src="https://flowfuse.com/images/cta/book-a-demo.png"} All you need is the name of the module you want to import, then: 1. Drop in a new "function" node & double-click it 2. Switch to the "Setup" tab 3. Underneath the "modules" tab, click "+ add" in the bottom-left of the window. 4. Enter the name of the module you want to use in the newly created row, and (optionally) modify the `variable` that this module will be imported in as. 5. Switch back to the "On Message" tab and write your function. Your new module will be available via the `variable` you defined in the "Setup" tab. ## Example: Moment.js :video{controls="true" height="315" width="560"} Recently we wanted to use [moment](https://www.npmjs.com/package/moment){rel=""nofollow""} for some custom date calculations. Whilst there was set of [Moment Node-RED nodes](https://flows.nodered.org/node/node-red-contrib-moment){rel=""nofollow""} already available, it didn't have all of the functionality we needed. So, all we needed to do was import the module into a function node, and define our comparison there instead, here's a working example: ## Example: Easy CRC :video{controls="true" height="315" width="560"} Something we see [a lot on the Node-RED Forums](https://discourse.nodered.org/search?q=crc%20order%3Alatest){rel=""nofollow""} are questions on how to conduct CRC calculations. There is a popular node module `easy-crc` that can be imported and used in the function nodes, e.g: ## Example: PostHog :video{controls="true" height="315" width="560"} Node-RED is great for [data integration](https://flowfuse.com/use-cases/data-integration/). We use [PostHog](https://posthog.com/) for our internal Product Analysis. We record live events as they occur on FlowFuse Cloud to better understand features that are (and are not) used. We wanted to investigate whether or not we could add backdated data, which in theory was possible via their [posthog-node](https://posthog.com/docs/libraries/node) module. We wanted to populate it with data driven from our own database and API. Within two minutes, we could wire up a node to retrieve data from our API, and then ingest it into `posthog-node` via the import of a function node. ## Simplify Function Node Creation with FlowFuse [FlowFuse](https://flowfuse.com) provides a powerful platform to enhance, scale, and secure your Node-RED applications efficiently. One of our latest features, the **FlowFuse Assistant**, is designed to streamline the process of creating Function nodes. With the FlowFuse Assistant, you can leverage AI to generate Function nodes effortlessly. Just input your prompt, and the Assistant will handle the creation for you, saving time and reducing manual coding. To explore how to make the most of the FlowFuse Assistant and its capabilities, check out the [Assistants Documentation](https://flowfuse.com/docs/user/expert/). # Introducing the FlowFuse Community Forum We are thrilled to announce the launch of the [Community Forum for FlowFuse](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""}. A forum dedicated to empowering developers and enthusiasts to create innovative applications using FlowFuse, Node-RED and related technologies. Our community faces a new set of challenges, for example how to configure FlowFuse templates as administrator, or integration of the FlowFuse platform into another existing environment. These questions do not fit on the Node-RED discourse which is why FlowFuse now sports our own Community Forum. Furthermore, it’s the intent to keep the Node-RED forums vendor agnostic by the OpenJS foundation. Given FlowFuse is a vendor, it’s a fine balance to find. We hope and intend to be additive to the Node-RED community at large. At FlowFuse, our vision is to create a thriving community of Node-RED and MQTT enthusiasts who are passionate about building real-world solutions. We believe in the power of collaboration, knowledge sharing, and problem-solving. With this in mind, FlowFuse aims to foster a positive and inclusive environment where members can engage in discussions, exchange ideas, and support one another. Initially the forum is used to allow for discussions under each blog post on the FlowFuse blog, as well as a venue to provide community support. We also welcome discussions on the roadmap and backlog of FlowFuse. We have hopes that this community thrives and provides guidance, inspiration and opportunity to learn. # Node-RED as a No-Code EtherNet/IP to S7 Protocol Converter Frequently in industrial automation, there's a need for two devices that use different protocols to communicate with each other, requiring protocol conversion. :br In this tutorial, we present a mock scenario where Node-RED is used to enable an Allen Bradley PLC, which uses ethernet/IP, to communicate with a Siemens PLC, which uses S7, using a no-code solution. This example is geared toward beginners and assumes that the end-user knows how to use PLCs, but may be using FlowFuse or Node-RED for the first time. ## Premise ![Mock production facility](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-1.png "FlowFuse Mock production facility") The figure above shows the layout of a mock production facility. Inside this facility, operations suggested adding stack lights as an extra visual aid for operators to get a quick status of its 4 conveyor lines, avoiding the need to constantly monitor the HMI/SCADA displays. :br Engineering has suggested adding a siemens S7 1200 PLC with an IO link connection to 4 stacklights, with each line PLC sending basic status information to the stacklight PLC to control the stack light outputs. :br Line 1-3 PLCs are Siemens-based, and can communicate with the stacklight PLC natively over S7. But line 4 is an Allen Bradley PLC that uses ethernet/IP, and can't communicate with the stacklight PLC without some form of protocol conversion. :br Traditionally, we'd use protocol gateway hardware, like Anybus or Red Lion, to convert ethernet/IP to S7. :br But for this application, we will instead use FlowFuse, a pure software-based approach, to convert ethernet/IP to S7. Let's walk through the process. ## Pre-Requisites and Set Up ### FlowFuse In addition to our two PLCs, we’ll be using FlowFuse software to serve our Node-RED instance. You can either self-host, on-premise or in the cloud. Or use the managed service \[FlowFuse Cloud]\({{ site.appURL }}). In this example, we will be using a self-hosted FlowFuse instance running on [Docker](https://flowfuse.com/docs/install/docker/). ### Data Treatment on Ethernet/IP PLC In our Allen Bradley line 4 PLC, we will send some arbitrary tags of various datatypes to the stacklight PLC for illustrative purposes, described in table 1 below - | **Tag** | **Data Type** | **Description** | | ----------------- | ------------- | ---------------------------- | | Conveyor\_RTS | BOOL | Conveyor Ready to Start | | Robot\_RTS | BOOL | Robot is Ready to Start | | Robot\_Position | REAL | Robot Arm position (degrees) | | Conveyor\_Running | BOOL | Conveyor is running | | Line4\_State | DINT | Line 4 Machine State | | Line4\_Fault | BOOL | Line 4 is faulted | Table 1 - Line 4 Tags to be sent to Stacklight PLC We can send any atomic data type we want, but it must be globally (controller) scoped. !["Screenshot showing the AB Controller Tags"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-2.png "Screenshot showing the AB Controller Tags") Each tag must also have external read/write access enabled. !["Screenshot showing the AB Tag Properties"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-3.png "Screenshot showing the AB Tag Properties") ### Data Treatment on S7 PLC In the Siemens PLC, we have a DB for the data from the Line 4 PLC to be written to. - In the DBs attributes, “optimized block access” must be disabled. - The tags must be writeable and accessible - !["Screenshot showing the Siemens Tag DB Properties"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-4.png "Screenshot showing the Siemens Tag DB Properties"):br “No protection” must be set in the CPU properties - !["Screenshot showing the Siemens CPU Properties"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-5.png "Screenshot showing the Siemens CPU Properties") ## Create The Flow With both PLCs up and running and properly set up to send/receive remote data, we can now create a flow to act as our protocol converter. ### Install Custom Nodes First, we need to add two custom nodes that will give Node-RED the ability to read/write ethernet/IP and S7 data. Click the hamburger icon → manage pallette ![Screenshot showing the 'Manage palette option' in the menu](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-6.png) On the `install` tab, search for `s7` and install the `node-red-contrib-s7` node. !["Installing S7 node"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-7.png "Installing S7 node") Next, search for `ethernet` and install the `node-red-contrib-cip-ethernet-ip` node. !["InstallING EthernetIP Node"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-8.png "InstallING EthernetIP Node") Go to the `nodes` tab and confirm both custom nodes have been properly installed. !["Screenshot of 'Nodes' tab showing Installed nodes List"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-9.png "Screenshot of 'Nodes' tab showing Installed nodes List") ### Set Up Ethernet/IP Data Let’s start by dragging a `eth-ip in` node onto the pallette. Then add a new endpoint, which will point to our Line4 PLC. !["Screenshot showing dragged 'eth-ip in' node and it's config tab"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-10.png "Screenshot showing dragged 'eth-ip in' node and it's config tab") In the endpoint `connection` properties, the connection information must match the PLC, so set the IP address and CPU slot number appropriately. Also, the default cycle time is 500ms. Depending on your application, polling the CPU at 500ms may be appropriate. But being that this is a simple stacklight, 500ms is unnecessarily fast. So we will change it to 1000ms, which is a more appropriate polling rate for this type of application. !["Screenshot showing the eth-ip Endpoint config"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-11.png "Screenshot showing the eth-ip Endpoint config") On the `Tags` tab, populate the tag information to match our Allen Bradley PLC. Then select `Update` to complete configuration of the `eth-ip endpoint`. !["Screenshot showing eth-ip Endpoint Tags"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-12.png "Screenshot showing eth-ip Endpoint Tags") Now that we have our endpoint, let’s finish configuring the `eth-ip in` node. 1. select the endpoint we just created 2. select the first tag in the drop-down 3. give the node a descriptive name ![Screeshot showing the eth-ip in Node config](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-13.png "Screeshot showing the eth-ip in Node config") Now let’s set up a quick test to confirm our PLC connection is valid by adding a `debug` node to the `eth-ip in` node. Then hit `deploy`. - note - you can see we also have a `comment` above the nodes that describes what is happening. This is optional but good practice to help organize and understand your flow. The output of the debug console did not report any errors so communication appears to be okay. ![Screenshot showing the output of eth-ip in Debug panel](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-14.png "Screenshot showing the output of eth-ip in Debug panel") But just to confirm, let’s toggle the value and see if comes through. ![Screenshot showing the eth-ip node output in Debug panel after Toggle](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-15.png "Screenshot showing the eth-ip node output in Debug panel after Toggle") So by toggling the value and see the result, here we confirmed 2 things: - We can detect changes in value - the `eth-ip in` node only sends a message when the value changes, also known as Report by Exception. Because the `eth-ip in` node implicitly uses report by exception, and the protocol doesn't rely on contiguous data consistency (unlike modbus, for instance), we can receive our data one tag at a time to keep our flow simple. Now we can remove the debug node and add the additional `eth-ip in` nodes to receive the remaining tags from our Line 4 PLC. Here’s how the the flow should look at this point. ![Screenshot of Line 4 PLC Nodes](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-16.png "Screenshot of Line 4 PLC Nodes") ### Set Up S7 Data Now we’ll set up the S7 endpoint, using an `s7 out` node. ![Screenshot of s7 out Node on Palette](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-17.png "Screenshot of s7 out Node on Palette") Populate the connection properties to match your hardware. The cycle time is updated to 1000ms to match the cycle time of our `eth-ip in` nodes. You can adjust this value to match your intended application. !["Screenshot showing the S7 endpoint Connection"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-18.png "Screenshot showing the S7 endpoint Connection") On the `Variables` tab, some special formatting is required to point to the absolute reference of the tag DB location in the S7 PLC. For information on how to format S7 absolute tag references in a way the `s7 endpoint` node is expecting, refer to the [node documentation](https://flows.nodered.org/node/node-red-contrib-s7){rel=""nofollow""} for further information. For reference, here is an example of how we set the tags in our stacklight PLC example and how it looks in our `s7 endpoint`. !["Screenshot of s7 endpoint Variables"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-19.png "Screenshot of s7 endpoint Variables") Once the tags are populated we can select our configured endpoint from the dropdown list, point to our first variable, `Conveyor_RTS`, and give the node a name. !["Screenshot of S7 out Config"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-20.png "Screenshot of S7 out Config") Repeat this process for the remaining tags. !["Screenshot of Stacklight PLC Nodes"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-21.png "Screenshot of Stacklight PLC Nodes") ## Test the Conversion The only thing remaining is to simply wire the nodes together, and confirm the values pass through. !["Screenshot of the complete flow with live Data"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-22.png "Screenshot of the complete flow with live Data") Manipulate the incoming values and confirm the data passes through as expected. Because of the report by exception nature of the `eth-ip in` node, tag changes should be near instantaneous on the receiving PLC. We can stop here, but we can improve this flow by adding a `filter` node on our REAL data-type, `Robot_Position`. ### Add Filter to REAL data Depending on how noisy the REAL data is, which is common with unfiltered 4-20mA field transmitters, and how much granularity you need to capture, it is good practice to add a filter on REAL data to reduce FieldBus traffic coming out of our soft protocol converter. !["Screenshot showing the Filter node Configuration"](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-23.png "Screenshot showing the Filter node Configuration") In the example above, we arbitrarily applied a 3% [deadband](https://flowfuse.com/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. We can see the effect the deadband filter had by adding debug nodes before and after the filter. ![Filter Node Debug](https://flowfuse.com/blog/2023/06/images/ethip-to-S7/e-to-p-24.png "Filter Node Debug") As shown above, when `Robot_Position` changed from 15.6 to 15.6999..., the value was captured on the input of the filter, but was discarded on the output. When the `Robot_Position` went from 15.6999 to 18, the filter allowed it to pass as it exceeded the deadband limit we had set. Use filters to optimize your fieldbus converter network performance, especially if dealing with noisy signals or large quantities of REAL datatypes. ## Conclusion In this tutorial, we demonstrated how to use Node-RED as a free Ethernet/IP to S7 protocol converter using a simple no-code approach. We showed how to configure PLC tags to be sent remotely using Ethernet/IP, how to configure PLC tags to be received remotely using S7, and how to create the flow to use Node-RED to seamlessly convert incoming PLC data between the two protocols using `node-red-contrib-cip-ethernet-ip` and `node-red-contrib-s7` custom nodes. We also took things one step further and added a `filter` node to optimize FieldBus network traffic by putting a deadband on REAL data being sent to the receiving PLC. The end result is a simple to set up, free and performant industrial protocol converter that requires minimal PLC configuration, which allows this application to be applied in non-mission critical production systems with minimal, if any downtime. Additionally, the protocol traffic can be visually observed in real-time for easy trouble-shooting and fault analysis by simply accessing the Node-RED UI. In later tutorials, we can show ways this simple flow can be extended to add additional capabilities not normally available in traditional off-the-shelf protocol gateways. If you found this tutorial helpful, or have any questions or comments, please leave us a comment and let us know your thoughts. JSON source code for the flow used in this tutorial is provided below - ::render-flow{:height='500'} ```json [{"id":"ad7b17411c8e83aa","type":"tab","label":"Line 4 to Stacklight PLC","disabled":false,"info":"","env":[]},{"id":"c97a4c9bd1981757","type":"comment","z":"ad7b17411c8e83aa","name":"AB EIP/CIP - Line 4 PLC","info":"","x":190,"y":140,"wires":[]},{"id":"2cc5227ef6a90814","type":"eth-ip in","z":"ad7b17411c8e83aa","endpoint":"4ab2910b66e16220","mode":"single","variable":"Conveyor_RTS","program":"","name":"Read Conveyor_RTS","x":200,"y":200,"wires":[["fe18ef80f9e18c13"]]},{"id":"9308dcbda17274c7","type":"comment","z":"ad7b17411c8e83aa","name":"Siemens S7 - Stacklight PLC","info":"","x":620,"y":140,"wires":[]},{"id":"fe18ef80f9e18c13","type":"s7 out","z":"ad7b17411c8e83aa","endpoint":"a1bec25858c6f3ef","variable":"Conveyor_RTS","name":"Write Conveyor_RTS","x":620,"y":200,"wires":[]},{"id":"94fe6b73efa1c56b","type":"eth-ip in","z":"ad7b17411c8e83aa","endpoint":"4ab2910b66e16220","mode":"single","variable":"Robot_RTS","program":"","name":"Read Robot_RTS","x":180,"y":280,"wires":[["7774d6ce188c288c"]]},{"id":"7e9564cd59e3d0a2","type":"eth-ip in","z":"ad7b17411c8e83aa","endpoint":"4ab2910b66e16220","mode":"single","variable":"Robot_Position","program":"","name":"Read Robot_Position","x":200,"y":360,"wires":[["832807bfdc4b76f0"]]},{"id":"c0f712b9e355f1f8","type":"eth-ip in","z":"ad7b17411c8e83aa","endpoint":"4ab2910b66e16220","mode":"single","variable":"Conveyor_Running","program":"","name":"Read Conveyor_Running","x":210,"y":440,"wires":[["fbf1b3e38897a9c7"]]},{"id":"db77621e418f1222","type":"eth-ip in","z":"ad7b17411c8e83aa","endpoint":"4ab2910b66e16220","mode":"single","variable":"Line4_State","program":"","name":"Read Line4_State","x":190,"y":520,"wires":[["cdeffd9e52cc4384"]]},{"id":"848af9b76f969dd2","type":"eth-ip in","z":"ad7b17411c8e83aa","endpoint":"4ab2910b66e16220","mode":"single","variable":"Line4_Fault","program":"","name":"Read Line4_Fault","x":190,"y":600,"wires":[["0c595b0ac2550593"]]},{"id":"7774d6ce188c288c","type":"s7 out","z":"ad7b17411c8e83aa","endpoint":"a1bec25858c6f3ef","variable":"Robot_RTS","name":"Write Robot_RTS","x":610,"y":280,"wires":[]},{"id":"f1572463c50bb4cb","type":"s7 out","z":"ad7b17411c8e83aa","endpoint":"a1bec25858c6f3ef","variable":"Robot_Position","name":"Write Robot_Position","x":620,"y":360,"wires":[]},{"id":"fbf1b3e38897a9c7","type":"s7 out","z":"ad7b17411c8e83aa","endpoint":"a1bec25858c6f3ef","variable":"Conveyor_Running","name":"Write Conveyor_Running","x":630,"y":440,"wires":[]},{"id":"cdeffd9e52cc4384","type":"s7 out","z":"ad7b17411c8e83aa","endpoint":"a1bec25858c6f3ef","variable":"Line4_State","name":"Write Line4_State","x":610,"y":520,"wires":[]},{"id":"0c595b0ac2550593","type":"s7 out","z":"ad7b17411c8e83aa","endpoint":"a1bec25858c6f3ef","variable":"Line4_Fault","name":"Write Line4_Fault","x":610,"y":600,"wires":[]},{"id":"832807bfdc4b76f0","type":"rbe","z":"ad7b17411c8e83aa","name":"","func":"deadbandEq","gap":"3%","start":"","inout":"in","septopics":true,"property":"payload","topi":"topic","x":420,"y":360,"wires":[["f1572463c50bb4cb"]]},{"id":"4ab2910b66e16220","type":"eth-ip endpoint","address":"192.168.0.5","slot":"0","cycletime":"1000","name":"Line4","vartable":{"":{"Conveyor_RTS":{"type":"BOOL"},"Robot_RTS":{"type":"BOOL"},"Robot_Position":{"type":"REAL"},"Conveyor_Running":{"type":"BOOL"},"Line4_State":{"type":"DINT"},"Line4_Fault":{"type":"BOOL"}}}},{"id":"a1bec25858c6f3ef","type":"s7 endpoint","transport":"iso-on-tcp","address":"192.168.0.10","port":"102","rack":"0","slot":"1","localtsaphi":"01","localtsaplo":"00","remotetsaphi":"01","remotetsaplo":"00","connmode":"rack-slot","adapter":"","busaddr":"2","cycletime":"1000","timeout":"3000","name":"Stacklight PLC","vartable":[{"addr":"DB1,X0.0","name":"Conveyor_RTS"},{"addr":"DB1,X0.1","name":"Robot_RTS"},{"addr":"DB1,R2","name":"Robot_Position"},{"addr":"DB1,X6.0","name":"Conveyor_Running"},{"addr":"DB1,DI8","name":"Line4_State"},{"addr":"DB1,X12.0","name":"Line4_Fault"}]}] ``` :: # Community News July 2023 Welcome to the FlowFuse newsletter for July 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community. ## New Release Last week we released FlowFuse 1.9, featuring new API documentation available in the Swagger UI and the ability to customize Node-RED palettes. Read about the details of FlowFuse 1.9 in our [release announcement](https://flowfuse.com/blog/2023/07/flowforge-1-9-release/). ## Upcoming events ### How to deploy Node-RED to hundreds of PLCs and IoT edge devices Our next webinar will be focused on the device management capabilities in the FlowFuse platform. Lots of companies are deploying Node-RED to PLCs and IIoT edge computers. FlowFuse allows these companies to scale and manage Node-RED deployment out to hundreds of these types of devices. Discover how during our next webinar. [Sign-up today](https://flowfuse.com/webinars/2023/flowforge-device-management/) to join us on July 27. ## From our Blog and Documentation - [The Next Step in Data Visualization - Announcing the Successor to the Node-RED Dashboard](https://flowfuse.com/blog/2023/06/dashboard-announcement/) - FlowFuse announces plans to develop the next version of the Node-RED Dashboard project. - [Node-RED as a No-Code Ethernet/IP to S7 Protocol Converter](https://flowfuse.com/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){rel=""nofollow""} of the same content. - [MQTT and its Role in IoT and Industrial IoT](https://flowfuse.com/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](https://flowfuse.com/node-red/core-nodes/split/) - [Nodes explained: Filter](https://flowfuse.com/node-red/core-nodes/filter/) ## From the Community - [Node-RED & Industry 4.0: The Future is Now](https://youtu.be/1GKkXJOQMhU){rel=""nofollow""} - An informative panel discussion led by Walker Reynolds on the role of Node-RED in the future of Industry 4.0. - [Node-RED Terminology](http://blog.openmindmap.org/blog/node-red-terminology){rel=""nofollow""} - An explanation of the different Node-RED terminology. ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - [Contract Front-End Engineer – Node-RED Dashboard](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} # First Pre-Alpha Release of the new Node-RED Dashboard Just weeks ago, we at FlowFuse [announced our plan](https://flowfuse.com/blog/2023/06/dashboard-announcement/) to develop a successor to the Node-RED Dashboard. Today, we're excited to reveal the pre-alpha release of this highly anticipated project, bringing us one step closer to a new era of data visualization in Node-RED. ## Sneak Peek into the New Node-RED Dashboard The Node-RED Dashboard successor is now available for install as an npm package under the name [@flowforge/node-red-dashboard](https://www.npmjs.com/package/@flowforge/node-red-dashboard){rel=""nofollow""} in your Node-RED palette manager. This pre-alpha version includes the first set of Vue.js-based elements familiar to Node-RED Dashboard users: !["new Node-RED Dashboard Elements"](https://flowfuse.com/blog/2023/07/images/nr-dashboard-screenshot.png "new Node-RED Dashboard Elements") This is but a hint of what's to come. The objective of these pre-alpha releases is to provide early access to the current status. The strength of the project comes from the community. Your insights, suggestions and contributions play a significant role in shaping the future of this dashboard. Keep them coming through our [Github page](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. ## Current Status and Next Steps As of now, we've implemented the following Dashboard Widgets: - UI Text ([ui-text Widget](https://github.com/FlowFuse/node-red-dashboard/issues/38){rel=""nofollow""}) - Text Input ([ui-text-input Widget](https://github.com/FlowFuse/node-red-dashboard/issues/39){rel=""nofollow""}) - Range Slider ([ui-slider Widget](https://github.com/FlowFuse/node-red-dashboard/issues/47){rel=""nofollow""}) - Dropdown/Select ([ui-dropdown Widget](https://github.com/FlowFuse/node-red-dashboard/issues/45){rel=""nofollow""}) We've also introduced a new widget: - Markdown ([ui-markdown Widget](https://github.com/FlowFuse/node-red-dashboard/issues/62){rel=""nofollow""}) Not yet part of the first Pre-Alpha Release: - Toggle Switch ([ui-switch Widget](https://github.com/FlowFuse/node-red-dashboard/issues/42){rel=""nofollow""}) - Color Picker ([ui-color-picker Widget](https://github.com/FlowFuse/node-red-dashboard/issues/46){rel=""nofollow""}) - Number Input ([ui-numeric Widget](https://github.com/FlowFuse/node-red-dashboard/issues/41){rel=""nofollow""}) - Form ([ui-form Widget](https://github.com/FlowFuse/node-red-dashboard/issues/49){rel=""nofollow""}) - Date selector ([ui-date-picker](https://github.com/FlowFuse/node-red-dashboard/issues/32){rel=""nofollow""}) Our immediate focus is to continue adding the missing elements from the original Node-RED Dashboard, releasing each as soon as they're fully developed. This will significantly increase the frequency of our releases in the upcoming weeks. In addition to these releases, we plan to publish regular blog posts titled "What's New in Node-RED Dashboard". These posts will keep you informed of all the latest features, updates, and improvements. # FlowFuse 1.9.3 and Device Agent 1.9.5 released FlowFuse and the Device Agent both received updates yesterday that bring improvements to the Device Agent editor experience, making it more resilient to network issues. ## Improving the Device Agent editor experience The ability to remotely edit flows running the Device Agent has been warmly welcomed by many users on the platform. Along with that comes great feedback we can use to continue improving the user experience. Some early feedback identified issues with the resilience of the tunnel we connect between the Device Agent and the platform. If the tunnel was interrupted for any reason, the user would have to manually set it up again. With the FlowFuse 1.9.3 release, now running FlowFuse Cloud, along with the latest version of the Device Agent, we have made the tunnel much more resilient. It can now restablish itself without any intervention from the user - making for a much more seamless experience. - [#2488](https://github.com/FlowFuse/flowfuse/pull/2488){rel=""nofollow""} - [#2507](https://github.com/FlowFuse/flowfuse/pull/2507){rel=""nofollow""} ## Other New Features and Bug Fixes - Fixes incorrect 'start-failed' notifications when restarting an instance [#2505](https://github.com/FlowFuse/flowfuse/pull/2505){rel=""nofollow""} The system log now includes more information about the callingThe FlowFuse device agent is now supported on Windows [#78](https://github.com/FlowFuse/device-agent/issues/78){rel=""nofollow""} - Ensures the system logging captures the proper source IP address of requests [#2505](https://github.com/FlowFuse/flowfuse/pull/2503){rel=""nofollow""} - A few documentation updates, including a clarfication on how to run the Device Agent under docker [#2498](https://github.com/FlowFuse/flowfuse/pull/2498){rel=""nofollow""} ## What's next? We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute: - **Roadmap Overview**: Check out our [Product Roadmap Page](https://flowfuse.com/changelog/) to see what we're planning for future updates. - **Entire Roadmap**: Visit our [Roadmap on GitHub](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""} to follow our progress and contribute your ideas. - **Feedback**: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any [issues on GitHub](https://github.com/FlowFuse/flowfuse/issues/new/choose){rel=""nofollow""}. Together, we can make FlowFuse better with each release! ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.9.3. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. Additionally you can go the the [community forum](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""} if you have any feedback or feature requests. # FlowFuse now offers API Documentation with Swagger UI FlowFuse 1.9 adds new features to make it easier to administer FlowFuse platform deployments, including new API documentation and the ability to create customized Node-RED palettes. ## API Documentation FlowFuse API allows developers to programmatically interact with the FlowFuse platform. This makes it possible to integrate FlowFuse into different infrastructure technologies, create scripts to automate specific FlowFuse tasks and embed FlowFuse into other applications. In the 1.9 release we are now publishing our [API documentation](https://flowfuse.com/docs/api/) using the [OpenAPI specification](https://swagger.io/specification/){rel=""nofollow""} and making it viewable with the Swagger UI. Both these industrial standards will make using the FlowFuse API easier to use and understand. ## Customize Node-RED Palettes [#2002](https://github.com/FlowFuse/flowfuse/issues/2002){rel=""nofollow""} FlowFuse platform adminstrators are now able to create customized Node-RED palettes that will be used when a Node-RED instance is created. An adminstator can create pre-defined templates to specify the nodes that should be included in the palette. This makes it easier for FlowFuse teams to standardized on Node-RED usage across an organization. Note: this feature is not available for FlowFuse cloud users since they do not have administrator access. ## New RBAC Role for Dashboard users [#2292](https://github.com/FlowFuse/flowfuse/issues/1924){rel=""nofollow""} A new FlowFuse user role has been created to view Node-RED dashboards. This allows for users to view Nod-RED dashboards without access to the Node-RED editor or requiring separate login credentials. ## Other New Features - FlowFuse device agent is now supported on Windows [#78](https://github.com/FlowFuse/device-agent/issues/78){rel=""nofollow""} - Allow local configuration of https/httpStatic on a device [#110](https://github.com/FlowFuse/device-agent/issues/110){rel=""nofollow""} - Implementing custom certificate settings for device configuration [#2257](https://github.com/FlowFuse/flowfuse/issues/2257){rel=""nofollow""} - Allow devices to access the "Snapshot ID" and the "Snapshot Name" running on them [#94](https://github.com/FlowFuse/device-agent/issues/94){rel=""nofollow""} - High Availability logging enhanced: Individual Node-RED Instance replica querying and filtering [#2260](https://github.com/FlowFuse/flowfuse/issues/2260){rel=""nofollow""} - High Availability is now generally available [#2414](https://github.com/FlowFuse/flowfuse/issues/2412){rel=""nofollow""} ## Bug Fixes - Can not promote NR instance in DevOps Pipeline [#2363](https://github.com/FlowFuse/flowfuse/issues/2363){rel=""nofollow""} - Billing team menu item missing on first page load [#2398](https://github.com/FlowFuse/flowfuse/issues/2398){rel=""nofollow""} - Duplicate labels in Instance Import dialog [#2200](https://github.com/FlowFuse/flowfuse/issues/2200){rel=""nofollow""} - Broken littie animations [#2354](https://github.com/FlowFuse/flowfuse/issues/2354){rel=""nofollow""} - Instance Logs page doesn't handle errors well [#1083](https://github.com/FlowFuse/flowfuse/issues/1083){rel=""nofollow""} - Device continues to run edited flows once taken out of dev mode [#2323](https://github.com/FlowFuse/flowfuse/issues/2323){rel=""nofollow""} - Device Editor cannot pickup FF theme [#89](https://github.com/FlowFuse/device-agent/issues/89){rel=""nofollow""} ## Community Contributions Thanks to our community members for their contributions to this release. - sumitshinde-84 - make Instance and application names in delete popup easily selectable [#2291](https://github.com/FlowFuse/flowfuse/pull/2291){rel=""nofollow""} - biancode - Fixed typo in doc [#2327](https://github.com/FlowFuse/flowfuse/pull/2327){rel=""nofollow""} ## What's next? We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute: - **Roadmap Overview**: Check out our [Product Roadmap Page](https://flowfuse.com/changelog/) to see what we're planning for future updates. - **Entire Roadmap**: Visit our [Roadmap on GitHub](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""} to follow our progress and contribute your ideas. - **Feedback**: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any [issues on GitHub](https://github.com/FlowFuse/flowfuse/issues/new/choose){rel=""nofollow""}. Together, we can make FlowFuse better with each release! ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.9. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. Additionally you can go the the [community forum](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""} if you have any feedback or feature requests. # How to Build an OPC UA Client Dashboard in Node-RED - Part 3 (2026) This article is the third and final part of our OPC UA content series. In the [first article](https://flowfuse.com/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](https://flowfuse.com/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. This article will requires the [Prosys OPC UA Simulation Server](https://prosysopc.com/products/opc-ua-simulation-server/){rel=""nofollow""}, an application designed for testing OPC UA client applications and learning the technology. It’s a free cross-platform application that supports Windows, Linux, and MacOS. This article will use the Windows version. Note: full source code for the OPC Client Dashboard is included at the end of the article. ## Custom Nodes Used & Assumptions Several custom nodes are required in order to properly deploy this flow. For more detailed information on how to install a custom node, follow the instructions from an [earlier article](https://flowfuse.com/blog/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/) where the process on installing custom nodes is explained in detail. - [@flowfuse/node-red-dashboard](https://flows.nodered.org/node/@flowfuse/node-red-dashboard){rel=""nofollow""} - [node-red-contrib-opcua](https://flows.nodered.org/node/node-red-contrib-opcua){rel=""nofollow""} - [@flowfuse/node-red-dashboard-2-ui-led](https://flows.nodered.org/node/@flowfuse/node-red-dashboard-2-ui-led){rel=""nofollow""} As this is not a production application, no security will be utilized, and it is assumed that the OPC UA Server is running on the same network as the Node-RED OPC Client. Is it also assumed that the end user of this article has familiarization with dashboards. There are many dashboard basic guides available on our FlowFuse website, For more infomation go to [Node-RED Dashboard 2.0 guides](https://flowfuse.com/blog/dashboard/). ## Install and Deploy the Prosys OPC UA Simulation Server The Prosys OPC UA Simulation Server is [free to download](https://prosysopc.com/products/opc-ua-simulation-server/evaluate/){rel=""nofollow""}, but requires a sign-up process. Download and install the server, then run the application. Once the application is started, the first thing you should do is go to `options -> switch to expert mode`. This will give us access to the address space tab, which we will need to develop our client application in Node-RED. ![expert-mode.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/expert-mode.png){dataZoomable=""} When the application is run, an endpoint url will be displayed on the `status` tab, along with an indication that the server is currently running. ![opc-endpoint-url.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/opc-endpoint-url.png){dataZoomable=""} Copy the connection endpoint, but be warned that you will likely need to replace the computer name (in my case `DESKTOP-0K0483A`, with the actual IP address of the machine running the server. The IP address of the machine on my local network is `192.168.0.141`, which changes my UA TCP endpoint address to `opc.tcp://192.168.0.141:53530/OPCUA/SimulationServer`. Now the simulation server is set up and we are ready to start developing the OPC Client application. ## Objectives of the Node-RED OPC Client Dashboard Application The goal is not to develop a production-level application, rather, it’s to show a variety of features that one can utilize to demonstrate common OPC UA Client application capabilities in Node-RED, while also demonstrating a variety of methods to visualize the results in a dashboard. There are 4 main objectives of the Node-RED OPC Client Dashboard application. They are: 1. Browse hierarchical server address space structure & display on a dashboard 2. Read OPC UA values from various namespaces, showing a variety of datatypes and different ways they can be visualized 3. Write OPC UA values back to the OPC UA server directly from the OPC UA Client dashboard 4. Read alarms & events from the OPC UA Server and display them on the dashboard Rather than building the flow step-by-step, the flow source code will be presented for each objective, and a the flow will be explained so that it is understood what is happening in each section of code. ## Browse Hierarchical Server Address Space Structure With OPC UA Browser Node :cta-image{alt="Wenco deploys new dashboard pages in days with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/wenco-book-demo.png"} The first flow will browse the hierarchical OPC UA Server address space structure and display it on the dashboard. ![image-20230727-085611.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/image-20230727-085611.png){dataZoomable=""} You can import this flow into Node-RED using the code below: ::render-flow ```json [{"id":"ca62be3e01388319","type":"group","z":"5b972161c4e0464e","name":"Browse Hierarchical Address Space Structure & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["6b17b2da2b942bb4","61797eccf2785257","4d92d940177b6ee3","68a113d5893b7c01","d0c969b6a59fac3a","639da01fc957e547","29437ca7222d9a64","49983d5da0958bf2","49040d0cf1144f0a","e7c55f412ef86543","de21b7ad98a05833","2d56e9a431c21a3b","ac95bd0e2b304eec","6fdabcc2950ccf4e","1c49fa5142d2cf17","335878527020598c","7b208f2e8cba6205","52dd2e5dcddad58f","a5acdccfd2033aec","157322c9c360446d","78a012e5db377fd9"],"x":94,"y":139,"w":1172,"h":422},{"id":"6b17b2da2b942bb4","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":280,"wires":[["4d92d940177b6ee3","d0c969b6a59fac3a","639da01fc957e547"]]},{"id":"61797eccf2785257","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get Base Folder Structure","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.3","topic":"","payload":"","payloadType":"date","x":280,"y":280,"wires":[["6b17b2da2b942bb4"]]},{"id":"4d92d940177b6ee3","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Simulation Folder","rules":[{"t":"set","p":"Objects.Simulation.nodeId","pt":"flow","to":"payload[2].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[2].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":220,"wires":[["335878527020598c"]]},{"id":"68a113d5893b7c01","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Display on Dashboard","info":"","x":1140,"y":180,"wires":[]},{"id":"d0c969b6a59fac3a","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"MyObjects Folder","rules":[{"t":"set","p":"Objects.MyObjects.nodeId","pt":"flow","to":"payload[4].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[4].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":340,"wires":[["52dd2e5dcddad58f"]]},{"id":"639da01fc957e547","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"StaticData Folder","rules":[{"t":"set","p":"Objects.StaticData.nodeId","pt":"flow","to":"payload[3].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[3].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":280,"wires":[["7b208f2e8cba6205"]]},{"id":"29437ca7222d9a64","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":440,"wires":[["49040d0cf1144f0a","e7c55f412ef86543"]]},{"id":"49983d5da0958bf2","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get StaticData Folder Structure","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.nodeId","vt":"flow"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.3","topic":"","payload":"","payloadType":"date","x":270,"y":440,"wires":[["29437ca7222d9a64"]]},{"id":"49040d0cf1144f0a","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"AnalogItemArrays Folder","rules":[{"t":"set","p":"Objects.StaticData.AnalogItemArrays.nodeId","pt":"flow","to":"payload[1].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[1].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":460,"wires":[["157322c9c360446d"]]},{"id":"e7c55f412ef86543","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"StaticArrayVariables Folder","rules":[{"t":"set","p":"Objects.StaticData.StaticArrayVariables.nodeId","pt":"flow","to":"payload[6].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[6].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":400,"wires":[["a5acdccfd2033aec"]]},{"id":"de21b7ad98a05833","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"MyDevice Object","rules":[{"t":"set","p":"Objects.MyObjects.MyDevice.nodeId","pt":"flow","to":"payload[0].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[0].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":520,"wires":[["78a012e5db377fd9"]]},{"id":"2d56e9a431c21a3b","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get MyObjects Object Structure","props":[{"p":"payload"},{"p":"topic","v":"Objects.MyObjects.nodeId","vt":"flow"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","payload":"","payloadType":"date","x":270,"y":520,"wires":[["ac95bd0e2b304eec"]]},{"id":"ac95bd0e2b304eec","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":520,"wires":[["de21b7ad98a05833"]]},{"id":"6fdabcc2950ccf4e","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Store & Parse nodeId & browseName","info":"","x":850,"y":180,"wires":[]},{"id":"1c49fa5142d2cf17","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Global Address Space Folder Browse","info":"","x":410,"y":220,"wires":[]},{"id":"335878527020598c","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"Simulation","order":1,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":220,"wires":[[]]},{"id":"7b208f2e8cba6205","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"StaticData","order":2,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":280,"wires":[[]]},{"id":"52dd2e5dcddad58f","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"MyObjects","order":5,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":340,"wires":[[]]},{"id":"a5acdccfd2033aec","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"StaticArrayVariables","order":3,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1140,"y":400,"wires":[[]]},{"id":"157322c9c360446d","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"AnalogItemArrays","order":4,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1130,"y":460,"wires":[[]]},{"id":"78a012e5db377fd9","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"MyDevice","order":6,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1100,"y":520,"wires":[[]]},{"id":"53f4394dbf12c6b7","type":"OpcUa-Endpoint","endpoint":"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer","secpol":"None","secmode":"None","none":true,"login":false,"usercert":false,"usercertificate":"","userprivatekey":""},{"id":"ef9998baf5f61e8a","type":"ui-group","name":" Address Space Folder Structure","page":"44d3feb2a1143d7b","width":"2","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"44d3feb2a1143d7b","type":"ui-page","name":"OPC UA","ui":"5355e0c476f9da3b","path":"/opcua","icon":"home","layout":"grid","theme":"61eee6fc60281b9b","order":1,"className":"","visible":"true","disabled":"false"},{"id":"5355e0c476f9da3b","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default"},{"id":"61eee6fc60281b9b","type":"ui-theme","name":"Default Theme","colors":{"surface":"#0094ce","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}] ``` :: To understand what is going on in this flow, we must refer back to the OPC UA Simulation Server `Address Space` tab. When we browse the OPC Server base folder structure in Node-RED, we will be browsing everything included under the `Objects` tree. ![address-space.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/address-space.png){dataZoomable=""} In our flow, we get the base folder structure by using an OPC-UA Browser node, as shown, with an endpoint that points to our OPC UA Server endpoint url we grabbed earlier in this article. It is also worth noting we leave the `Topic` blank. By doing this, we will browse the entire folder structure by default. ![image-20230727-090252.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/image-20230727-090252.png){dataZoomable=""} The configuration of the endpoint properties includes no security credentials, as shown below. ![endpoint-configure.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/endpoint-configure.png){dataZoomable=""} Using the output of a debug node, we get from the OPC UA Browser yield a payload with an array of 5 objects. ![address-debug.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/address-debug.png){dataZoomable=""} Each object returned represents the 5 objects that are in our OPC UA Server Objects tree. ![browse-payload-1.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/browse-payload-1.png){dataZoomable=""} However, of those 5 objects, only 3 of them are folders that contain actual OPC values. `MyObjects`, `Simulation`, and `StaticData`. We can ignore `Aliases` and `Server`. ![address-space-folders-only.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/address-space-folders-only.png){dataZoomable=""} So looking deeper into the payload of our global browse from the `OPC UA Browser node`, we can drill down into the details and see how they correlate with the folders in the server. ![browse-node.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/browse-node.png){dataZoomable=""} As shown above, element 2 in the array returned from the global browse corresponds to the `Simulation` folder. And we are interested in two important values in this data-structure - the `NodeId`, which is topic an OPC Client uses to point specific OPC values, and the `browseName`, which is the name we see visually when we try to identify an OPC topic. We can now use this logic to parse out this useful information using a change node. ![simulation-folder.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/simulation-folder.png){dataZoomable=""} This change node is grabbing the `nodeId` and `browseName` . The `nodeId` is stored in a context variable for later use, while the `browseName` is used as the payload to be displayed on our dashboard. The rest of the flow follows this same pattern, to end up with a folder structure that we can display on our dashboard that matches the structure on our OPC Server - note - to make the flow more manageable, not all browsable folders were included in the dashboard, as this flow is just meant to serve as an example, rather than be a 1:1 copy of everything in the server. If you deploy the flow and pull up the dashboard, it results in the following output - ![address-space-dashboard.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/address-space-dashboard.png){dataZoomable=""} Showing side-by-side with the server, you can see that we successfully browsed a portion of the address space and displayed the values on the dashboard. Admittedly, a lot of work for not much pay-off, but it’s a worthwhile exercise in understanding how to browse topics using the `OPC UA Browser` node. The browser node is best used for reading OPC UA values, which will be covered next. ## Read OPC UA Values Using OPC UA Browser Node The next set of flows read OPC UA values from the server and displays them on the dashboard. ![read-opc-values.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/read-opc-values.png){dataZoomable=""} You can import these flows into Node-RED using the code below: ::render-flow ```json [{"id":"ca62be3e01388319","type":"group","z":"5b972161c4e0464e","name":"Browse Hierarchical Address Space Structure & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["6b17b2da2b942bb4","61797eccf2785257","4d92d940177b6ee3","68a113d5893b7c01","d0c969b6a59fac3a","639da01fc957e547","29437ca7222d9a64","49983d5da0958bf2","49040d0cf1144f0a","e7c55f412ef86543","de21b7ad98a05833","2d56e9a431c21a3b","ac95bd0e2b304eec","6fdabcc2950ccf4e","1c49fa5142d2cf17","335878527020598c","7b208f2e8cba6205","52dd2e5dcddad58f","a5acdccfd2033aec","157322c9c360446d","78a012e5db377fd9"],"x":94,"y":139,"w":1172,"h":422},{"id":"6b17b2da2b942bb4","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":280,"wires":[["4d92d940177b6ee3","d0c969b6a59fac3a","639da01fc957e547"]]},{"id":"61797eccf2785257","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get Base Folder Structure","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.3","topic":"","payload":"","payloadType":"date","x":280,"y":280,"wires":[["6b17b2da2b942bb4"]]},{"id":"4d92d940177b6ee3","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Simulation Folder","rules":[{"t":"set","p":"Objects.Simulation.nodeId","pt":"flow","to":"payload[2].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[2].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":220,"wires":[["335878527020598c"]]},{"id":"68a113d5893b7c01","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Display on Dashboard","info":"","x":1140,"y":180,"wires":[]},{"id":"d0c969b6a59fac3a","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"MyObjects Folder","rules":[{"t":"set","p":"Objects.MyObjects.nodeId","pt":"flow","to":"payload[4].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[4].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":340,"wires":[["52dd2e5dcddad58f"]]},{"id":"639da01fc957e547","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"StaticData Folder","rules":[{"t":"set","p":"Objects.StaticData.nodeId","pt":"flow","to":"payload[3].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[3].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":280,"wires":[["7b208f2e8cba6205"]]},{"id":"29437ca7222d9a64","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":440,"wires":[["49040d0cf1144f0a","e7c55f412ef86543"]]},{"id":"49983d5da0958bf2","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get StaticData Folder Structure","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.nodeId","vt":"flow"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.3","topic":"","payload":"","payloadType":"date","x":270,"y":440,"wires":[["29437ca7222d9a64"]]},{"id":"49040d0cf1144f0a","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"AnalogItemArrays Folder","rules":[{"t":"set","p":"Objects.StaticData.AnalogItemArrays.nodeId","pt":"flow","to":"payload[1].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[1].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":460,"wires":[["157322c9c360446d"]]},{"id":"e7c55f412ef86543","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"StaticArrayVariables Folder","rules":[{"t":"set","p":"Objects.StaticData.StaticArrayVariables.nodeId","pt":"flow","to":"payload[6].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[6].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":400,"wires":[["a5acdccfd2033aec"]]},{"id":"de21b7ad98a05833","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"MyDevice Object","rules":[{"t":"set","p":"Objects.MyObjects.MyDevice.nodeId","pt":"flow","to":"payload[0].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[0].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":520,"wires":[["78a012e5db377fd9"]]},{"id":"2d56e9a431c21a3b","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get MyObjects Object Structure","props":[{"p":"payload"},{"p":"topic","v":"Objects.MyObjects.nodeId","vt":"flow"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","payload":"","payloadType":"date","x":270,"y":520,"wires":[["ac95bd0e2b304eec"]]},{"id":"ac95bd0e2b304eec","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":520,"wires":[["de21b7ad98a05833"]]},{"id":"6fdabcc2950ccf4e","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Store & Parse nodeId & browseName","info":"","x":850,"y":180,"wires":[]},{"id":"1c49fa5142d2cf17","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Global Address Space Folder Browse","info":"","x":410,"y":220,"wires":[]},{"id":"335878527020598c","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"Simulation","order":1,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":220,"wires":[[]]},{"id":"7b208f2e8cba6205","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"StaticData","order":2,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":280,"wires":[[]]},{"id":"52dd2e5dcddad58f","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"MyObjects","order":5,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":340,"wires":[[]]},{"id":"a5acdccfd2033aec","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"StaticArrayVariables","order":3,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1140,"y":400,"wires":[[]]},{"id":"157322c9c360446d","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"AnalogItemArrays","order":4,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1130,"y":460,"wires":[[]]},{"id":"78a012e5db377fd9","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"MyDevice","order":6,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1100,"y":520,"wires":[[]]},{"id":"53f4394dbf12c6b7","type":"OpcUa-Endpoint","endpoint":"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer","secpol":"None","secmode":"None","none":true,"login":false,"usercert":false,"usercertificate":"","userprivatekey":""},{"id":"ef9998baf5f61e8a","type":"ui-group","name":" Address Space Folder Structure","page":"44d3feb2a1143d7b","width":"2","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"44d3feb2a1143d7b","type":"ui-page","name":"OPC UA","ui":"5355e0c476f9da3b","path":"/opcua","icon":"home","layout":"grid","theme":"61eee6fc60281b9b","order":1,"className":"","visible":"true","disabled":"false"},{"id":"5355e0c476f9da3b","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default"},{"id":"61eee6fc60281b9b","type":"ui-theme","name":"Default Theme","colors":{"surface":"#0094ce","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"8557072f05e4bda0","type":"group","z":"5b972161c4e0464e","name":"Read Simulation Values & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["9659d40ac9063764","9f5b597ec8179fb4","a8d919f497fcff04","13f5c98b7fd5f5da","ec5dca5eb9d4971b","1780cb86597d3c67","1a2fcac87247cda4","4d9b758e39555124","da468bc150517fa6","82aa12173dd7bbca","57d8777e34b55b7b","10877909d1daf6fe","c4d4a3b0df372e4c","b0cf511f824f2a86","f2efc6b419414c9a"],"x":94,"y":599,"w":1372,"h":302},{"id":"9659d40ac9063764","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"8557072f05e4bda0","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":570,"y":760,"wires":[["ec5dca5eb9d4971b"]]},{"id":"9f5b597ec8179fb4","type":"inject","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Update Simulation Values @ 1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.Simulation.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":300,"y":760,"wires":[["9659d40ac9063764"]]},{"id":"a8d919f497fcff04","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Read Simulation Values","info":"","x":460,"y":720,"wires":[]},{"id":"13f5c98b7fd5f5da","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Counter Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[1].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1070,"y":680,"wires":[["10877909d1daf6fe"]]},{"id":"ec5dca5eb9d4971b","type":"switch","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":790,"y":760,"wires":[["13f5c98b7fd5f5da","1780cb86597d3c67","1a2fcac87247cda4","4d9b758e39555124"]]},{"id":"1780cb86597d3c67","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Random Number Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[2].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":740,"wires":[["c4d4a3b0df372e4c"]]},{"id":"1a2fcac87247cda4","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Sawtooth Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[3].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":800,"wires":[["b0cf511f824f2a86"]]},{"id":"4d9b758e39555124","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Sawtooth Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[4].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":860,"wires":[["f2efc6b419414c9a"]]},{"id":"da468bc150517fa6","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Discard Empty Datasets","info":"","x":780,"y":720,"wires":[]},{"id":"82aa12173dd7bbca","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Parse Simulation Values","info":"","x":1070,"y":640,"wires":[]},{"id":"57d8777e34b55b7b","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Display on Dashboard","info":"","x":1340,"y":640,"wires":[]},{"id":"10877909d1daf6fe","type":"ui-gauge","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Counter","group":"af263064820fb7d0","order":0,"width":3,"height":3,"gtype":"gauge-half","gstyle":"needle","title":"gauge","units":"units","icon":"","prefix":"","suffix":"","segments":[{"from":"0","color":"#5cd65c"},{"from":"15","color":"#ffc800"},{"from":"30","color":"#ea5353"}],"min":0,"max":"30","sizeThickness":16,"sizeGap":4,"sizeKeyThickness":8,"styleRounded":true,"styleGlow":false,"className":"","x":1320,"y":680,"wires":[]},{"id":"c4d4a3b0df372e4c","type":"ui-text","z":"5b972161c4e0464e","g":"8557072f05e4bda0","group":"af263064820fb7d0","order":0,"width":0,"height":0,"name":"Random Number","label":"Random Number","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1350,"y":740,"wires":[]},{"id":"b0cf511f824f2a86","type":"ui-chart","z":"5b972161c4e0464e","g":"8557072f05e4bda0","group":"af263064820fb7d0","name":"","label":"Sawtooth","order":9007199254740991,"chartType":"line","category":"Sawtooth","categoryType":"str","xAxisProperty":"","xAxisPropertyType":"msg","xAxisType":"time","yAxisProperty":"","ymin":"","ymax":"","action":"append","pointShape":"line","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"60","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":"3","height":"4","className":"","x":1320,"y":800,"wires":[[]]},{"id":"f2efc6b419414c9a","type":"ui-chart","z":"5b972161c4e0464e","g":"8557072f05e4bda0","group":"af263064820fb7d0","name":"","label":"Sinusoid","order":9007199254740991,"chartType":"line","category":"Sawtooth","categoryType":"str","xAxisProperty":"","xAxisPropertyType":"msg","xAxisType":"time","yAxisProperty":"","ymin":"","ymax":"","action":"append","pointShape":"line","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"60","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":"3","height":"4","className":"","x":1320,"y":860,"wires":[[]]},{"id":"af263064820fb7d0","type":"ui-group","name":"Simulation values","page":"44d3feb2a1143d7b","width":"3","height":"1","order":2,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"5afdbddf71507886","type":"group","z":"5b972161c4e0464e","name":"Read StaticData Values & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["e998aa804042128b","6c9b7d4d195a1e9a","cd097b744d0ec625","18d21607c87ab153","7b5143c4960f92a1","0625b0cf6f546a4a","9d899fbb4d1648b3","6e1edc31687dde54","051e1f282076fed2","de2a1c3e380f743b","c74606c48ccf5a40","053bda13f2a2eabe","277dcf430dc86996","d708e6264cec0070"],"x":84,"y":939,"w":1382,"h":202},{"id":"e998aa804042128b","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"5afdbddf71507886","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":630,"y":1020,"wires":[["7b5143c4960f92a1"]]},{"id":"6c9b7d4d195a1e9a","type":"inject","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Update AnalogItemArrays Values @ 1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.AnalogItemArrays.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":320,"y":1020,"wires":[["e998aa804042128b"]]},{"id":"cd097b744d0ec625","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Read StaticData Values","info":"","x":520,"y":980,"wires":[]},{"id":"18d21607c87ab153","type":"change","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Get ByteAnalogItemArray Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"$string(payload[0].item.value)\t","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1070,"y":1020,"wires":[["277dcf430dc86996"]]},{"id":"7b5143c4960f92a1","type":"switch","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":830,"y":1020,"wires":[["18d21607c87ab153"]]},{"id":"0625b0cf6f546a4a","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"5afdbddf71507886","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":630,"y":1100,"wires":[["051e1f282076fed2"]]},{"id":"9d899fbb4d1648b3","type":"inject","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Update StaticArrayVariables Values @1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.StaticArrayVariables.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":320,"y":1100,"wires":[["0625b0cf6f546a4a"]]},{"id":"6e1edc31687dde54","type":"change","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Get BooleanArray Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[0].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1050,"y":1100,"wires":[["d708e6264cec0070"]]},{"id":"051e1f282076fed2","type":"switch","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":830,"y":1100,"wires":[["6e1edc31687dde54"]]},{"id":"de2a1c3e380f743b","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Discard Empty Datasets","info":"","x":820,"y":980,"wires":[]},{"id":"c74606c48ccf5a40","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Parse StaticData Values","info":"","x":1070,"y":980,"wires":[]},{"id":"053bda13f2a2eabe","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Display on Dashboard","info":"","x":1340,"y":980,"wires":[]},{"id":"277dcf430dc86996","type":"ui-text","z":"5b972161c4e0464e","g":"5afdbddf71507886","group":"3d4f386e812e8b5f","order":0,"width":0,"height":0,"name":"","label":"ByteAnalogItemArray","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1340,"y":1020,"wires":[]},{"id":"d708e6264cec0070","type":"ui-text","z":"5b972161c4e0464e","g":"5afdbddf71507886","group":"3d4f386e812e8b5f","order":0,"width":0,"height":0,"name":"","label":"BooleanArray","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1320,"y":1100,"wires":[]},{"id":"3d4f386e812e8b5f","type":"ui-group","name":"StaticData Values","page":"44d3feb2a1143d7b","width":"4","height":"1","order":3,"showTitle":true,"className":"","visible":"true","disabled":"false"}] ``` :: The values are derived from the `nodeId` values we stored in memory in our previous flow, via our `change` nodes in the previous flow. ![flow-context-nodeid.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/flow-context-nodeid.png){dataZoomable=""} As stated earlier, you reference a OPC UA topic by its `nodeId`. So we will use these node IDs to read actual values from our OPC nodes. In our first flow, we want to read the values in the `Simulation` folder at a 1 second interval. So we use an `inject` node with a `msg.topic` that references the `nodeId` corresponding to the `Simulation` folder. ![simulation-injection.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/simulation-injection.png){dataZoomable=""} That `msg.topic` tells the `OPC UA Browser` node what `nodeId` to browse. If we look at the debug output of the browser `msg.payload`, we can see that it produces an array of 7 objects, and an empty set array. ![simulation-debug.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/simulation-debug.png){dataZoomable=""} If we allow that empty array to be passed, that means all values will be reset to 0 on each read. So to prevent that from happening, we use a `switch` node to filter out the empty set. ![empty-check.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/empty-check.png){dataZoomable=""} Now only non-empty payloads will be passed, preventing the values being reset to 0 on each read. Now we can actually read the values. To do this, we use a `change` node again, referencing the non-empty payload and drilling down to the `value` that corresponds to the `name` of the node we want to read. In this case, we’re getting the value of the node `Counter` located in the `Simulation` folder. ![get-counter-value.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/get-counter-value.png){dataZoomable=""} Going back to our OPC Server, we can see that exactly where that value is derived below - ![sim-counter-server.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/sim-counter-server.png){dataZoomable=""} Now we add a `gauge` dashboard node to visualize the counter on the dashboard. In the OPC Server, it is shown that the counter increments in a range of 0-30 in 1 count increments. ![counter-properties.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/counter-propertie.png){dataZoomable=""} Now that we’ve gone through the full process of reading an OPC UA value and displaying it on the dashboard, we can apply the same logic other values published by the OPC UA Server, which are repeated in the remaining parts of the flow. The end result on the dashboard now looks like this - :video{ariaLabel="Video showing the OPC UA dashboard reading live values from the server" autoPlay="true" height="450" loop="true" muted="true" playsInline="true" preload="none" width="800"} ## Write OPC UA Values To Server Using OpcUa-Item and Opc-Ua-Client Nodes The next flow writes OPC UA values to the server using dashboard UI elements. ![write-mydevice.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/write-mydevice.png){dataZoomable=""} You can import this flow into Node-RED using the code below: ::render-flow ```json [{"id":"3de6c861611c3afa","type":"group","z":"5b972161c4e0464e","name":"Write Mydevices values to OPC UA Server","style":{"label":true,"color":"#000000"},"nodes":["a66583d91b581cd8","3e8cb6e199012155","9fa33d1c9c621611","fb7f57b4da5883ae","9c5ff104eb9c8b10","77bcb828bec95336","afa83dbb46449d4a","fa08f0ed04296363","9f591797b56c565d"],"x":94,"y":1439,"w":792,"h":182},{"id":"a66583d91b581cd8","type":"OpcUa-Item","z":"5b972161c4e0464e","g":"3de6c861611c3afa","item":"ns=6;s=MySwitch","datatype":"Boolean","value":"","name":"Toggle MySwitch","x":470,"y":1520,"wires":[["3e8cb6e199012155"]]},{"id":"3e8cb6e199012155","type":"OpcUa-Client","z":"5b972161c4e0464e","g":"3de6c861611c3afa","endpoint":"53f4394dbf12c6b7","action":"write","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","securitymode":"None","securitypolicy":"None","useTransport":false,"maxChunkCount":1,"maxMessageSize":8192,"receiveBufferSize":8192,"sendBufferSize":8192,"name":"Write MySwitch","x":720,"y":1520,"wires":[[],[]]},{"id":"9fa33d1c9c621611","type":"comment","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"Dashboard Input","info":"","x":200,"y":1480,"wires":[]},{"id":"fb7f57b4da5883ae","type":"OpcUa-Item","z":"5b972161c4e0464e","g":"3de6c861611c3afa","item":"ns=6;s=MyLevel","datatype":"Double","value":"","name":"Modify MyLevel","x":460,"y":1580,"wires":[["9c5ff104eb9c8b10"]]},{"id":"9c5ff104eb9c8b10","type":"OpcUa-Client","z":"5b972161c4e0464e","g":"3de6c861611c3afa","endpoint":"53f4394dbf12c6b7","action":"write","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","securitymode":"None","securitypolicy":"None","useTransport":false,"maxChunkCount":1,"maxMessageSize":8192,"receiveBufferSize":8192,"sendBufferSize":8192,"name":"Write MyLevel","x":720,"y":1580,"wires":[[],[]]},{"id":"77bcb828bec95336","type":"comment","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"Call OPC UA Item","info":"","x":470,"y":1480,"wires":[]},{"id":"afa83dbb46449d4a","type":"comment","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"Write OPC UA Item to Client","info":"","x":740,"y":1480,"wires":[]},{"id":"fa08f0ed04296363","type":"ui-switch","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"","label":"Toggle MySwitch","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"passthru":false,"topic":"topic","topicType":"msg","style":"","className":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":210,"y":1520,"wires":[["a66583d91b581cd8"]]},{"id":"9f591797b56c565d","type":"ui-slider","z":"5b972161c4e0464e","g":"3de6c861611c3afa","group":"ec0ecb26fde8db3e","name":"","label":"Modify MyLevel","tooltip":"","order":0,"width":0,"height":0,"passthru":false,"outs":"all","topic":"topic","topicType":"msg","thumbLabel":true,"min":"0","max":"100","step":1,"className":"","x":200,"y":1580,"wires":[["fb7f57b4da5883ae"]]},{"id":"53f4394dbf12c6b7","type":"OpcUa-Endpoint","endpoint":"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer","secpol":"None","secmode":"None","none":true,"login":false,"usercert":false,"usercertificate":"","userprivatekey":""},{"id":"ec0ecb26fde8db3e","type":"ui-group","name":"MyDevice Status & Control","page":"44d3feb2a1143d7b","width":"3","height":"1","order":4,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"44d3feb2a1143d7b","type":"ui-page","name":"OPC UA","ui":"5355e0c476f9da3b","path":"/opcua","icon":"home","layout":"grid","theme":"61eee6fc60281b9b","order":1,"className":"","visible":"true","disabled":"false"},{"id":"5355e0c476f9da3b","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default"},{"id":"61eee6fc60281b9b","type":"ui-theme","name":"Default Theme","colors":{"surface":"#0094ce","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}] ``` :: We have two values to write, a boolean value corresponding to the node object `MySwitch`, and an integer value corresponding to the object `MyLevel`. Therefore, we will use a toggle switch to toggle the `MySwitch`, and a slider to modify `MyLevel`. There’s no need to modify the toggle switch properties, other than giving it a name. The slider needs to have the range modified to match the range of the level, which is 0-100%. ![level-range.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/level-range.png){dataZoomable=""} For the `OpcUa-Item` nodes, copy the `NodeId` corresponding to each device, ![copy-node-id.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/copy-node-id.png){dataZoomable=""} and paste it into `OpcUa-Item` node. You must also ensure the data-type matches with the value you’re writing to. ![opcua-item.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/opcua-item.png){dataZoomable=""} The `Opc-Ua-Client` needs to have an endpoint and the action changed to `WRITE`. ![client-node.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/client-node.png){dataZoomable=""} The process is the same for `MySwitch` and `MyLevel`, the only difference being what `NodeId` is referenced in the `OpcUa-Item` node. When deployed, you can confirm values are being written to from the client to the server from the dashboard. :video{ariaLabel="Video showing values being written from the client to the server on the dashboard" autoPlay="true" height="390" loop="true" muted="true" playsInline="true" preload="none" width="800"} ## Read Alarms & Events from OPC UA Server Using OpcUa-Event and Opc-Ua-Client Nodes Our last flow we’ll show how to read OPC UA Alarms & Events. ![opc-event-flow.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/opc-event-flow.png){dataZoomable=""} You can import this flow into Node-RED using the code below: ::render-flow ```json [{"id":"a6e9abacd0bdf3b6","type":"group","z":"5b972161c4e0464e","name":"Read Alarms & Events From OPC UA Server","style":{"label":true,"color":"#000000"},"nodes":["90fb4ca64a642edf","b76f64786bc681c3","71e24b671bc03fb8","c7438df35b506470","c7e8919b636cb51d","5952b86dae22b056","04992b24a3836f19","325068cb935cd6d1","5b4d1bd8b342fc05","ba1ea89438335cb8","d662d662c5ccb9c1","1e3956200997581f","0b8ac86e5e4f9f8d","62b2e14ce0429eef"],"x":94,"y":1679,"w":1352,"h":282},{"id":"90fb4ca64a642edf","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Call OPC UA Item","info":"","x":470,"y":1820,"wires":[]},{"id":"b76f64786bc681c3","type":"OpcUa-Event","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","root":"ns=6;s=MyLevel.Alarm","activatecustomevent":false,"eventtype":"i=2041","customeventtype":"","name":"MyLevel Alarms","x":500,"y":1860,"wires":[["c7438df35b506470"]]},{"id":"71e24b671bc03fb8","type":"inject","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Trigger Alarm Event Capture","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":1860,"wires":[["b76f64786bc681c3"]]},{"id":"c7438df35b506470","type":"OpcUa-Client","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","endpoint":"53f4394dbf12c6b7","action":"events","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","securitymode":"None","securitypolicy":"None","useTransport":false,"maxChunkCount":1,"maxMessageSize":8192,"receiveBufferSize":8192,"sendBufferSize":8192,"name":"Get MyLevel Events","x":720,"y":1860,"wires":[["c7e8919b636cb51d","5952b86dae22b056","04992b24a3836f19"],[]]},{"id":"c7e8919b636cb51d","type":"change","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Event Text","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Message.text","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1800,"wires":[["d662d662c5ccb9c1","1e3956200997581f"]]},{"id":"5952b86dae22b056","type":"change","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Event Time","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Time","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1860,"wires":[["0b8ac86e5e4f9f8d"]]},{"id":"04992b24a3836f19","type":"change","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Event Severity","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Severity","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":1920,"wires":[["62b2e14ce0429eef"]]},{"id":"325068cb935cd6d1","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Parse Event Dataset","info":"","x":990,"y":1760,"wires":[]},{"id":"5b4d1bd8b342fc05","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Get OPC Events from Client","info":"","x":720,"y":1820,"wires":[]},{"id":"ba1ea89438335cb8","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Display Events on Dashboard","info":"","x":1240,"y":1720,"wires":[]},{"id":"d662d662c5ccb9c1","type":"ui-notification","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","ui":"5355e0c476f9da3b","position":"center center","colorDefault":true,"color":"#000000","displayTime":"3","showCountdown":true,"outputs":1,"allowDismiss":true,"dismissText":"Close","raw":false,"className":"","name":"Event Notification","x":1230,"y":1800,"wires":[[]]},{"id":"1e3956200997581f","type":"ui-text","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"name":"","label":"Latest MyLevel Event","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1240,"y":1840,"wires":[]},{"id":"0b8ac86e5e4f9f8d","type":"ui-text","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"name":"","label":"Latest MyLevel Event Timestamp","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1280,"y":1880,"wires":[]},{"id":"62b2e14ce0429eef","type":"ui-text","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"name":"","label":"Latest MyLevel Event Severity","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1270,"y":1920,"wires":[]},{"id":"53f4394dbf12c6b7","type":"OpcUa-Endpoint","endpoint":"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer","secpol":"None","secmode":"None","none":true,"login":false,"usercert":false,"usercertificate":"","userprivatekey":""},{"id":"5355e0c476f9da3b","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default"},{"id":"ec0ecb26fde8db3e","type":"ui-group","name":"MyDevice Status & Control","page":"44d3feb2a1143d7b","width":"3","height":"1","order":4,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"44d3feb2a1143d7b","type":"ui-page","name":"OPC UA","ui":"5355e0c476f9da3b","path":"/opcua","icon":"home","layout":"grid","theme":"61eee6fc60281b9b","order":1,"className":"","visible":"true","disabled":"false"},{"id":"61eee6fc60281b9b","type":"ui-theme","name":"Default Theme","colors":{"surface":"#0094ce","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}] ``` :: We use an inject node to trigger the `OpcUa-Event` node. In the properties of the event node, we get the `NodeId` from the `MyLevelAlarm` event from the OPC Server - ![mylevel-event.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/mylevel-event.png){dataZoomable=""} And copy that `NodeId` into the `OpcUa-Event` node. Event type will be `BaseEvent (all)`. ![event-node-properties.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/event-node-properties.png){dataZoomable=""} In the `Opc-Ua-Client` node, we set the `Action` to `EVENTS`. ![client-events.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/client-events.png){dataZoomable=""} If we stick a debug node on the output of the client event, we can see how the OPC Server annunciates events. ![event-debug.png](https://flowfuse.com/blog/2023/07/images/opc-ua-3/event-debug.png){dataZoomable=""} Every time `MyLevel` exceeds certain thresholds (10%, 30%, 70% and 90%) it will flag a `Level Exceeded` alarm. The event is timestamped and assigned a severity level, which we will record and put onto the dashboard. To make things simple, we’ll only track the last event. But in a production system, you’d likely want to store these events in a relational database (historian) to keep an alarm history. We’ll also include a notification pop-up when an alarm occurs to notify someone monitoring the dashboard a new alarm has occurred. Adding alarms and events to our dashboard creates the following result - :video{ariaLabel="Video showing alarms and events displayed on the dashboard" autoPlay="true" height="450" loop="true" muted="true" playsInline="true" preload="none" width="800"} ## Using FlowFuse to Enhance Your Node-RED Application: Security, Scalability, and Robustness So, you've successfully built your Node-RED application, congratulations! But now, how do you ensure its security, scalability, and ease of collaboration? What if you want to invite your team to work on the application simultaneously or access it remotely? Enter [FlowFuse](https://flowfuse.com), 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](https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/). For additional resources, visit our [Node-RED Learning Resources section](https://flowfuse.com/node-red/core-nodes/), where you can explore integrations with different protocols, messaging services, databases, hardware, and much more. ## Conclusion In this final article, we went over building a OPC UA Client dashboard that can browse the address space, read values from an OPC Server, write values to an OPC Server, and get events from an OPC Server. This flow provides examples that can serve as a foundation for an interactive OPC Client application built in Node-RED. This now concludes the OPC UA Series. full source code for this project - ::render-flow ```json [{"id":"ca62be3e01388319","type":"group","z":"5b972161c4e0464e","name":"Browse Hierarchical Address Space Structure & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["6b17b2da2b942bb4","61797eccf2785257","4d92d940177b6ee3","68a113d5893b7c01","d0c969b6a59fac3a","639da01fc957e547","29437ca7222d9a64","49983d5da0958bf2","49040d0cf1144f0a","e7c55f412ef86543","de21b7ad98a05833","2d56e9a431c21a3b","ac95bd0e2b304eec","6fdabcc2950ccf4e","1c49fa5142d2cf17","335878527020598c","7b208f2e8cba6205","52dd2e5dcddad58f","a5acdccfd2033aec","157322c9c360446d","78a012e5db377fd9"],"x":94,"y":139,"w":1172,"h":422},{"id":"6b17b2da2b942bb4","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":280,"wires":[["4d92d940177b6ee3","d0c969b6a59fac3a","639da01fc957e547"]]},{"id":"61797eccf2785257","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get Base Folder Structure","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.3","topic":"","payload":"","payloadType":"date","x":280,"y":280,"wires":[["6b17b2da2b942bb4"]]},{"id":"4d92d940177b6ee3","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Simulation Folder","rules":[{"t":"set","p":"Objects.Simulation.nodeId","pt":"flow","to":"payload[2].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[2].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":220,"wires":[["335878527020598c"]]},{"id":"68a113d5893b7c01","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Display on Dashboard","info":"","x":1140,"y":180,"wires":[]},{"id":"d0c969b6a59fac3a","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"MyObjects Folder","rules":[{"t":"set","p":"Objects.MyObjects.nodeId","pt":"flow","to":"payload[4].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[4].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":340,"wires":[["52dd2e5dcddad58f"]]},{"id":"639da01fc957e547","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"StaticData Folder","rules":[{"t":"set","p":"Objects.StaticData.nodeId","pt":"flow","to":"payload[3].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[3].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":280,"wires":[["7b208f2e8cba6205"]]},{"id":"29437ca7222d9a64","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":440,"wires":[["49040d0cf1144f0a","e7c55f412ef86543"]]},{"id":"49983d5da0958bf2","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get StaticData Folder Structure","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.nodeId","vt":"flow"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.3","topic":"","payload":"","payloadType":"date","x":270,"y":440,"wires":[["29437ca7222d9a64"]]},{"id":"49040d0cf1144f0a","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"AnalogItemArrays Folder","rules":[{"t":"set","p":"Objects.StaticData.AnalogItemArrays.nodeId","pt":"flow","to":"payload[1].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[1].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":460,"wires":[["157322c9c360446d"]]},{"id":"e7c55f412ef86543","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"StaticArrayVariables Folder","rules":[{"t":"set","p":"Objects.StaticData.StaticArrayVariables.nodeId","pt":"flow","to":"payload[6].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[6].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":860,"y":400,"wires":[["a5acdccfd2033aec"]]},{"id":"de21b7ad98a05833","type":"change","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"MyDevice Object","rules":[{"t":"set","p":"Objects.MyObjects.MyDevice.nodeId","pt":"flow","to":"payload[0].item.nodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload[0].item.browseName.name","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":520,"wires":[["78a012e5db377fd9"]]},{"id":"2d56e9a431c21a3b","type":"inject","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Get MyObjects Object Structure","props":[{"p":"payload"},{"p":"topic","v":"Objects.MyObjects.nodeId","vt":"flow"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","payload":"","payloadType":"date","x":270,"y":520,"wires":[["ac95bd0e2b304eec"]]},{"id":"ac95bd0e2b304eec","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"ca62be3e01388319","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":550,"y":520,"wires":[["de21b7ad98a05833"]]},{"id":"6fdabcc2950ccf4e","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Store & Parse nodeId & browseName","info":"","x":850,"y":180,"wires":[]},{"id":"1c49fa5142d2cf17","type":"comment","z":"5b972161c4e0464e","g":"ca62be3e01388319","name":"Global Address Space Folder Browse","info":"","x":410,"y":220,"wires":[]},{"id":"335878527020598c","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"Simulation","order":1,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":220,"wires":[[]]},{"id":"7b208f2e8cba6205","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"StaticData","order":2,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":280,"wires":[[]]},{"id":"52dd2e5dcddad58f","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"MyObjects","order":5,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1110,"y":340,"wires":[[]]},{"id":"a5acdccfd2033aec","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"StaticArrayVariables","order":3,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1140,"y":400,"wires":[[]]},{"id":"157322c9c360446d","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"AnalogItemArrays","order":4,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1130,"y":460,"wires":[[]]},{"id":"78a012e5db377fd9","type":"ui-template","z":"5b972161c4e0464e","g":"ca62be3e01388319","group":"ef9998baf5f61e8a","page":"","ui":"","name":"MyDevice","order":6,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1100,"y":520,"wires":[[]]},{"id":"53f4394dbf12c6b7","type":"OpcUa-Endpoint","endpoint":"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer","secpol":"None","secmode":"None","none":true,"login":false,"usercert":false,"usercertificate":"","userprivatekey":""},{"id":"ef9998baf5f61e8a","type":"ui-group","name":" Address Space Folder Structure","page":"44d3feb2a1143d7b","width":"2","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"44d3feb2a1143d7b","type":"ui-page","name":"OPC UA","ui":"5355e0c476f9da3b","path":"/opcua","icon":"home","layout":"grid","theme":"61eee6fc60281b9b","order":1,"className":"","visible":"true","disabled":"false"},{"id":"5355e0c476f9da3b","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default"},{"id":"61eee6fc60281b9b","type":"ui-theme","name":"Default Theme","colors":{"surface":"#0094ce","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"8557072f05e4bda0","type":"group","z":"5b972161c4e0464e","name":"Read Simulation Values & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["9659d40ac9063764","9f5b597ec8179fb4","a8d919f497fcff04","13f5c98b7fd5f5da","ec5dca5eb9d4971b","1780cb86597d3c67","1a2fcac87247cda4","4d9b758e39555124","da468bc150517fa6","82aa12173dd7bbca","57d8777e34b55b7b","10877909d1daf6fe","c4d4a3b0df372e4c","b0cf511f824f2a86","f2efc6b419414c9a"],"x":94,"y":599,"w":1372,"h":302},{"id":"9659d40ac9063764","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"8557072f05e4bda0","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":570,"y":760,"wires":[["ec5dca5eb9d4971b"]]},{"id":"9f5b597ec8179fb4","type":"inject","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Update Simulation Values @ 1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.Simulation.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":300,"y":760,"wires":[["9659d40ac9063764"]]},{"id":"a8d919f497fcff04","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Read Simulation Values","info":"","x":460,"y":720,"wires":[]},{"id":"13f5c98b7fd5f5da","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Counter Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[1].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1070,"y":680,"wires":[["10877909d1daf6fe"]]},{"id":"ec5dca5eb9d4971b","type":"switch","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":790,"y":760,"wires":[["13f5c98b7fd5f5da","1780cb86597d3c67","1a2fcac87247cda4","4d9b758e39555124"]]},{"id":"1780cb86597d3c67","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Random Number Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[2].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1100,"y":740,"wires":[["c4d4a3b0df372e4c"]]},{"id":"1a2fcac87247cda4","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Sawtooth Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[3].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":800,"wires":[["b0cf511f824f2a86"]]},{"id":"4d9b758e39555124","type":"change","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Get Sawtooth Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[4].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":860,"wires":[["f2efc6b419414c9a"]]},{"id":"da468bc150517fa6","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Discard Empty Datasets","info":"","x":780,"y":720,"wires":[]},{"id":"82aa12173dd7bbca","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Parse Simulation Values","info":"","x":1070,"y":640,"wires":[]},{"id":"57d8777e34b55b7b","type":"comment","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Display on Dashboard","info":"","x":1340,"y":640,"wires":[]},{"id":"10877909d1daf6fe","type":"ui-gauge","z":"5b972161c4e0464e","g":"8557072f05e4bda0","name":"Counter","group":"af263064820fb7d0","order":0,"width":3,"height":3,"gtype":"gauge-half","gstyle":"needle","title":"gauge","units":"units","icon":"","prefix":"","suffix":"","segments":[{"from":"0","color":"#5cd65c"},{"from":"15","color":"#ffc800"},{"from":"30","color":"#ea5353"}],"min":0,"max":"30","sizeThickness":16,"sizeGap":4,"sizeKeyThickness":8,"styleRounded":true,"styleGlow":false,"className":"","x":1320,"y":680,"wires":[]},{"id":"c4d4a3b0df372e4c","type":"ui-text","z":"5b972161c4e0464e","g":"8557072f05e4bda0","group":"af263064820fb7d0","order":0,"width":0,"height":0,"name":"Random Number","label":"Random Number","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1350,"y":740,"wires":[]},{"id":"b0cf511f824f2a86","type":"ui-chart","z":"5b972161c4e0464e","g":"8557072f05e4bda0","group":"af263064820fb7d0","name":"","label":"Sawtooth","order":9007199254740991,"chartType":"line","category":"Sawtooth","categoryType":"str","xAxisProperty":"","xAxisPropertyType":"msg","xAxisType":"time","yAxisProperty":"","ymin":"","ymax":"","action":"append","pointShape":"line","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"60","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":"3","height":"4","className":"","x":1320,"y":800,"wires":[[]]},{"id":"f2efc6b419414c9a","type":"ui-chart","z":"5b972161c4e0464e","g":"8557072f05e4bda0","group":"af263064820fb7d0","name":"","label":"Sinusoid","order":9007199254740991,"chartType":"line","category":"Sawtooth","categoryType":"str","xAxisProperty":"","xAxisPropertyType":"msg","xAxisType":"time","yAxisProperty":"","ymin":"","ymax":"","action":"append","pointShape":"line","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"60","removeOlderPoints":"","colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"width":"3","height":"4","className":"","x":1320,"y":860,"wires":[[]]},{"id":"af263064820fb7d0","type":"ui-group","name":"Simulation values","page":"44d3feb2a1143d7b","width":"3","height":"1","order":2,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"5afdbddf71507886","type":"group","z":"5b972161c4e0464e","name":"Read StaticData Values & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["e998aa804042128b","6c9b7d4d195a1e9a","cd097b744d0ec625","18d21607c87ab153","7b5143c4960f92a1","0625b0cf6f546a4a","9d899fbb4d1648b3","6e1edc31687dde54","051e1f282076fed2","de2a1c3e380f743b","c74606c48ccf5a40","053bda13f2a2eabe","277dcf430dc86996","d708e6264cec0070"],"x":84,"y":939,"w":1382,"h":202},{"id":"e998aa804042128b","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"5afdbddf71507886","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":630,"y":1020,"wires":[["7b5143c4960f92a1"]]},{"id":"6c9b7d4d195a1e9a","type":"inject","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Update AnalogItemArrays Values @ 1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.AnalogItemArrays.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":320,"y":1020,"wires":[["e998aa804042128b"]]},{"id":"cd097b744d0ec625","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Read StaticData Values","info":"","x":520,"y":980,"wires":[]},{"id":"18d21607c87ab153","type":"change","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Get ByteAnalogItemArray Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"$string(payload[0].item.value)\t","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1070,"y":1020,"wires":[["277dcf430dc86996"]]},{"id":"7b5143c4960f92a1","type":"switch","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":830,"y":1020,"wires":[["18d21607c87ab153"]]},{"id":"0625b0cf6f546a4a","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"5afdbddf71507886","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":630,"y":1100,"wires":[["051e1f282076fed2"]]},{"id":"9d899fbb4d1648b3","type":"inject","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Update StaticArrayVariables Values @1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.StaticData.StaticArrayVariables.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":320,"y":1100,"wires":[["0625b0cf6f546a4a"]]},{"id":"6e1edc31687dde54","type":"change","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Get BooleanArray Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[0].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1050,"y":1100,"wires":[["d708e6264cec0070"]]},{"id":"051e1f282076fed2","type":"switch","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":830,"y":1100,"wires":[["6e1edc31687dde54"]]},{"id":"de2a1c3e380f743b","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Discard Empty Datasets","info":"","x":820,"y":980,"wires":[]},{"id":"c74606c48ccf5a40","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Parse StaticData Values","info":"","x":1070,"y":980,"wires":[]},{"id":"053bda13f2a2eabe","type":"comment","z":"5b972161c4e0464e","g":"5afdbddf71507886","name":"Display on Dashboard","info":"","x":1340,"y":980,"wires":[]},{"id":"277dcf430dc86996","type":"ui-text","z":"5b972161c4e0464e","g":"5afdbddf71507886","group":"3d4f386e812e8b5f","order":0,"width":0,"height":0,"name":"","label":"ByteAnalogItemArray","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1340,"y":1020,"wires":[]},{"id":"d708e6264cec0070","type":"ui-text","z":"5b972161c4e0464e","g":"5afdbddf71507886","group":"3d4f386e812e8b5f","order":0,"width":0,"height":0,"name":"","label":"BooleanArray","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1320,"y":1100,"wires":[]},{"id":"3d4f386e812e8b5f","type":"ui-group","name":"StaticData Values","page":"44d3feb2a1143d7b","width":"4","height":"1","order":3,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"25f95391088d4a08","type":"group","z":"5b972161c4e0464e","name":"Read MyDevice Values & Display on Dashboard","style":{"label":true,"color":"#000000"},"nodes":["bfe4274963a84e2e","0662c62c7f0cfac0","249c223139c5779e","efb293f13af17dc1","507d2c11c7586957","3b7695a52a1bf6e0","594ec38acadc9673","d4e2915ba92db8a6","85619f0eab615ac7","cd4941a8db3edcb6","ad91d2ca81697fc2"],"x":94,"y":1179,"w":1292,"h":182},{"id":"bfe4274963a84e2e","type":"OpcUa-Browser","z":"5b972161c4e0464e","g":"25f95391088d4a08","endpoint":"53f4394dbf12c6b7","item":"","datatype":"","topic":"","items":[],"name":"OPC Client Namespace Browse","x":590,"y":1280,"wires":[["507d2c11c7586957"]]},{"id":"0662c62c7f0cfac0","type":"inject","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Read MyDevice Values @ 1 second","props":[{"p":"payload"},{"p":"topic","v":"Objects.MyObjects.MyDevice.nodeId","vt":"flow"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":290,"y":1280,"wires":[["bfe4274963a84e2e"]]},{"id":"249c223139c5779e","type":"comment","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Read MyDevice","info":"","x":480,"y":1240,"wires":[]},{"id":"efb293f13af17dc1","type":"change","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Get MyLevel Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[0].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1030,"y":1260,"wires":[["cd4941a8db3edcb6"]]},{"id":"507d2c11c7586957","type":"switch","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"empty check","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":810,"y":1280,"wires":[["efb293f13af17dc1","3b7695a52a1bf6e0"]]},{"id":"3b7695a52a1bf6e0","type":"change","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Get MySwitch Value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload[4].item.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1040,"y":1320,"wires":[["ad91d2ca81697fc2"]]},{"id":"594ec38acadc9673","type":"comment","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Display on Dashboard","info":"","x":1260,"y":1220,"wires":[]},{"id":"d4e2915ba92db8a6","type":"comment","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Parse MyDevice Values","info":"","x":1040,"y":1220,"wires":[]},{"id":"85619f0eab615ac7","type":"comment","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Discard Empty Datasets","info":"","x":800,"y":1240,"wires":[]},{"id":"cd4941a8db3edcb6","type":"ui-gauge","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"Level","group":"ec0ecb26fde8db3e","order":0,"width":3,"height":3,"gtype":"gauge-half","gstyle":"needle","title":"Level","units":"%","icon":"","prefix":"","suffix":"","segments":[{"from":"0","color":"#0094ce"},{"from":"25","color":"#0094ce"},{"from":"50","color":"#0094ce"},{"from":"100","color":"#0094ce"}],"min":0,"max":"100","sizeThickness":16,"sizeGap":4,"sizeKeyThickness":8,"styleRounded":true,"styleGlow":false,"className":"","x":1250,"y":1260,"wires":[]},{"id":"ad91d2ca81697fc2","type":"ui-led","z":"5b972161c4e0464e","g":"25f95391088d4a08","name":"","group":"ec0ecb26fde8db3e","order":-1,"width":0,"height":0,"label":"Switch","labelPlacement":"left","labelAlignment":"flex-start","states":[{"value":"false","valueType":"bool","color":"#ff0000"},{"value":"true","valueType":"bool","color":"#00ff00"}],"allowColorForValueInMessage":false,"shape":"circle","showBorder":true,"showGlow":true,"x":1250,"y":1320,"wires":[]},{"id":"ec0ecb26fde8db3e","type":"ui-group","name":"MyDevice Status & Control","page":"44d3feb2a1143d7b","width":"3","height":"1","order":4,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"3de6c861611c3afa","type":"group","z":"5b972161c4e0464e","name":"Write Mydevices values to OPC UA Server","style":{"label":true,"color":"#000000"},"nodes":["a66583d91b581cd8","3e8cb6e199012155","9fa33d1c9c621611","fb7f57b4da5883ae","9c5ff104eb9c8b10","77bcb828bec95336","afa83dbb46449d4a","fa08f0ed04296363","9f591797b56c565d"],"x":94,"y":1439,"w":792,"h":182},{"id":"a66583d91b581cd8","type":"OpcUa-Item","z":"5b972161c4e0464e","g":"3de6c861611c3afa","item":"ns=6;s=MySwitch","datatype":"Boolean","value":"","name":"Toggle MySwitch","x":470,"y":1520,"wires":[["3e8cb6e199012155"]]},{"id":"3e8cb6e199012155","type":"OpcUa-Client","z":"5b972161c4e0464e","g":"3de6c861611c3afa","endpoint":"53f4394dbf12c6b7","action":"write","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","securitymode":"None","securitypolicy":"None","useTransport":false,"maxChunkCount":1,"maxMessageSize":8192,"receiveBufferSize":8192,"sendBufferSize":8192,"name":"Write MySwitch","x":720,"y":1520,"wires":[[],[]]},{"id":"9fa33d1c9c621611","type":"comment","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"Dashboard Input","info":"","x":200,"y":1480,"wires":[]},{"id":"fb7f57b4da5883ae","type":"OpcUa-Item","z":"5b972161c4e0464e","g":"3de6c861611c3afa","item":"ns=6;s=MyLevel","datatype":"Double","value":"","name":"Modify MyLevel","x":460,"y":1580,"wires":[["9c5ff104eb9c8b10"]]},{"id":"9c5ff104eb9c8b10","type":"OpcUa-Client","z":"5b972161c4e0464e","g":"3de6c861611c3afa","endpoint":"53f4394dbf12c6b7","action":"write","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","securitymode":"None","securitypolicy":"None","useTransport":false,"maxChunkCount":1,"maxMessageSize":8192,"receiveBufferSize":8192,"sendBufferSize":8192,"name":"Write MyLevel","x":720,"y":1580,"wires":[[],[]]},{"id":"77bcb828bec95336","type":"comment","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"Call OPC UA Item","info":"","x":470,"y":1480,"wires":[]},{"id":"afa83dbb46449d4a","type":"comment","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"Write OPC UA Item to Client","info":"","x":740,"y":1480,"wires":[]},{"id":"fa08f0ed04296363","type":"ui-switch","z":"5b972161c4e0464e","g":"3de6c861611c3afa","name":"","label":"Toggle MySwitch","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"passthru":false,"topic":"topic","topicType":"msg","style":"","className":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":210,"y":1520,"wires":[["a66583d91b581cd8"]]},{"id":"9f591797b56c565d","type":"ui-slider","z":"5b972161c4e0464e","g":"3de6c861611c3afa","group":"ec0ecb26fde8db3e","name":"","label":"Modify MyLevel","tooltip":"","order":0,"width":0,"height":0,"passthru":false,"outs":"all","topic":"topic","topicType":"msg","thumbLabel":true,"min":"0","max":"100","step":1,"className":"","x":200,"y":1580,"wires":[["fb7f57b4da5883ae"]]},{"id":"a6e9abacd0bdf3b6","type":"group","z":"5b972161c4e0464e","name":"Read Alarms & Events From OPC UA Server","style":{"label":true,"color":"#000000"},"nodes":["90fb4ca64a642edf","b76f64786bc681c3","71e24b671bc03fb8","c7438df35b506470","c7e8919b636cb51d","5952b86dae22b056","04992b24a3836f19","325068cb935cd6d1","5b4d1bd8b342fc05","ba1ea89438335cb8","d662d662c5ccb9c1","1e3956200997581f","0b8ac86e5e4f9f8d","62b2e14ce0429eef"],"x":94,"y":1679,"w":1352,"h":282},{"id":"90fb4ca64a642edf","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Call OPC UA Item","info":"","x":470,"y":1820,"wires":[]},{"id":"b76f64786bc681c3","type":"OpcUa-Event","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","root":"ns=6;s=MyLevel.Alarm","activatecustomevent":false,"eventtype":"i=2041","customeventtype":"","name":"MyLevel Alarms","x":500,"y":1860,"wires":[["c7438df35b506470"]]},{"id":"71e24b671bc03fb8","type":"inject","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Trigger Alarm Event Capture","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":1860,"wires":[["b76f64786bc681c3"]]},{"id":"c7438df35b506470","type":"OpcUa-Client","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","endpoint":"53f4394dbf12c6b7","action":"events","deadbandtype":"a","deadbandvalue":1,"time":10,"timeUnit":"s","certificate":"n","localfile":"","localkeyfile":"","securitymode":"None","securitypolicy":"None","useTransport":false,"maxChunkCount":1,"maxMessageSize":8192,"receiveBufferSize":8192,"sendBufferSize":8192,"name":"Get MyLevel Events","x":720,"y":1860,"wires":[["c7e8919b636cb51d","5952b86dae22b056","04992b24a3836f19"],[]]},{"id":"c7e8919b636cb51d","type":"change","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Event Text","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Message.text","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1800,"wires":[["d662d662c5ccb9c1","1e3956200997581f"]]},{"id":"5952b86dae22b056","type":"change","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Event Time","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Time","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":990,"y":1860,"wires":[["0b8ac86e5e4f9f8d"]]},{"id":"04992b24a3836f19","type":"change","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Event Severity","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Severity","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":1920,"wires":[["62b2e14ce0429eef"]]},{"id":"325068cb935cd6d1","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Parse Event Dataset","info":"","x":990,"y":1760,"wires":[]},{"id":"5b4d1bd8b342fc05","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Get OPC Events from Client","info":"","x":720,"y":1820,"wires":[]},{"id":"ba1ea89438335cb8","type":"comment","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","name":"Display Events on Dashboard","info":"","x":1240,"y":1720,"wires":[]},{"id":"d662d662c5ccb9c1","type":"ui-notification","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","ui":"5355e0c476f9da3b","position":"center center","colorDefault":true,"color":"#000000","displayTime":"3","showCountdown":true,"outputs":1,"allowDismiss":true,"dismissText":"Close","raw":false,"className":"","name":"Event Notification","x":1230,"y":1800,"wires":[[]]},{"id":"1e3956200997581f","type":"ui-text","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"name":"","label":"Latest MyLevel Event","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1240,"y":1840,"wires":[]},{"id":"0b8ac86e5e4f9f8d","type":"ui-text","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"name":"","label":"Latest MyLevel Event Timestamp","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1280,"y":1880,"wires":[]},{"id":"62b2e14ce0429eef","type":"ui-text","z":"5b972161c4e0464e","g":"a6e9abacd0bdf3b6","group":"ec0ecb26fde8db3e","order":0,"width":0,"height":0,"name":"","label":"Latest MyLevel Event Severity","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"","fontSize":16,"color":"#717171","className":"","x":1270,"y":1920,"wires":[]}] ``` :: # How to Deploy a Basic OPC-UA Server in Node-RED - Part 1 (2026) This article is the first part of a series of OPC-UA content. Here, we will explain some basic concepts of OPC-UA as they apply to building a server in Node-RED, then walk through and deploy an example OPC-UA Server. ## What is OPC-UA? Open Platform Communications Unified Architecture (OPC UA) is an open, platform independent communication framework frequently utilized in industrial automation, and is considered one of the key protocol standards for Industry 4.0 and Industrial IoT (IIoT). The standard is developed and maintained by a consortium called the OPC Foundation, with recognizable industry names such as Siemens, Honeywell, Microsoft, Beckhoff, SAP, Yokogawa, ABB, Rockwell, and Schneider Electric. Because of OPC-UA’s wide industry acceptance, it is increasingly becoming natively supported on devices and systems spanning the entirety of the automation pyramid. !["Automation Pyramid"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/automation-pyramid.jpg "Automation Pyramid")*Image reference - [imagecontroltips.com](https://www.motioncontroltips.com/what-is-opc-ua-and-how-does-it-compare-with-industrial-ethernet/){rel=""nofollow""}* ## Fieldbus Model vs OPC-UA Information Model As of today, industrial ethernet fieldbuses dominate the field/device-level (level 0) and controller/PLC-level (level 1) of the automation pyramid. !["OPC-UA Pyramid"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/OPC-UA-pyramid-2.webp "OPC-UA Pyramid")*Image reference - [mdpi.com](https://www.mdpi.com/1424-8220/21/14/4656){rel=""nofollow""}* Fieldbuses such as Profinet, Ethernet/IP, and EtherCAT, employ deterministic, real-time communication, which is essential for mission-critical and safety-oriented automation tasks. OPC-UA is most commonly encountered at the SCADA level and above (level 2-4). However, with the inclusion of [Time Sensitive Networking (TSN) into the OPC-UA technology stack](https://www.tttech-industrial.com/resource-library/blog-posts/opc-ua-fx){rel=""nofollow""}, OPC-UA can be feasibly used for real-time communication all the way down to the device level. Traditionally, fieldbus protocols transmit only raw data from field devices (ie, a float to represent a pressure, or a boolean to represent the position of a switch). The fieldbus data gets pushed up the automation stack layer by layer, where eventually it will be converted to a format suitable for IT systems to consume (such as OPC-UA). !["Fieldbus Model"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/fieldbus-model.png "Fieldbus Model") In contrast to fieldbus protocols, OPC-UA represents automation data in the form of nodes. The framework for constructing nodes is referred to as the [OPC Information model](https://reference.opcfoundation.org/Core/Part5/v104/docs/){rel=""nofollow""}, and consists of pre-defined classes and methods that are programmed in the OPC Server address space. !["OPC Information Model"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/opc-information-model.png "OPC Information Model") Devices can be described as objects that give a holistic view of the device, beyond simply the raw value. To construct a device object, we can take different individual attributes associated with a device, such as the transmitter raw value, transmitter fault flag, alarm setpoint, and combine them, similar to how user-defined datatypes (UDTs) are objects used to represent devices in PLCs. The information model also defines a folder structure, to allow devices information to reside in a structured hierarchy. Using the example temperature transmitter above, an example folder structure can be constructed as follows: `/Root/Objects/Calcinator 1 PLC/Temperature Transmitters/Tank 1 Temperature/Transmitter Value` This folder structure will be exposed via the OPC Client browser, allowing end-users to easily “drill down” to individual node information in a logical manner. !["OPC Client Browser"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/opc-client-browser.png "OPC Information Model") In summary, OPC-UA represents a trade-off between complex information modeling, with the versatility for that data to be consumed by devices and systems all the way up the automation pyramid layers. The data does not have to pass through subsequent automation layers on the way up, nor does the data need to undergo any conversion along the way. !["OPC-UA Distributed Model"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/OPC-UA-distributed-model.jpg "OPC-UA Distributed Model")*Image reference - [ifr.org](https://ifr.org/post/faster-robot-communication-through-the-opc-robotics-companion-specification){rel=""nofollow""}* The OPC client simply needs to subscribe to the OPC Server endpoint url (ex. opc.tcp\://server.address), and the client will be able to browse the structured OPC data as it’s modeled in the server. Any client will receive the information in the same manner, regardless if it’s a PLC, SCADA, MES, or ERP system. This opens the possibility for horizontal and vertical system integration in a standardized manner. Additionally, the more information that is exposed about a device, the easier it is to track, and use said data to autonomously reconfigure, or pre-emptively take maintenance actions. ## Deploying an Example OPC-UA Server in Node-RED With some background on OPC-UA and how information is modeled in mind, we can take a look at the [node-red-contrib-opcua-server](https://flows.nodered.org/node/node-red-contrib-opcua-server){rel=""nofollow""} node, which is merely a compact version of the [node-red-contrib-opcua](https://flows.nodered.org/node/node-red-contrib-opcua){rel=""nofollow""} node that only focuses on the OPC-UA server and hence requires less dependencies. :cta-image{alt="Aperia Technologies stopped reprogramming controllers station by station with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/aperia-book-demo.png"} An [example flow](https://github.com/BiancoRoyal/node-red-contrib-opcua-server/blob/master/examples/server-with-context.json){rel=""nofollow""} is provided on github that can serve as a basis for understanding how a OPC-UA server is constructed. Let’s get the example server up and running. Deploying the example flow yields the following result - ![Compact Server Flow](https://flowfuse.com/blog/2023/07/images/opc-ua-1/compact-server-flow.png "Compact Server Flow") - an inject node is trigging the function `set flow context Inputs` at a one second interval, which creates 7 randomly generated float values and stores them as flow context variables, `isoInput2` - `isoInput8` (isolated inputs). The values will change to a new random number each time the node is injected. ```javascript flow.set('isoInput2', Math.random() + 12.0) flow.set('isoInput3', Math.random() + 13.0) flow.set('isoInput4', Math.random() + 14.0) flow.set('isoInput5', Math.random() + 15.0) flow.set('isoInput6', Math.random() + 16.0) flow.set('isoInput7', Math.random() + 17.0) flow.set('isoInput8', Math.random() + 18.0) ... ``` - another inject node is triggering the function `set flow context Outputs`, also at a one second interval, which creates another set of 7 randomly generated float values and stores them as flow context variables, `isoOutput2` - `isoOutput8` (isolated inputs). The values will change to a new random number each time the node is injected. ```javascript flow.set('isoOutput2', Math.random() + 2.0) flow.set('isoOutput3', Math.random() + 3.0) flow.set('isoOutput4', Math.random() + 4.0) flow.set('isoOutput5', Math.random() + 5.0) flow.set('isoOutput6', Math.random() + 6.0) flow.set('isoOutput7', Math.random() + 7.0) flow.set('isoOutput8', Math.random() + 8.0) ... ``` We can confirm the values are being stored in memory by checking the flow context data and pressing the refresh button. !["Screenshot showing the Context Data option"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/context-data-1.png "Screenshot showing the Context Data option") ![Screenshot showing the flow variables in the context data tab](https://flowfuse.com/blog/2023/07/images/opc-ua-1/context-data-2.png "Screenshot showing the flow variables in the context data tab") Each time we hit refresh, the values change, confirming that the values are randomly changing every second. The last, and most important part of the flow, is the `Compact-Server` node, which actually stands alone without any incoming or outgoing connections. !["Compact Server Node"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/compact-server-node.png "Compact Server Node") In the `Compact-Server` node properties, the first tab is `Settings`, and the two important properties here are `Port` and `Show Errors`. As can be seen in the node screenshot above, the node is reporting `active`, which means the server is configured correctly. ![Screenshot showing the Settings Tab of compact server node](https://flowfuse.com/blog/2023/07/images/opc-ua-1/settings-tab.png "Screenshot showing the Settings Tab of compact server node") The `Limits` tab specifies some default limits that we can configure if we like, but are not necessary to be modified for test purposes. The `Security` tab has one important option, `Allow Anonymous`. By default, anonymous access is enabled. ![Screenshot showing the Security Tab of compact server node](https://flowfuse.com/blog/2023/07/images/opc-ua-1/security-tab.png "Screenshot showing the Security Tab of compact server node") For a production system, we will want to enable security, but for test purposes, we will leave anonymous access enabled. `Users & Sets` tab is related to security and permissions. We can leave this empty for testing. The `Address Space` tab is where our server OPC Information Model is constructed, using classes and methods from the [node-opcua sdk](https://node-opcua.github.io/){rel=""nofollow""}. Breaking down the provided example code for further context, it starts with a function that is responsible for invoking the OPC-UA server, ```javascript const opcua = coreServer.choreCompact.opcua; ``` and then the namespace is created. ```javascript const namespace = addressSpace.getOwnNamespace(); ``` Further down, the variables that will be published by the server (which are our `isoInput` & `isoOutput` flow context variables) are initialized, ```javascript this.sandboxFlowContext.set("isoInput1", 0); this.setInterval(() => { flexServerInternals.sandboxFlowContext.set( "isoInput1", Math.random() + 50.0 ); }, 500); this.sandboxFlowContext.set("isoInput2", 0); this.sandboxFlowContext.set("isoInput3", 0); ... ``` and an OPC folder structure is defined. ```javascript coreServer.debugLog("init dynamic address space"); const rootFolder = addressSpace.findNode("RootFolder"); node.warn("construct new address space for OPC UA"); const myDevice = namespace.addFolder(rootFolder.objects, { "browseName": "RaspberryPI-Zero-WLAN" }); ... ``` Then, with our variables and folder structure defined, nodes are added to the namespace for each context variable. ```javascript const gpioDI1 = namespace.addVariable({ "organizedBy": isoInputs, "browseName": "I1", "nodeId": "ns=1;s=Isolated_Input1", "dataType": "Double", "value": { "get": function() { return new Variant({ "dataType": DataType.Double, "value": flexServerInternals.sandboxFlowContext.get("isoInput1") }); }, "set": function(variant) { flexServerInternals.sandboxFlowContext.set( "isoInput1", parseFloat(variant.value) ); return opcua.StatusCodes.Good; } } }); ... ``` Last, OPC views are defined. Views create custom hierarchies our OPC Client can browse as an alternative to the default folder structure. ```javascript const viewDI = namespace.addView({ "organizedBy": rootFolder.views, "browseName": "RPIW0-Digital-Ins" }); const viewDO = namespace.addView({ "organizedBy": rootFolder.views, "browseName": "RPIW0-Digital-Outs" }); viewDI.addReference({ "referenceType": "Organizes", "nodeId": gpioDI1.nodeId }); ... ``` Finally, on the `Discovery` tab, we must define an endpoint for an OPC Client to subscribe to. The `Endpoint Url` follows the format `opc.tcp://
:port`. Our port was defined on the `Settings` tab, which by default, is port `54845`. The address will be either the url or ip address of your Node-RED instance. In my case, it’s 192.168.0.114. So my Endpoint Url = `opc.tcp://192.168.0.114:54845` ![Screenshot showing the Discovery Tab of compact server node](https://flowfuse.com/blog/2023/07/images/opc-ua-1/discovery-tab.png "Screenshot showing the Discovery Tab of compact server node") Once the endpoint url is added, deploy the flow, and confirm the server is reporting “active”. ![Screenshot showing the Active Tab of compact server node](https://flowfuse.com/blog/2023/07/images/opc-ua-1/compact-server-active.png "Screenshot showing the Active Tab of compact server node") ## Connect to Example OPC-Server Using OPC-UA Browser To connect to our OPC endpoint, we need an OPC Client. Prosys provides a [free OPC-UA Browser ](https://www.prosysopc.com/products/opc-ua-browser/){rel=""nofollow""}that supports Windows, Linux, and Mac OS. To test our Server, the Windows version of Prosys OPC-UA Browser will be utilized. To connect to our Node-RED OPC server, enter the endpoint url and press “connect to server”. !["Screenshot showing the OPC Client"](https://flowfuse.com/blog/2023/07/images/opc-ua-1/opc-client-connect.png "Screenshot showing the OPC Client") It will ask for security. Remember that we allowed anonymous access, so the default security mode of `None` is the correct option. Once connected, we can browse our OPC Server. ![OPC Client UI](https://flowfuse.com/blog/2023/07/images/opc-ua-1/opc-client-ui.png) If we navigate to `Objects → RaspberryPI-Zero-WLAN → GPIO → Inputs`, we can see a list of inputs that correspond to the `isoInput` context variables defined in the example flow, which are randomly generated numbers. Clicking `I1` we can see the value in real-time, along with some additional properties. ![OPC Client Node](https://flowfuse.com/blog/2023/07/images/opc-ua-1/opc-client-node.png "OPC Client Node") If we go to `Views`, we can see the custom hierarchy defined in the example server, which divides the data by Digital-Ins and Digital-Outs. ![OPC Client View](https://flowfuse.com/blog/2023/07/images/opc-ua-1/opc-client-view.png) ## Summary In this article, we compare OPC-UA to traditional fieldbus protocols, explain the importance of the OPC UA Information Model to understand how data is modeled in the address space of an OPC Server, and then walk through and deploy an example compact OPC-UA Server flow. This isn't just a lab exercise: [Opto 22, one of the original vendors behind the OPC standard, ships Node-RED pre-installed on its groov EPIC edge controllers](https://flowfuse.com/customer-stories/opto22-embraces-node-red/), putting this same OPC-UA server pattern into production on the factory floor. In our next article, we will build a custom OPC-UA Server in Node-RED with data pulled from an Allen Bradley PLC over Ethernet/IP, using the PLC data to develop a custom OPC UA Information Model programmed in the OPC server address space. # How to add images to Node-RED dashboards when using FlowFuse (2026) Using images in your Node-RED dashboards can significantly improve your users' experience. The most common method to add images to dashboards is to store them within the filesystem of an Node-RED instance but sometimes that's not an option. How can you easily use images when working in a containerized environment such as Docker, or Kubernetes? We will also explore latest feature from FlowFuse that makes this step super easy. When designing a dashboard, images allow you to significantly enrich your content. Some examples include: - displaying maps to guide engineers to a problem which needs resolving. - displaying pictures of specific hardware on a factory-floor which needs to be checked. - displaying physical tools which should be used to resolve a problem. ### Prerequisites Before we begin, ensure you have the following custom nodes installed: - [@flowfuse/node-red-dashboard](https://flows.nodered.org/node/@flowfuse/node-red-dashboard){rel=""nofollow""} - A set of dashboard nodes for Node-RED. We will use this dashboard to demonstrate how to quickly display images using static assets. If you're a beginner and want to dive deeper, refer to [Getting started with FlowFuse Dashboarad](https://flowfuse.com/blog/2024/03/dashboard-getting-started/). - [node-red-contrib-string](https://flows.nodered.org/node/node-red-contrib-string){rel=""nofollow""} - A string manipulation node based on the lightweight stringjs library. - [node-red-node-base64](https://flows.nodered.org/node/node-red-node-base64){rel=""nofollow""} - A Node-RED node to encode and decode data to and from base64. ## Easily Add Images to Node-RED Dashboards with FlowFuse’s Static Asset Service [FlowFuse's static assets](https://flowforge.com/docs/user/static-asset-service/){rel=""nofollow""} service provides a simple way to manage images and other assets in Node-RED. Follow these steps to quickly add images to your Node-RED dashboard. :cta-image{alt="Wenco deploys new dashboard pages in days with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/wenco-book-demo.png"} ### Steps to Add Images Using the Static Asset Service: ##### 1. Access the Static Assets Service - Log into your FlowFuse account, navigate to your **Node-RED instance**, and click on the **Static Assets Service** tab. ##### 2. Create a New Folder (Optional) - Click the **New Folder** button to create a folder that will help you organize your assets. Provide a folder name and **confirm** the creation. ##### 3. Upload Your Image - Enter the folder (or skip this step if not using folders), click **Upload**, select your image file, and **confirm**. ##### 4. Copy the Image Path - Once uploaded, click the **copy icon** next to the image to get the path for use in Node-RED. ##### 5. Set Up the Image Flow in Node-RED - Open the Node-RED editor for the relevant instance. - Drag an `inject` node onto the canvas and set it to trigger immediately when the flow is deployed. - Drag a `read file` node, paste the copied file path in the **Filename** field, and set the output to Single buffer object. ##### 6. Prepare the Image for Display - Add a `string` node to convert the buffer to a base64 string. Set **From** as `msg.filename` and adjust the **Method** to `getmost`. - Add a `change` node and configure it to add the elements shown in the following image: :br![The change node showing added elements](https://flowfuse.com/blog/2023/07/images/change-node.png "The change node showing added elements") ##### 7. Display the Image in the Dashboard - Drag a `ui-template` node onto the canvas. - Add the code in `ui-template` with an `` tag, configuring the `src` attribute with `msg.payload`, as shown in the following code. Alternatively, you can use the following code directly if you want to display the image in the top-left corner of your dashboard header: ```javascript ``` ##### 8. Connect the Nodes - Finally connect the nodes in the following order: the **output** of the `inject` node to the **input** of the `read file` node, then link to the `string node`, followed by the `change` node, and finally to the `ui-template` node. :br`inject → read file → string → change → ui-template` ::render-flow ```json [{"id":"e50f7c57189d62f8","type":"group","z":"d4aa6dd5b63a56de","style":{"stroke":"#b2b3bd","stroke-opacity":"1","fill":"#f2f3fb","fill-opacity":"0.5","label":true,"label-position":"nw","color":"#32333b"},"nodes":["3bf4c71150bd0524","4d3ca1b96c181645","315948cc6a2cc9e8","fcf061d4cc732e2e","6563d8715af4cb06","d40caa36040de881"],"x":514,"y":699,"w":1752,"h":82},{"id":"3bf4c71150bd0524","type":"ui-template","z":"d4aa6dd5b63a56de","g":"e50f7c57189d62f8","group":"","page":"","ui":"25f447d87d1ce5c9","name":"Display image","order":0,"width":0,"height":0,"head":"","format":"\n\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:ui","className":"","x":2160,"y":740,"wires":[[]]},{"id":"4d3ca1b96c181645","type":"inject","z":"d4aa6dd5b63a56de","g":"e50f7c57189d62f8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":630,"y":740,"wires":[["315948cc6a2cc9e8"]]},{"id":"315948cc6a2cc9e8","type":"file in","z":"d4aa6dd5b63a56de","g":"e50f7c57189d62f8","name":"","filename":"Images/ff-logo--wordmark--light.png","filenameType":"str","format":"","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":880,"y":740,"wires":[["d40caa36040de881"]]},{"id":"fcf061d4cc732e2e","type":"change","z":"d4aa6dd5b63a56de","g":"e50f7c57189d62f8","name":"Add the file type to the mimetype, add to image content","rules":[{"t":"set","p":"mimetype","pt":"msg","to":"\"data:image/\"&msg.filetype&\";base64,\"","tot":"jsonata"},{"t":"set","p":"output","pt":"msg","to":"msg.mimetype&msg.payload","tot":"jsonata"},{"t":"move","p":"output","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1820,"y":740,"wires":[["3bf4c71150bd0524"]]},{"id":"6563d8715af4cb06","type":"string","z":"d4aa6dd5b63a56de","g":"e50f7c57189d62f8","name":"Get file type from file name","methods":[{"name":"getRightMost","params":[{"type":"str","value":"."}]}],"prop":"filename","propout":"filetype","object":"msg","objectout":"msg","x":1480,"y":740,"wires":[["fcf061d4cc732e2e"]]},{"id":"d40caa36040de881","type":"base64","z":"d4aa6dd5b63a56de","g":"e50f7c57189d62f8","name":"Convert Buffer to Base 64 String","action":"","property":"payload","x":1210,"y":740,"wires":[["6563d8715af4cb06"]]},{"id":"25f447d87d1ce5c9","type":"ui-base","name":"Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-control","ui-notification"],"showPathInSidebar":false,"showPageTitle":false,"titleBarStyle":"default"}] ``` :: Using the FlowFuse Static Assets service is highly beneficial when you want to display images in Node-RED dashboards, as it saves time compared to alternative solutions. However, it’s important to note that moving Node-RED instances through a DevOps pipeline currently does not support handling static assets. This feature is expected in future updates. If you want to manage images effectively within your Node-RED dashboards, consider the alternative solutions discussed in this blog, ensuring that the movement of instances does not affect the usage of these assets. ## Why not just store them in Node-RED's host operating system? Storing images locally can work well when you can access and edit the images on an operating system, but that approach doesn't scale if you are moving instances through a DevOps pipeline. It can also not work well when deploying to environments where you don't have easy access to the host operating system. How can we include images in dashboards, and be confident that a given build of an application will show the correct images, no matter where your Node-RED instances are hosted? ## Inspiration There are various solutions to this problem, I wanted to share one I came across when working with a FlowFuse customer recently. I've modified the flows to make them more general in design, but the underlying principal is the same. I asked if I was OK to credit the customer but they said there was no need. Thanks for the inspiration, kind customer! ## Solution explanation There are three key sections to this solution: 1. Pull the images we need from URLs 2. Store those images in the temporary filesystem of Node-RED 3. Serve up those images as needed in the dashboard It is possible for us to skip step 2, but I wanted to have the images stored locally, in the Node-RED instance. storing the images locally will improve the loading times of the dashboard. This is especially beneficial when your dashboard is dynamically displaying relevant images, e.g. to show an image of a specific machine which needs to be attended to. The key benefit of pulling the images from URLs this way is, no matter where you are running Node-RED, the correct images will be shown in your dashboard. ## Prequsite Before moving forward, ensure you have the following nodes installed, as the flows shared later will require them: - [node-red-contrib-calc](https://flows.nodered.org/node/node-red-contrib-calc){rel=""nofollow""} - A Node-RED node to perform basic mathematical calculations. - [node-red-contrib-image-output](https://flows.nodered.org/node/node-red-contrib-image-output){rel=""nofollow""} - A simple way to preview and examine images in your flows. - [node-red-contrib-os](https://flows.nodered.org/node/node-red-contrib-os){rel=""nofollow""} - Nodes for obtaining system information like CPU usage. - [node-red-contrib-string](https://flows.nodered.org/node/node-red-contrib-string){rel=""nofollow""} - A string manipulation node based on the lightweight stringjs library. - [@flowfuse/node-red-dashboard](https://flows.nodered.org/node/@flowfuse/node-red-dashboard){rel=""nofollow""} - A set of dashboard nodes for Node-RED. - [node-red-node-base64](https://flows.nodered.org/node/node-red-node-base64){rel=""nofollow""} - A Node-RED node to encode and decode data to and from base64. ## File and file-in nodes I've included the flows as json below so you can try them out yourself. Please note, I'm using FlowFuse's own [file and file-in nodes](https://flowfuse.com/docs/user/filenodes/) in these examples. If you want to use these flows on hosting other than FlowFuse, you will need to replace the nodes with the standard Node-RED file and file-in nodes. ## The flows The first flow takes image URLs in an array, each image is downloaded, processed, then saved to the local file storage. Let's take a look at the flow: ::render-flow ```json [{"id":"6b8059f703d0f574","type":"group","z":"c6f2a894be05d857","name":"Write the images to disk from the URLs","style":{"label":true},"nodes":["04fb6911559797a0","8a3c077f0f85a905","22c5026dd58e418b","6fcca5cfee2bcb89"],"x":38,"y":53,"w":1004,"h":434},{"id":"04fb6911559797a0","type":"group","z":"c6f2a894be05d857","g":"6b8059f703d0f574","name":"Inject the image URLs to download","style":{"label":true},"nodes":["e635ceb0577a86d5","29fe40a054be5b2b","1e81a35c27aae6ad"],"x":74,"y":79,"w":502,"h":82},{"id":"e635ceb0577a86d5","type":"inject","z":"c6f2a894be05d857","g":"04fb6911559797a0","name":"Send in image URLs as an array","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"https://openjsf.org/wp-content/uploads/sites/84/2023/02/ff-logo-wordmark-light_4x.png\",\"https://nodered.org/images/nr-image-1.png\",\"/img/screen-pseudo-overview-2QvTVle3Mr-384.avif\"]","payloadType":"json","x":250,"y":120,"wires":[["29fe40a054be5b2b"]]},{"id":"29fe40a054be5b2b","type":"split","z":"c6f2a894be05d857","g":"04fb6911559797a0","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":450,"y":120,"wires":[["1e81a35c27aae6ad"]]},{"id":"1e81a35c27aae6ad","type":"link out","z":"c6f2a894be05d857","g":"04fb6911559797a0","name":"link out 3","mode":"link","links":["f582702ec222069c"],"x":535,"y":120,"wires":[]},{"id":"8a3c077f0f85a905","type":"group","z":"c6f2a894be05d857","g":"6b8059f703d0f574","name":"Download the images","style":{"label":true},"nodes":["453f3b9d7d312bd2","ecbd7b1a410ecd9d","4d650baa2118036e","f582702ec222069c"],"x":84,"y":179,"w":532,"h":82},{"id":"453f3b9d7d312bd2","type":"http request","z":"c6f2a894be05d857","g":"8a3c077f0f85a905","name":"Get the image","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":460,"y":220,"wires":[["4d650baa2118036e"]]},{"id":"ecbd7b1a410ecd9d","type":"change","z":"c6f2a894be05d857","g":"8a3c077f0f85a905","name":"Set URL to download","rules":[{"t":"move","p":"payload","pt":"msg","to":"url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":220,"wires":[["453f3b9d7d312bd2"]]},{"id":"4d650baa2118036e","type":"link out","z":"c6f2a894be05d857","g":"8a3c077f0f85a905","name":"link out 1","mode":"link","links":["8bc38803dec97185"],"x":575,"y":220,"wires":[]},{"id":"f582702ec222069c","type":"link in","z":"c6f2a894be05d857","g":"8a3c077f0f85a905","name":"link in 3","links":["1e81a35c27aae6ad"],"x":125,"y":220,"wires":[["ecbd7b1a410ecd9d"]]},{"id":"22c5026dd58e418b","type":"group","z":"c6f2a894be05d857","g":"6b8059f703d0f574","name":"Save the images to the local storage","style":{"label":true},"nodes":["95c819560d22f394","0fe7f013b6356c5d","7edc6cb2b0d243db","829bfc8e6e293ada","8bc38803dec97185","98980d88013c6e12"],"x":84,"y":279,"w":932,"h":82},{"id":"95c819560d22f394","type":"base64","z":"c6f2a894be05d857","g":"22c5026dd58e418b","name":"convert to base64","action":"str","property":"payload","x":250,"y":320,"wires":[["829bfc8e6e293ada"]]},{"id":"0fe7f013b6356c5d","type":"file","z":"c6f2a894be05d857","g":"22c5026dd58e418b","name":"Write file to storage","filename":"filename","filenameType":"msg","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":850,"y":320,"wires":[["98980d88013c6e12"]]},{"id":"7edc6cb2b0d243db","type":"string","z":"c6f2a894be05d857","g":"22c5026dd58e418b","name":"Get filename from the URL","methods":[{"name":"getRightMost","params":[{"type":"str","value":"/"}]}],"prop":"responseUrl","propout":"filename","object":"msg","objectout":"msg","x":620,"y":320,"wires":[["0fe7f013b6356c5d"]]},{"id":"829bfc8e6e293ada","type":"image","z":"c6f2a894be05d857","g":"22c5026dd58e418b","name":"preview","width":"150","data":"payload","dataType":"msg","thumbnail":false,"active":true,"pass":true,"outputs":1,"x":420,"y":320,"wires":[["7edc6cb2b0d243db"]]},{"id":"8bc38803dec97185","type":"link in","z":"c6f2a894be05d857","g":"22c5026dd58e418b","name":"link in 1","links":["4d650baa2118036e"],"x":125,"y":320,"wires":[["95c819560d22f394"]]},{"id":"98980d88013c6e12","type":"link out","z":"c6f2a894be05d857","g":"22c5026dd58e418b","name":"link out 2","mode":"link","links":["1e94b5bab542830a"],"x":975,"y":320,"wires":[]},{"id":"6fcca5cfee2bcb89","type":"group","z":"c6f2a894be05d857","g":"6b8059f703d0f574","name":"Output a debug once all images have been processed","style":{"label":true},"nodes":["d1a6feea3ac829c6","119f8008752bc4fb","1e94b5bab542830a"],"x":64,"y":379,"w":382,"h":82},{"id":"d1a6feea3ac829c6","type":"debug","z":"c6f2a894be05d857","g":"6fcca5cfee2bcb89","name":"debug 140","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":330,"y":420,"wires":[]},{"id":"119f8008752bc4fb","type":"join","z":"c6f2a894be05d857","g":"6fcca5cfee2bcb89","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":190,"y":420,"wires":[["d1a6feea3ac829c6"]]},{"id":"1e94b5bab542830a","type":"link in","z":"c6f2a894be05d857","g":"6fcca5cfee2bcb89","name":"link in 2","links":["98980d88013c6e12"],"x":105,"y":420,"wires":[["119f8008752bc4fb"]]}] ``` :: We've now downloaded the images we need, and saved them to our local storage, to make them load more quickly when a user views them in the dashboard. Onto the second flow, which will get the images from the local storage and then load them into the dashboard. Let's take a look at it: ![Get the images from the local storage and place them in a dashboard](https://flowfuse.com/blog/2023/07/images/load-images-from-disk-and-show-in-dashboard.png "Get the images from the local storage and place them in a dashboard") You can import this flow into Node-RED using the code below: ::render-flow ```json [{"id":"596006fe79275d55","type":"group","z":"d4aa6dd5b63a56de","name":"Get the images from the filestore and display in the Dashboard","style":{"label":true},"nodes":["be421f8265c4c090","d2dc98b4f3d6b968","b9029adc6097bd14","1e54acecec6bd411","99aaee673fc70bc2"],"x":1228,"y":3733,"w":974,"h":654},{"id":"be421f8265c4c090","type":"group","z":"d4aa6dd5b63a56de","g":"596006fe79275d55","name":"Get the images from the local storage","style":{"label":true},"nodes":["c698b2945583e126","ab22b978a2f5d80f","2836914f9b643e34","8e32aba67d9ac9d4"],"x":1264,"y":3899,"w":492,"h":82},{"id":"c698b2945583e126","type":"image","z":"d4aa6dd5b63a56de","g":"be421f8265c4c090","name":"preview","width":"150","data":"payload","dataType":"msg","thumbnail":false,"active":true,"pass":true,"outputs":1,"x":1620,"y":3940,"wires":[["8e32aba67d9ac9d4"]]},{"id":"ab22b978a2f5d80f","type":"file in","z":"d4aa6dd5b63a56de","g":"be421f8265c4c090","name":"Read file from storage","filename":"payload","filenameType":"msg","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":1440,"y":3940,"wires":[["c698b2945583e126"]]},{"id":"2836914f9b643e34","type":"link in","z":"d4aa6dd5b63a56de","g":"be421f8265c4c090","name":"link in 4","links":["a9958e1e4b07191a"],"x":1305,"y":3940,"wires":[["ab22b978a2f5d80f"]]},{"id":"8e32aba67d9ac9d4","type":"link out","z":"d4aa6dd5b63a56de","g":"be421f8265c4c090","name":"link out 5","mode":"link","links":["bbdd3a2e737e1074"],"x":1715,"y":3940,"wires":[]},{"id":"d2dc98b4f3d6b968","type":"group","z":"d4aa6dd5b63a56de","g":"596006fe79275d55","name":"Prepare each image to be shown in the dashboard","style":{"label":true},"nodes":["54f0e7fddf447d10","d856077cc6d37093","bbdd3a2e737e1074","2d29c953bcdab787"],"x":1264,"y":3999,"w":832,"h":82},{"id":"54f0e7fddf447d10","type":"change","z":"d4aa6dd5b63a56de","g":"d2dc98b4f3d6b968","name":"Add the file type to the mimetype, add to image content","rules":[{"t":"set","p":"mimetype","pt":"msg","to":"\"data:image/\"&msg.filetype&\";base64,\"","tot":"jsonata"},{"t":"set","p":"output","pt":"msg","to":"msg.mimetype&msg.payload","tot":"jsonata"},{"t":"move","p":"output","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1810,"y":4040,"wires":[["2d29c953bcdab787"]]},{"id":"d856077cc6d37093","type":"string","z":"d4aa6dd5b63a56de","g":"d2dc98b4f3d6b968","name":"Get file type from file name","methods":[{"name":"getRightMost","params":[{"type":"str","value":"."}]}],"prop":"filename","propout":"filetype","object":"msg","objectout":"msg","x":1460,"y":4040,"wires":[["54f0e7fddf447d10"]]},{"id":"bbdd3a2e737e1074","type":"link in","z":"d4aa6dd5b63a56de","g":"d2dc98b4f3d6b968","name":"link in 5","links":["8e32aba67d9ac9d4"],"x":1305,"y":4040,"wires":[["d856077cc6d37093"]]},{"id":"2d29c953bcdab787","type":"link out","z":"d4aa6dd5b63a56de","g":"d2dc98b4f3d6b968","name":"link out 6","mode":"link","links":["c5e4febe48363987"],"x":2055,"y":4040,"wires":[]},{"id":"b9029adc6097bd14","type":"group","z":"d4aa6dd5b63a56de","g":"596006fe79275d55","name":"Send the images to the correct section of the dashboard","style":{"label":true},"nodes":["fa873d7bccd2b6be","c5e4febe48363987","d80e5a54a7c5b2f8","fb0c76a6934b0a7f","4e4105a9774c7b5c","917c842a1b46ad4c"],"x":1264,"y":4099,"w":912,"h":162},{"id":"fa873d7bccd2b6be","type":"switch","z":"d4aa6dd5b63a56de","g":"b9029adc6097bd14","name":"Send the image to the correct section of the dashboard","property":"filename","propertyType":"msg","rules":[{"t":"eq","v":"ff-logo-wordmark-light_4x.png","vt":"str"},{"t":"eq","v":"screen-pseudo-overview-2QvTVle3Mr-384.avif","vt":"str"},{"t":"eq","v":"nr-image-1.png","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":1550,"y":4180,"wires":[["917c842a1b46ad4c"],["4e4105a9774c7b5c"],["fb0c76a6934b0a7f"]]},{"id":"c5e4febe48363987","type":"link in","z":"d4aa6dd5b63a56de","g":"b9029adc6097bd14","name":"link in 6","links":["2d29c953bcdab787"],"x":1305,"y":4180,"wires":[["fa873d7bccd2b6be"]]},{"id":"d80e5a54a7c5b2f8","type":"link out","z":"d4aa6dd5b63a56de","g":"b9029adc6097bd14","name":"link out 7","mode":"link","links":["c9de7384c9ca672f"],"x":2135,"y":4180,"wires":[]},{"id":"fb0c76a6934b0a7f","type":"ui-template","z":"d4aa6dd5b63a56de","g":"b9029adc6097bd14","group":"ec62d482d77f7908","page":"","ui":"","name":"Display the image on the Dashboard","order":6,"width":"3","height":"1","head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1930,"y":4220,"wires":[["d80e5a54a7c5b2f8"]]},{"id":"4e4105a9774c7b5c","type":"ui-template","z":"d4aa6dd5b63a56de","g":"b9029adc6097bd14","group":"ec62d482d77f7908","page":"","ui":"","name":"Display the image on the Dashboard","order":5,"width":"3","height":"1","head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1930,"y":4180,"wires":[["d80e5a54a7c5b2f8"]]},{"id":"917c842a1b46ad4c","type":"ui-template","z":"d4aa6dd5b63a56de","g":"b9029adc6097bd14","group":"ec62d482d77f7908","page":"","ui":"","name":"Display the image on the Dashboard","order":2,"width":0,"height":0,"head":"","format":"","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1930,"y":4140,"wires":[["d80e5a54a7c5b2f8"]]},{"id":"ec62d482d77f7908","type":"ui-group","name":"Default","page":"550c9ac3b2ed01c9","width":"6","height":"1","order":1,"showTitle":false,"className":"","visible":"true","disabled":"false"},{"id":"550c9ac3b2ed01c9","type":"ui-page","name":"Home","ui":"25f447d87d1ce5c9","path":"/","icon":"home","layout":"grid","theme":"c68088445147719b","breakpoints":[{"name":"Default","px":0,"cols":3},{"name":"Tablet","px":576,"cols":6},{"name":"Small Desktop","px":768,"cols":9},{"name":"Desktop","px":1024,"cols":12}],"order":1,"className":"","visible":true,"disabled":false},{"id":"25f447d87d1ce5c9","type":"ui-base","name":"Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-control","ui-notification"],"showPathInSidebar":false,"showPageTitle":false,"titleBarStyle":"default"},{"id":"c68088445147719b","type":"ui-theme","name":"Theme Name","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}},{"id":"1e54acecec6bd411","type":"group","z":"d4aa6dd5b63a56de","g":"596006fe79275d55","name":"Output a debug once all images have been processed","style":{"label":true},"nodes":["c9de7384c9ca672f","b073cd4970e6ddf2","c1b52f44fd4260b3"],"x":1254,"y":4279,"w":392,"h":82},{"id":"c9de7384c9ca672f","type":"link in","z":"d4aa6dd5b63a56de","g":"1e54acecec6bd411","name":"link in 7","links":["d80e5a54a7c5b2f8"],"x":1295,"y":4320,"wires":[["b073cd4970e6ddf2"]]},{"id":"b073cd4970e6ddf2","type":"join","z":"d4aa6dd5b63a56de","g":"1e54acecec6bd411","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":1380,"y":4320,"wires":[["c1b52f44fd4260b3"]]},{"id":"c1b52f44fd4260b3","type":"debug","z":"d4aa6dd5b63a56de","g":"1e54acecec6bd411","name":"debug 141","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1530,"y":4320,"wires":[]},{"id":"99aaee673fc70bc2","type":"group","z":"d4aa6dd5b63a56de","g":"596006fe79275d55","name":"Inject the image files' names","style":{"label":true},"nodes":["11787d1ab6c8a9ec","49b8f3cce48dd5a9","a9958e1e4b07191a","c452f250e1303c69","08b30802899573a6"],"x":1254,"y":3759,"w":782,"h":122},{"id":"11787d1ab6c8a9ec","type":"inject","z":"d4aa6dd5b63a56de","g":"99aaee673fc70bc2","name":"Inject","props":[],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","x":1350,"y":3800,"wires":[["c452f250e1303c69"]]},{"id":"49b8f3cce48dd5a9","type":"split","z":"d4aa6dd5b63a56de","g":"99aaee673fc70bc2","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":1910,"y":3800,"wires":[["a9958e1e4b07191a"]]},{"id":"a9958e1e4b07191a","type":"link out","z":"d4aa6dd5b63a56de","g":"99aaee673fc70bc2","name":"link out 4","mode":"link","links":["2836914f9b643e34"],"x":1995,"y":3800,"wires":[]},{"id":"c452f250e1303c69","type":"change","z":"d4aa6dd5b63a56de","g":"99aaee673fc70bc2","name":"Image file names as an array","rules":[{"t":"set","p":"payload","pt":"msg","to":"[\"ff-logo-wordmark-light_4x.png\",\"screen-pseudo-overview-2QvTVle3Mr-384.avif\",\"nr-image-1.png\"]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":1720,"y":3800,"wires":[["49b8f3cce48dd5a9"]]},{"id":"08b30802899573a6","type":"ui-event","z":"d4aa6dd5b63a56de","g":"99aaee673fc70bc2","ui":"25f447d87d1ce5c9","name":"Update images on dashboard open","x":1420,"y":3840,"wires":[["c452f250e1303c69"]]}] ``` :: I have also included some simple dashboard elements you can view alongside the images. Let's take a look at the dashboard: :video{ariaLabel="The dashboard showing our images alongside other standard elements" autoPlay="true" height="656" loop="true" muted="true" playsInline="true" preload="none" width="646"} If you import these flows into Node-RED, you should see the images automatically loaded into the dashboard when you view it. You can also replace the URLs and file paths to try using some different images if you'd like to. ## More things to try In this example, the images are static but it's simple to load images depending on the state of the flow. As mentioned in this article's introduction, you could display context aware images guiding the user of the dashboard to a specific location on a map, to complete a maintenance task. If you're interested in seeing examples of dynamic image loading please comment below. ## Conclusion Images can significantly enhance dashboards, but ensuring their proper display in different Node-RED hosting environments, especially within DevOps pipelines, can be challenging. The techniques discussed here enable effective use of images in dashboards, even within containerized setups. Additionally, if you are using FlowFuse, the new features simplify adding and managing static assets. I'd love to hear your comments and suggestions on this article. please tell us what you think about this article, and how you might use these techniques in the comments section below. # Creating a Historical Data Dashboard with InfluxDB and Node-RED Every new dashboard is met with the fast-following request, “can we save this data and somehow look back on it?” Yes, you can, and let’s use InfluxDB to make it happen! :product-update-note Edge devices are often polling sensors at regular intervals and are a perfect candidate to be paired with a database purpose-built for time-series data, like InfluxDB. Let’s capture some data, create a live chart, store the data, and then create a GUI for retrieving the data. Here’s a screenshot of the dashboard we will create, which is divided into two sections. The first section displays live data, while the second section consists of fields that enable users to query the database and retrieve historical data. Looking at the live data, the chart depicts a sinusoidal graph that represents the scale measurements used for quality assurance in the aggregate production process at an automated mining operation. The graph showcases fluctuations in weight over time, indicating variations in the samples being weighed. This monitoring process ensures the quality and consistency of the aggregates being produced. The historical data shows a snippet of this information that was retrieved from InfluxDB. !["Screenshot showing the historical dashboard"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/dashboard-1.png "Screenshot showing the historical dashboard") :br Here is a screenshot of the simple Node-RED flow to create that dashboard. We will dive into the details through this article, and, by the end, you will be able to create this flow yourself. ![Screenshot showing the historical dashboard flow](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/flow-2.png "Screenshot showing the historical dashboard flow") ## Capturing serial port data The live view is fed by data coming from a simple scale with a serial interface. This [Brecknell LPS-150](https://www.brecknellscales.com/wp-content/uploads/2022/09/LPS-Series_u_en_fr_501724-1.pdf){rel=""nofollow""} scale will auto power-on, remembers the last tare setting, and continuously sends its reading via RS-232, so it is a great unit to use for unattended IoT projects. On the Node-RED side, a serial node can be configured to capture this incoming data. If your device running Node-RED doesn’t have an RS-232 port, there are many variations of RS-232-to-USB cables to help you connect. This scale is sending data at a very high-speed interval so it is important to use a “delay” node before the rest of your flow gets bogged down. Below, I have configured the serial port node with the same settings that were used to set up the scale. These settings are commonly documented as "9600 8N1" in shorthand. In serial communication it is necessary for the two devices to have the exact same settings or the data becomes garbled. The incoming stream of ASCII text is divided using the hex value 0x0D, which corresponds to the return character. This character is used as a delimiter to separate the individual chunks of text within the incoming data stream. !["Screenshot showing the serial port node config"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/serial-port-node-3.png "Screenshot showing the serial port node config") :br With this “delay” node, we now have a new message from the scale at a rate of 1 msg per 5 seconds !["Screenshot showing the delay node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/delay-4.png "Screenshot showing the delay node properties tab") :br The debugger allows us to see the raw data as it is captured. !["Debugger"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/debugger-5.png "Debugger") :br Unfortunately, these values are not in a friendly form to work with. Ideally, we want our payload to just be a number, not this string with odd characters, extra spaces, and the units. !["Screenshot showing the change node setting payload"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/change-node-6.png "Screenshot showing the change node setting payload") :br :cta-image{alt="Power Workplace relies on FlowFuse for scalability, reliability and security audits - book a demo" cta="demo" src="https://flowfuse.com/images/cta/power-workplace-book-demo.png"} We need to extract the numeric part of the string using a regular expression with a “change” node and the JSONata expression `$number($match(msg.payload, /-?(\d+(\.\d+)?)/, 10).match)`. `$match` and `/-?(\d+(\.\d+)?)/` help the function pull out the numeric components of the string and `$number` parses these components to be an actual number data type. Here are the properties of the “change” node. When we look in the debugger we see the payload specified as a “number” and the value displayed in blue, both indications that we have successfully extracted the weight as the correct data type. !["Screenshot showing the output type in the debug panel"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/number-7.png "Screenshot showing the output type in the debug panel") ## Setting up serverless InfluxDB in the cloud Now we have some live data, let’s store it using InfluxDB. Below are the steps to set up an account with the InfluxDB free service. Navigate to {rel=""nofollow""} and let’s begin. Click on “Get Started for Free” under Cloud, InfluxDB Cloud Serverless. !["Screenshot showing the Influxdb cloud 'Getting started' button"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/run-influxdb-8.png "Screenshot showing the Influxdb cloud 'Getting started' button") For this example the Free plan will work fine. !["Screenshot showing the influxdb cloud tiers"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/free-influxdb-9.png "Screenshot showing the influxdb cloud tiers") Create a bucket to store the data. !["Screenshot showing the 'Go to buckets' button"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/bucket-influxdb-10.png "Screenshot showing the 'Go to buckets' button") !["Screenshot showing the 'Create Bucket' button"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/load-data-influxdb-11.png "Screenshot showing the 'Create Bucket' button") Generate a token to direct the calls from Node-RED to your InfluxDB account when they hit the InfluxDB server: !["Screenshot showing the 'Genrate token' button"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/token-influxdb-12.png "Screenshot showing the 'Genrate token' button") I selected “Generate All Access API Token,” but eventually you will want a custom, more restricted approach. !["Screenshot showing the 'prompt' asing to enter the description for token"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/generate-token-influxdb-13.png "Screenshot showing the 'prompt' asing to enter the description for token") Copy your token and do not share it! (mine will be deleted later) ## Connecting Node-RED to InfluxDB Navigate to “Manage Palette” in the Node-RED hamburger menu in the upper right corner of the flow editor. I did a search for InfluxDB and selected the most popular one, “node-red-contrib-influxdb” by looking at the number of downloads per week at {rel=""nofollow""}. When you are just starting out, it can be a smart decision to go with the popular option. The popularity indicates a level of trust and adoption within the community, making it a reliable choice for beginners !["Screenshot showing the influxdb node in the manage palette"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/install-contrib-influxdb-14.png "Screenshot showing the influxdb node in the manage palette") :br After installing this package you will see three new nodes in your flow editor. ![Screenshot showing the installed influxdb nodes in the palette](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/influxdb-nodes-15.png "Screenshot showing the installed influxdb nodes in the palette") Drag and drop the “influxdb out” node into your flow, double click on it, and start filling out the needed fields. The naming convention of `test<>` works well for initial setups to make it clear what names should go where. !["Screenshot showing the influxdb-out node config"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/influxdb-out-node-16.png "Screenshot showing the influxdb-out node config") :br It was a little unclear what URL to use with this serverless option, but I guessed it was the same as the URL for the InfluxDB resource center account page, “{rel=""nofollow""}” and it worked. Then, enter the API token that was generated earlier. !["image\_tooltip"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/influxdb-node-17.png "image_tooltip") :br The “influxdb out” node is now ready to start storing payloads. The documentation for the InfluxDB nodes at {rel=""nofollow""} gives more detail as to extra options, such as tags, that you might want to attach to your data being stored. In this simple example, we are just going to send the “influxdb out” node a number via the msg.payload. ![Screenshot showing the flow sending data to InfluxDB and to a dashboard chart widget"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/flow-influxdb-out-18.png "Screenshot showing the flow sending data to InfluxDB and to a dashboard chart widget") :br Here is a chart of the live data which is also being stored. !["Screenshot showing the dashboard with live data chart"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/live-data-chart-19.png "Screenshot showing the dashboard with live data chart") :br The InfluxDB Data Explorer helps you create a SQL call and allows you to run it right in the browser so you can verify that your data is being stored correctly. !["Screenshot showing the InfluxDB Explorer"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/influxdb-explorer-20.png "Screenshot showing the InfluxDB Explorer") :br ## Creating a historical data GUI Now we have our data being stored, but we aren’t quite finished. We still want an easy way to pull this information up and for it to be presented in a chart, just like the live data. Here is the Dashboard group we will create for this GUI. !["Screenshot showing the GUI for historical data"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/historical-data-21.png "Screenshot showing the GUI for historical data") And here is the flow to create it. !["Screenshot showing the flow of historical data GUI."](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/data-flow-22.png "Screenshot showing the flow of historical data GUI") A “template” node creates a convenient way to create a plain text output with variable properties within. Below you can see that `msg.query` is created from a string of text with “rangeStart” and “rangeEnd” dynamically inserted using the “mustache” syntax. More information about how to query InfluxDB can be found here: {rel=""nofollow""}. !["Screenshot showing the template node"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/template-node-23.png "Screenshot showing the template node") :br Using the "Form" dashboard node is an easy way to collect all the required information for our query. We need to be able to enter in a date and time to start gathering the data, and a window to know how long a range of values to pull. !["Screenshot of the form widget"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/form-node-24.png "Screenshot of the form widget") Here is the code from the “time/date” function node. A bit of juggling of local time versus UTC time is needed to allow the user to intuitively query the correct data for their timezone. !["Screenshot of the function node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/function-node-25.png "Screenshot of the function node properties tab") :br Here is the “change” node used to create the msg.rangeEnd. The JSONatta expression is `$fromMillis($toMillis(msg.rangeStart) + msg.payload.window * 60 * 1000)`. The expression combines the milliseconds from the `msg.rangeStart` with the calculated milliseconds in the “Window (minutes)” from the GUI. !["Screenshot of the change node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/change-node-26.png "Screenshot of the change node properties tab") :br Now that the query is coming back from InfluxDB, let’s break down how to transform this data object into one that can be read by the “chart” node. Below, we see on the left column what the object looks like from InfluxDB and on the right we see how it must be structured to be viewed in the chart. !["Screenshot showing the formated data in the debug panel"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/data-format-27.png "Screenshot showing the formated data in the debug panel") Rob Marcer has a great article on working with persistent chart data found here: [/blog/2023/05/persisting-chart-data-in-node-red/](https://flowfuse.com/blog/2023/05/persisting-chart-data-in-node-red/). We can use a series of nodes from the Node-RED core package to transform this data. !["Screenshot showing the flow to transform the data"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/transform-data-28.png "Screenshot showing the flow to transform the data") :br First, a “switch” node is used to determine if the response InfluxDB contains any data so that we can either format the data properly, or clear the chart and indicate “No Data.” !["Screenshot of the switch node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/switch-node-28.png "Screenshot of the switch node properties tab") :br The “Label” field in the “chart” node can also be dynamically created with the mustache syntax. !["Screenshot of the chart widget setting label dynamically"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/chart-node-29.png "Screenshot of the chart widget setting label dynamically") :br If the “is not empty” “switch” node sees an empty payload, this “change” node sets the payload to an empty array, clearing the chart, and sets the `msg.title` to “No Data” so users know their query, though successful, returned an empty set of values. !["Screenshot of the change node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/change-node-30.png "Screenshot of the change node properties tab") :br The parameters for the “split” node can be left as-is. !["Screenshot of the split node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/split-node-31.png "Screenshot of the split node properties tab") :br In the “chartData” “change” node, will pull out the two values we need for the chart, milliseconds since the UNIX epoch for the x-value and the measurement from the scale for the y-value. A simple JSONatta expression helps us transform the date from a string to milliseconds for the x-value. !["Screenshot of the change node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/change-node-32.png "Screenshot of the change node properties tab") :br The “join” node just needs to be set to “Combine each” msg.chartData object and configured “to create” an array. !["Screenshot of the join node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/join-node-33.png "Screenshot of the join node properties tab") :br The final “change” node, “format,” is where we prescribe the format needed for the “chart” node, [{"series": [""],"data":\[\[]],"labels": [""] }], and finally we insert our `msg.chartData` array into that structure. Notice `msg.title` is now set to “Data Received.” !["Screenshot of the change node properties tab"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/change-node-33.png "Screenshot of the change node properties tab") :br And, there you have it. You can query the same range of data found on the live chart to ensure the code is working and then you can use the dashboard to pull up historical data, way in the past from what is shown on the live chart. !["Screenshot of the dashboard showing the historical and live data chart"](https://flowfuse.com/blog/2023/07/images/influxdb-historical-data/final-dashboard-34.png "Screenshot of the dashboard showing the historical and live data chart") Your current version is clear and concise, great for a blog or call-to-action section. Here's a slightly refined version with improved flow, punctuation, and tone for polish, while keeping the structure intact: ## Final Thoughts You’ve successfully built a powerful dashboard that captures real-time data and stores it in InfluxDB for historical analysis. This setup provides valuable insights into your process data and helps identify trends over time. If you are planning to run this dashboard in production, a few important questions may come to mind: How do you ensure it runs reliably 24/7? How do you deploy it to other locations? What happens if someone accidentally breaks the flow? This is where **FlowFuse** comes in. It takes your existing Node-RED flows and adds: - Automatic backups and instant recovery - Easy deployment to multiple locations - Version control for safe and trackable updates - Team collaboration without conflicts - Remote instance management, and much more Your flows continue to work just as they are, FlowFuse simply makes them production-ready. [Try FlowFuse free for 30 days](https://app.flowfuse.com/account/create){rel=""nofollow""} and see how it transforms Node-RED into a scalable, enterprise-ready platform. # FlowFuse is now available on AWS Marketplace Many customers want to run FlowFuse in their own cloud environment, AWS being a great example. Today we're excited to announce that FlowFuse is now available from the [AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-3ycrknfg67rug?sr=0-1&ref_=beagle&applicationId=AWSMPContessa){rel=""nofollow""}. This makes it very easy for customers to install and run Node-RED and FlowFuse within minutes. FlowFuse allows organizations to reliably deliver Node-RED applications in a continuous, collaborative and secure manner. Customers running FlowFuse on AWS Cloud will benefit from FlowFuse's features, including: - Team collaboration for Node-RED developers, allowing multiple developers to work together on a single Node-RED instance, including the ability to have an audit log of changes. - DevOps deliver pipelines that support a software development lifecycle for Node-RED development. Pipelines can be setup to establish development, test and production environments for Node-RED instances. - Snapshot of Node-RED instances to create a version history of changes to Node-RED applications. This also includes the ability to rollback to a previous version of a Node-RED instance. FlowFuse on the AWS Marketplace is available under the Apache License c2.0 open source license. Customers can use FlowFuse free of charge but will need to pay AWS EC2 usage for hosting FlowFuse. FlowFuse offers commercially licensed Premimum and Enterprise tiers that includes enterprise oriented features, including: - 24/7 technical support - Single Sign-on - High Availability for Node-RED applications - Remote device management Customers can easily upgrade to the premium or enterprise tier by obtaining a commercial license from FlowFuse. To understand what FlowFuse can do for your use-case, please [book a demo](https://flowfuse.com/book-demo). FlowFuse on AWS Marketplace is available [immediately](https://aws.amazon.com/marketplace/pp/prodview-3ycrknfg67rug?sr=0-1&ref_=beagle&applicationId=AWSMPContessa){rel=""nofollow""}. Please refer to our [documentation](https://flowfuse.com/docs/install/docker/aws-marketplace/#installing-flowfuse-from-aws-market-place) and give it a try and let us know what you think. # Community News August 2023 Welcome to the FlowFuse newsletter for August 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community. ## New Release Last week we released FlowFuse 1.10, featuring improvements to our device management solutions and the new ability to import environment variable templates. Read about the details of FlowFuse 1.10 in our [release announcement](https://flowfuse.com/blog/2023/08/flowforge-1-10-release/). ## Upcoming events ### Getting Started with OPC-UA and Node-RED OPC-UA is a popular communication protocol used to communicate industrial data between different types of hardware and software. Our next webinar show how to use Node-RED to create an OPC-UA client that can read OPC data and visualize the data in Node-RED. We are glad to welcome Mika Karaila, Research Director @ Valmet Automation and creator of the OPC-UA nodes, as our webinar speaker. [Sign-up today](https://flowfuse.com/webinars/2023/getting-started-opcua-node-red/) to join us on July 27. ## From our Blog - 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](https://flowfuse.com/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](https://flowfuse.com/node-red/protocol/opc-ua/) - Part 3: [How to Build an OPC UA Client Dashboard in Node-RED](https://flowfuse.com/blog/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/) - [First Pre-Alpha Release of the new Node-RED Dashboard](https://flowfuse.com/blog/2023/07/dashboard-0-1-release/) - update on FlowFuse's work to develop the next generation of Node-RED dashboard. - [How to add images to Node-RED dashboards when using FlowFuse](https://flowfuse.com/blog/2023/07/images-in-node-red-dashboards/) - some tips on how to add images to a dashboard when running Node-RED instances in a docker environment, like FlowFuse. - [Creating a Historical Data Dashboard with InfluxDB and Node-RED](https://flowfuse.com/blog/2023/07/influxdb-historical-data/) - an in-depth article on how to store historical data in InfluxDB that can be visualize with Node-RED dashboard. ## From the Community - **Featured Node**: [Buffer Parser](https://flows.nodered.org/node/node-red-contrib-buffer-parser){rel=""nofollow""} - a really useful node for parsing buffers/arrays that are common in industrial data. ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - [Contract Front-End Engineer – Node-RED Dashboard](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} # Dashboard 2.0 - Community Update Welcome to the latest Node-RED Dashboard 2.0 update. We've added lots of new widgets, cleaned up compatibility issues alongside Dashboard 1.0 and made strides to improve the events system linking the Node-RED editor with the Dashboard. I firstly need to begin with a *"Thank You"* to the dozens of pre-alpha users we've had so far. Thanks for being patient whilst we're shipping fast and breaking things. We've had some great feedback, and we're working hard to implement is as best as possible. With all of the changes we've been making, we've also made the decision to jump to minor version numbers, and so, **0.1.0 is available now**. Below you'll find a summary of the changes we've made since our [last community update](https://flowfuse.com/blog/2023/07/dashboard-0-1-release). ## New Widgets ### Template ([docs](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html)) Steve has been doing some incredible work on the new `ui-template` widget. This widget allows you to create your own custom components using raw HTML, but also works with any of the components in the [Vuetify](https://vuetifyjs.com/en/components/all/){rel=""nofollow""} component library. It's a powerful tool that will enable users to be creative with their own widgets that are not currently available with the standard set of widgets. ![Examples of ui-template](https://dashboard.flowfuse.com/images/node-examples/ui-template.png) The Template node also provides access to two built-in functions that can be used to send data back to Node-RED: - **send(msg)**: Outputs a message (defined by the input to this function call) from this node in the Node-RED flow. - **submit()**: Send a `FormData` object when attached to a `
` element. The created object will consist of the `name` attributes for each form element, corresponding to their respective `value` attributes. ### Toggle Switch ([docs](https://dashboard.flowfuse.com/nodes/widgets/ui-switch.html)) ![Examples of ui-switch](https://dashboard.flowfuse.com/images/node-examples/ui-switch.png) Adds a toggle switch to the user interface that can be rendered with a label, and traditional toggle switch, or, as in Dashboard 1.0, can be a square element with an icon & color provided. ## Fixes & Other Changes ### Sidebar As requested on multiple occasions by the community when we released v0.0.4 of Dashboard 2.0, we've now added a side menu, as per Dashboard 1.0. Currently, this *just* provides a link to the Dashboard UI, but gives us a canvas on which to expand functionality in the future. ### Improved Events System We've re-structured the hierarchy of the events system to make it more streamlined. Now, the `ui-base` manages comms via single channels dedicated to each event type, and the widget's ID is then used as a topic. Previously, we had a separate channel for each `action:id`. If you're interested in learning more about our events architecture, you can read about it [here](https://dashboard.flowfuse.com/contributing/guides/events.html){rel=""nofollow""} in the docs. ### Documentation Updates It's not glamorous, but it's important. We've made sure that all documentation and help text inside Node-RED is fully up to date for the Dashboard 2.0 nodes. We've also include rendered examples for all widgets in our [online documentation](https://dashboard.flowfuse.com/){rel=""nofollow""} too. We've also made sure that any legacy options that had been transferred over from Dashboard 1.0 that haven't been fully implemented yet are temporarily hidden. This means, any options you're seeing, *should* be working. If they're not - it's a bug. ## What's Next? We have a lot of things to keep us busy, we are documenting them all in GitHub, and have made public our [planning board](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""}. You can see what we're working on, what's coming up next, and what we've got planned for the future. As always, we're open to ideas, feedback & contributions. If you'd like to get involved, please check out our GitHub Repository [here](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. ## Join Our Team If you'd like to be paid to directly contribute to Dashboard 2.0, we are hiring for a 2-3 month position to do just that: - [Contract Front-End Engineer – Node-RED Dashboard](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} # FlowFuse 1.10 Release Now Available FlowFuse 1.10 release includes improvements to device management and importing environment variable templates. ## Import Environment Variable Templates [#2372](https://github.com/FlowFuse/flowfuse/issues/2372){rel=""nofollow""} FlowFuse 1.10 now allows users to import environment variable templates. This makes it much easier and less error prone to maintain and add new environment variables to Node-RED instances running on FlowFuse. ## DevOps Pipelines now can include devices [#2243](https://github.com/FlowFuse/flowfuse/issues/2243){rel=""nofollow""} DevOps pipelines have proven very popular for creating dev/test/production environments for Node-RED flow development. Now, devices can be associated with a pipeline so when a snapshot is created it can be pushed to all the devices associated with the pipeline. This will improve the overall quality and reliability of Node-RED development for remote devices. ## Devices can now access the team library [#2294](https://github.com/FlowFuse/flowfuse/issues/2294){rel=""nofollow""} Team libraries allow Node-RED development team to share common flows and nodes through a shared library. Until the 1.10 release, Node-RED running remotely on a device did not have access to the team library. This limitation is now removed so device development can benefit from reusing standard flows. ## Other New Features - Add description of device type field [#2428](https://github.com/FlowFuse/flowfuse/issues/2428){rel=""nofollow""} - Improve reliability of device editor [#2483](https://github.com/FlowFuse/flowfuse/issues/2483){rel=""nofollow""} - Improve error feedback from device editor tunnel [#2473](https://github.com/FlowFuse/flowfuse/issues/2473){rel=""nofollow""} ## Bug Fixes - Improve visualization of Last Seen & Last Known with large amounts of devices. [#2380](https://github.com/FlowFuse/flowfuse/issues/2380){rel=""nofollow""} - Fix billing information error in FlowFuse Cloud [#2416](https://github.com/FlowFuse/flowfuse/issues/2416){rel=""nofollow""} - Fix T\&C checkbox on sign-up page [#2419](https://github.com/FlowFuse/flowfuse/issues/2419){rel=""nofollow""} ## Community Contributions Thanks to our community members for their contributions to this release. - [dfulgham](https://github.com/dfulgham){rel=""nofollow""} - Added support for annotation substitutions [#95](https://github.com/FlowFuse/flowforge-driver-k8s/pull/95){rel=""nofollow""} - [elanaviter](https://github.com/elenaviter){rel=""nofollow""} - Editors: allow optional service account linkage [#92](https://github.com/FlowFuse/flowforge-driver-k8s/pull/92){rel=""nofollow""} ## What's next? We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute: - **Roadmap Overview**: Check out our [Product Roadmap Page](https://flowfuse.com/changelog/) to see what we're planning for future updates. - **Entire Roadmap**: Visit our [Roadmap on GitHub](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""} to follow our progress and contribute your ideas. - **Feedback**: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any [issues on GitHub](https://github.com/FlowFuse/flowfuse/issues/new/choose){rel=""nofollow""}. Together, we can make FlowFuse better with each release! ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.10. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. Additionally you can go the the [community forum](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""} if you have any feedback or feature requests. # FlowForge is now FlowFuse We are happy to announce that FlowForge is changing its name to FlowFuse. Changing our corporate identity wasn’t our top priority but a recent trademark challenge has promoted us to create a new brand for our company. :cta-image{alt="Walk through your FlowFuse setup with our team - book a demo" cta="demo" src="https://flowfuse.com/images/cta/book-a-demo.png"} We believe that this new name better reflects our core mission and aspirations. Just as electricity fuses elements to create energy, the FlowFuse platform fuses together data, ideas, processes, and technologies to generate a powerful force of innovation and transformation. Selecting a new name for a company or product is never easy. Some of the requirements we set for a new name were: 1) it should be reasonably close to FlowForge so it will be easier to transition the brand identity, 2) it should even better reflect our mission, and 3) keeping the FF acronym will help with environment variable prefixes :-). I think we have accomplished all the above and am delighted to move forward as FlowFuse. **What to Expect Next** Our team is heads down building a platform to allow organizations to reliably deliver Node-RED applications in a continuous, collaborative, and secure manner. We'll continue to release regular updates packed with new features and enhancements that keeps moving us towards this goal. A complete rebranding is a big piece of work - today marks the start of a process as we introduce the new FlowFuse name and updated [website](https://flowfuse.com). Over the next number of days and weeks we'll continue to roll this change out, including our social media accounts and other accounts. The product branding will be changed over the next couple of releases, including FlowForge Cloud and FlowForge open source edition. The underlying software will be the as-if you’re running the next FlowForge release, though now called FlowFuse. We'll share more technical details of this when any changes are made. We are excited by the future FlowFuse presents to our customers and the industry. Node-RED and FlowFuse is a powerful combination that gives access to industrial data to transform organizations and drive forward innovation across industries. Join us as we continue our journey. # FlowFuse 1.11 makes it easier to get started with FlowFuse and Node-RED FlowFuse 1.11 introduces a new starter tier for FlowFuse Cloud that makes it easier to get started with FlowFuse and Node-RED. :product-update-note ## New FlowFuse Cloud Starter Tier [#2328](https://github.com/FlowFuse/flowfuse/issues/2328){rel=""nofollow""} It is now easier for Node-RED developers to get started with FlowFuse and Node-RED. The new starter tier allows developers to use two Node-RED instances and two remote device deployments. Ideal for creating proof of concepts or running a home automation system with Node-RED. FlowFuse provides a cloud hosted version of Node-RED so developers don't need to worry about Node-RED installation or operation. This makes it a lot easier to get started with Node-RED and easier to maintain a running instance. ## FlowFuse API access now possible via personal access tokens [#14](https://github.com/FlowFuse/flowfuse/issues/14){rel=""nofollow""} FlowFuse APIs are now accessible via personal access tokens (PAT). This makes it possible to create automation scripts that interact with the FlowFuse platform using the API and authenticate the scripts with the PAT. ## Usability Improvements to Device Management [#2294](https://github.com/FlowFuse/flowfuse/issues/2334){rel=""nofollow""} A number of usability improvements have been added to the FlowFuse device management solution to make it more flexible and intuitive to use. These improvements include being able to associate devices at the application level allowing for easier editing of Node-RED instances on edge devices. ## FlowFuse Rebranding [#119](https://github.com/orgs/FlowFuse/projects/1?pane=issue&itemId=34719640){rel=""nofollow""} Earlier in August, [FlowForge announced](https://flowfuse.com/blog/2023/08/flowforge-is-now-flowfuse/) a change to our company and product name to FlowFuse. Work has begun to change the product branding to FlowFuse. The UI has been rebranded and the remaining points will be changed in the next release. ## Other New Features - Add ability to add a description to an application and display it in the portal [#2279](https://github.com/FlowFuse/flowfuse/issues/2279){rel=""nofollow""} - UI Improvements to device management [#2427](https://github.com/FlowFuse/flowfuse/issues/2427){rel=""nofollow""} - Improve landing page for documentation [#842](https://github.com/FlowFuse/website/issues/842){rel=""nofollow""} - Restructure of user interface navigation [#2474](https://github.com/FlowFuse/flowfuse/issues/2474){rel=""nofollow""} ## Bug Fixes - Device running old snapshot [#132](https://github.com/FlowFuse/device-agent/issues/132){rel=""nofollow""} ## What's next? We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute: - **Roadmap Overview**: Check out our [Product Roadmap Page](https://flowfuse.com/changelog/) to see what we're planning for future updates. - **Entire Roadmap**: Visit our [Roadmap on GitHub](https://github.com/orgs/FlowFuse/projects/5){rel=""nofollow""} to follow our progress and contribute your ideas. - **Feedback**: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any [issues on GitHub](https://github.com/FlowFuse/flowfuse/issues/new/choose){rel=""nofollow""}. Together, we can make FlowFuse better with each release! ## Try it out We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running 1.11. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. Additionally you can go to the [community forum](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""} if you have any feedback or feature requests. # Why the Automation Pyramid blocks digital transformation - The Role of Unified Namespace A few years ago, I wrote an [article](https://www.linkedin.com/pulse/iiot-circle-marian-raphael-demme/){rel=""nofollow""}, in German, detailing my understanding of how the Automation Pyramid, a widely adopted reference model for the IT landscape of manufacturing firms, is essentially hindering digital transformation. Now, as conversations around the Unified Namespace (UNS) and particular frameworks continue to evolve, I revisit my earlier notions, review the latest updates to reference frameworks, and update my article. ## The Pyramid’s Dilemma The Automation Pyramid is grounded in the standard [ISA-95](https://www.isa.org/products/ansi-isa-95-00-01-2010-iec-62264-1-mod-enterprise){rel=""nofollow""}, which aligns with [IEC 62264](https://www.iso.org/standard/57308.html){rel=""nofollow""} and [DIN EN 62264](https://www.beuth.de/en/standard/din-en-62264-1/207270059){rel=""nofollow""}. It delineates the functional hierarchy within a manufacturing enterprise. Over 25 variations of the Automation Pyramid exist in academic literature, all of them fundamentally mapping to the same core concept, tracing back to the [Computer-integrated manufacturing](https://en.wikipedia.org/wiki/Computer-integrated_manufacturing){rel=""nofollow""} (CIM)-Pyramid of the 1970s. Although ISA-95 does not explicitly refer to a pyramid, it introduces five functional hierarchical levels often visualized as a pyramid. #### ISA-95 - Visualization ![ISA95](https://flowfuse.com/blog/2023/08/images/UNS/ISA95.svg) #### Automation Pyramid - Visualization ![Automation Pyramid](https://flowfuse.com/blog/2023/08/images/UNS/Automation-Pyramid.png) Source: Katti, Badarinath. (2020). Ontology-Based Approach to Decentralized Production Control in the Context of Cloud Manufacturing Execution Systems. 10.13140/RG.2.2.11486.46402. A notable critique of ISA-95 is the absence of some operational functions and hierarchical levels commonly seen in manufacturing, leading to a rigidity that limits its applicability. This inflexibility has been acknowledged in a more recent framework, called the ["Reference Architectural Model Industry 4.0"](https://www.isa.org/intech-home/2019/march-april/features/rami-4-0-reference-architectural-model-for-industr){rel=""nofollow""} RAMI 4.0 ([IEC PAS 63088](https://www.beuth.de/en/norm/pd-iec-pas-63088/272832590){rel=""nofollow""}). As a result, the authors' introduced a ["Smart Grid Architecture Model"](https://syc-se.iec.ch/wp-content/uploads/2019/10/Reference_Architecture_final.pdf){rel=""nofollow""} (SGAM) with three primary dimensions: Life Cycle & Value Stream ([IEC 62890](https://www.vde-verlag.de/iec-normen/248992/iec-62890-2020.html){rel=""nofollow""}), Hierarchy Levels ([IEC 62264](https://www.iso.org/standard/57308.html){rel=""nofollow""} and [IEC 61512](https://www.vde-verlag.de/iec-normen/216764/iec-61512-4-2009.html){rel=""nofollow""}), and six main layers displaying the functional architecture of the asset and the separation into physical and digital world. :video{ariaLabel="RAMI4.0" autoPlay="true" height="292" loop="true" muted="true" playsInline="true" preload="none" width="520"} However, my primary critique revolves around another issue – the structure and proposed communication methodology. Models based on layers, where each tier represents a functional area and could be covered by one or more applications, almost always lead to three fundamental problems: ### Problem 1: Information Loss and Transaction Costs In the traditional model, data collection flows upward from Levels 0 to 4, while planning goes downward from Level 4 to 0. Information traversing from Level 0 to Level 4 has to pass through at least four stages. Despite the theoretical lossless transmission of information, the practical scenario inevitably results in some degree of information loss between levels. The result is that the original information from Level 0 arrives at Level 4 late, altered, or not at all. **Example:** In a manufacturing plant, multiple sensors at Level 1 detect a sudden event. By the time this information passes through intermediary layers (e.g. PLC, SCADA, MES) to reach Level 4 where a planning decision can be made, it is delayed and distorted due to the multiple transitions. The factory might suffer damage before proper actions are taken because the original data didn't arrive on time or at all. ### Problem 2: The Expense of One-to-One Connections The Automation Pyramid is based on different layers. Consequently, one-to-one connections between IT systems become a necessity for data transfer between levels. For example, Level 3 IT systems need at least two connections to the adjacent levels. This can lead to thousands of one-to-one interfaces between IT systems, incurring exorbitant costs for projects and maintenance. **Example:** In a semiconductor company, the Manufacturing Execution System (MES) serves as a critical intermediary in the Automation Pyramid. It must be integrated both with PLCs at the lower level for real-time control and with the ERP system at the higher level for business planning. This complex integration leads to the creation of numerous one-to-one connections. Furthermore, in implementing Industry 4.0 use cases like analytical applications, MES data is often required, creating even more connections. The multitude of connections complicates the system, making changes extremely difficult and maintenance intensive. This inflexibility becomes a barrier to adaptability and growth, hindering the efficient digital transformation of the manufacturing process. ### Problem 3: AI's Dependence on Data Artificial intelligence (AI) requires extensive, well-organized data. Given the current architecture, data would have to be collected and prepared from case to case for each individual system and level. This would invariably lead to numerous new one-to-one connections, offering no flexibility. Hence, AI and the Automation Pyramid can only collaborate in a significantly restricted manner. **Example:** A car manufacturing firm aims to leverage a neural network for predictive maintenance. Within the constraints of the existing Automation Pyramid's architecture, the positioning for such an application is nonexistent. To train the neural network and subsequently analyse the data, a consolidation of varying hierarchical data is essential, such as sensory input, maintenance records, production scheduling plans, etc. Under the current architecture, the introduction of this application precipitates the creation of a multitude of new one-to-one connections. Consequently, it underscores the pressing need to rethink the structural paradigms. ## IIoT Circle and Unified Namespace To overcome the limitations of traditional industrial data architecture, a paradigm shift towards a modern distributed architecture is necessary. Rather than allowing data to exist in silos within and across layers of the technology stack, data should be made accessible in a unified manner, creating a single, centralized repository. This approach facilitates a single centralized source for all enterprise systems to access the required data for their operations. This framework, which I have been calling the IIoT Circle, modernizes the original idea of the Automation Pyramid. A "Unified Namespace" operates as the core element that processes, and permits data streams to be loaded and exported from other systems. All other applications communicate exclusively through the Unified Namespace, requiring only a single interface to be maintained per application. ![IToT Circle Image](https://flowfuse.com/blog/2023/08/images/UNS/IIoT-Circle.svg) In essence, Unified Namespace serves as the main data exchange hub within an organization. It structures, organizes, and maintains a real-time flow of data from a variety of sources, becoming the indisputable source of truth across the business. It simplifies data integration, eliminating the frequently convoluted, layered approach of traditional data systems. ### Single Source of Truth The Unified Namespace breaks down the linear and deterministic data structure, which create data silos restricted to their specific systems. Instead, Unified Namespace centralizes data from across the entire organization. This results in a 'single source of truth' - a consolidated, current, and comprehensive overview of the organization's data. ### The Organizational Structure Unified Namespace organizes data using a semantic hierarchy, similar to a meticulously arranged file share system. It can use the [ISA-95 part 2](https://www.isa.org/products/ansi-isa-95-00-02-2018-enterprise-control-system-i){rel=""nofollow""} or the RAMI 4.0 Hierarchy Level standards to structure the hierarchy. This data organization facilitates navigation, management, and decision-making. ### The Pub-Sub Approach The Publish-Subscribe (Pub-Sub) model facilitates communication that decouples the sender (publisher) from the receiver (subscriber), providing an efficient communication protocol to avoid one-to-one connections. It offers flexibility and scalability as it allows for one-to-many and many-to-one communications, enabling data to flow freely between systems. ## A Necessity for Open Source Moreover, in this discourse on the Unified Namespace, we cannot overlook the role of open-source. Owning foundational digital services, such as the Unified Namespace, is a necessity for any corporation embarking on its digital transformation journey. This ownership provides a solid foundation, allowing companies to chart their destinies. To avoid the constraining bounds of vendor lock-in, which can significantly limit a company's digital capabilities; open-source or self-developed software offers the best recourse. By its nature, open-source promotes transparency, collaboration, and freedom of use. These aspects are fundamental to fostering innovation and continuous improvement. As exemplified by the [MING Stack](https://flowfuse.com/blog/2023/02/ming-blog/), open source software can and should be incorporated into every level of the hierarchy. ## Summary – Advancing Current Standards The lag of standards behind the latest innovation is an open secret, a problem rooted in the nature and development of these standards. However, maintaining and updating these standards remains crucial as many people adhere to them. ISA-95 Part 6 mentions a Messaging Service Model (MSM) and proposes a "publish-subscribe" model as an option for transactions. This is a great step in the right direction. My recommendation for ISA-95 is to further develop Part 6 to clearly delineate the implementation pattern of the Unified Namespace. Additionally, ISA-95 Part 1 should make explicit references to the communication pattern detailed in Part 6 and transition from a layer model to a cycle, with the Unified Namespace as an integral part of the framework. RAMI 4.0's Communication Layer is rather abstract. It suggests the use of OPC-UA for everything in manufacturing, from "Product" to "Work Center". For "Enterprise" and "Connected World", it states "still undecided". My improvement suggestion is to define the "Communication Layer" new and to be more technology-agnostic. Be more explicit about what needs to be done and more flexible about how to do it. # Our Open Source offering is a tier, not our competition More than once we’ve been in discussion with prospective customers on what tier is the right tier for their current Node-RED adoption. The question is likely to come up "Why wouldn't we just use the open source version of FlowFuse?". The implicit discussion created is one that is alike the question: “Why wouldn’t we go with your competition?”. For FlowFuse, and most other open-core companies like us, the open licensed and free to use core is tier, not competition. In the traditional sense, the prospective customer is right. By the definition, a customer **buys** goods or services by exchanging it for **money**. For FlowFuse’s open-source edition, which is free as in beer and free as in speech, no money changes hands. It can be installed and run by anyone. Once the software is being installed and used, we consider we've gained a new customer. There’s an agreement in place, the Apache 2.0 license, and value is obtained by the customer. The only missing component compared to an ‘ordinary’ sale is the lack of money from the customer to FlowFuse. The fact that no monetary value is exchanged, like the situation where a customer picks the competitor, doesn’t make the open source tier competition. It is just a free tier. Another reason it's really a tier is that the core of the product is the same. In many open-core products, the path to upgrade from the open source license product to the paid tier is much alike customers are used to on SaaS models. In the reals of self-managed software that's mostly uploading a license and at times a few configuration steps. Furthermore, the open tier is a tier as the customer choses to not adopt all capabilities. They're leaving value on the table. Either this is because it's not quite clear what the value is or if the higher tiers provides enough business value to warrant the expense. Or the adoption journey for the customer doesn't yet require the full featured tiers. What’s unique about open source software, is that customers can exchange value towards the company and community building the software in other forms: by opening issues, updating documentation, advocating for the OSS variant, among other ways. While this is not **money**, it is significant for a young company like FlowFuse. ## Challenges with a open core free tier That's not to say that an open source tier is a silver bullet for a company. For one, it's hard to track how many users a software package has, and who these users are. For example; FlowFuse has [Telemetry](https://flowfuse.com/docs/admin/telemetry/#usage-telemetry), though it can be turned off. Nor do we know who hosts this software. Another challenge is around product and feature packaging. At FlowFuse and other open core companies it's uncommon to move features from the open tier to paid tier only. If this choice has been made it's a done deal, even when the product team got it wrong. Usually the initial thoughts are therefor to move all features into the paid tiers. However, this hampers long term growth as adoption of paid features are adopted later or not at all. We follow the [Open-Core buyer based model](https://opencoreventures.com/blog/2023-01-open-core-standard-pricing-model){rel=""nofollow""} to segregate the value, about which I'll write a post next time. Photo by [Matt Hardy](https://unsplash.com/@matthardy). # FlowFuse announces a Node-RED stack for Industry 4.0 applications on ctrlX AUTOMATION FlowFuse is pleased to announce they are now offering Node-RED plus select third party nodes from the Bosch Rexroth ctrlX World marketplace. FlowFuse is pleased to announce they are now offering Node-RED plus select third party nodes from the [Bosch Rexroth ctrlX Store](https://developer.community.boschrexroth.com/t5/Store-and-How-to/FlowFuse-Node-RED/ba-p/82135){rel=""nofollow""} marketplace. Rexroth customers building Industry 4.0 applications will now have a trusted vendor, in FlowFuse, to provide support and updates for using open source Node-RED in production. By partnering with FlowFuse, customers can reduce their risk of using Node-RED in production by relying upon FlowFuse Node-RED experts to assist with any development or production issues. [Node-RED](https://nodered.org/){rel=""nofollow""} is a popular open source low-code development environment widely used in industries for collecting and processing industrial data to deliver Industry 4.0 applications. FlowFuse is uniquely positioned to partner with ctrlX customers looking to use Node-RED in production. FlowFuse CTO Nick O’Leary is the co-creator and project leader of Node-RED. FlowFuse employs many Node-RED experts who have years of experience helping customers with successful deployment of Node-RED applications. The FlowFuse package offered in the ctrlX World marketplace will provide ctrlX customers the following benefits: - Support for Node-RED development and production deployments - Support for third party nodes of popular industrial protocols including: Modbus, OMRON, S7 and MC Protocol. FlowFuse is the only vendor providing professional technical support for Node-RED on the ctrlX platform. The package is available today at ctrlX World. Interested customers should contact their Rexroth sales representative for purchasing details. Interested customers can also contact FlowFuse directly at for additional information about the offering. # How ChatGPT improves Node-RED Developer Experience ChatGPT has the potential to have a significant impact on the Node-RED community. It is a powerful language model that can be used to generate flows, interpret them, and provide documentation, maybe soon even write the flow! The combination of ChatGPT, or generative AI at large, with Node-RED can significantly improve the developer experience with Node-RED. In this post we’ll review what the community has already built. ## How generative AI like ChatGPT is used for Node-RED ### Function node <3 ChatGPT ChatGPT, and other models, can write code for you, much like [GitHub CoPilot](https://github.com/features/copilot){rel=""nofollow""} or [GitLab Duo](https://about.gitlab.com/gitlab-duo/){rel=""nofollow""}. As Node-RED is ‘low-code’ the ability for generative AI to write the required code for you creates a paradigm shift to ‘no-code’! ![Example of Chat GPT to generate contents of a function node](https://flowfuse.com/blog/2023/09/images/chatgpt-fcn-example.png) At FlowFuse we’ve written about this [before](https://flowfuse.com/blog/2023/05/chatgpt-nodered-fcn-node/), and published a [plugin](https://github.com/FlowFuse/node-red-function-gpt){rel=""nofollow""}. This node allows flow developers to be more productive and efficient. While this works only for the function node, there’s countless other possibilities to describe a flow in text and import a ChatGPT generated flow that are on the horizon! ### Flow Interpretation When developing larger projects with multiple tabs, it’s important to understand what each tab contributes to the full project. This problem is compounded when the flows are developed by a team or the time between the flow was last updated is higher. ![ChatGPT Flow Interpretation](https://raw.githubusercontent.com/node-red-jp/node-red-contrib-plugin-chatgpt/main/infotab.png) [Kazuhito-san](https://www.linkedin.com/in/kazuhitoyokoi/){rel=""nofollow""} wrote a module for Node-RED to interpret the flow, nodes, and their order into a well structured documentation. Through a click of a button it's generated by the well-known OpenAI model. This is especially interesting as it's thus able regenerate it when changes were made by the developers. It’s a plugin that requires very little setup, and can be found in the [flow library](https://www.npmjs.com/package/node-red-contrib-plugin-chatgpt){rel=""nofollow""}. ### Lots of plugins The ecosystem of Node-RED has always been a fast adopter of new technology. There's nodes for [ChatGPT](https://flows.nodered.org/node/node-red-contrib-chatgpt){rel=""nofollow""}, [Google's Bard](https://flows.nodered.org/node/node-red-contrib-bard){rel=""nofollow""}, and many more. These plugins genernally let you build on top of these models, and don't nessecairly improve the developer experience. It's however a great source of inspiration! ### Further discussion These were three examples of how generative AI is used in the Node-RED community. Please let us know if you're using ChatGPT or other AI models with Node-RED? And what would be the killer feature for Node-RED and AI? # Community News September 2023 Welcome to the FlowFuse newsletter for September 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community. ## New Name The big news this month is that [FlowForge is now FlowFuse](https://flowfuse.com/blog/2023/08/flowforge-is-now-flowfuse/). Yes, we have changed our name but we are still focus on delivering great products for the Node-RED community. :product-update-note ## New Releases - Node-RED 3.1 has been [released](https://nodered.org/blog/2023/09/06/version-3-1-released){rel=""nofollow""}. Among the changes are Mermaid chart support, locking flows, and much more. - Last week, FlowFuse 1.11 was released. This release included personal access tokens for FlowFuse API and new tiers for FlowFuse Cloud, including a new starter tier. Check out the details in our [announcement](https://flowfuse.com/blog/2023/08/flowfuse-1-11-release/). ## Upcoming events ### Celebrate 10 Years of Node-RED and What’s New in 3.1 and Beyond Hard to believe that Node-RED was launched 10 years ago. We want to celebrate this great accomplishment and also show off the new Node-RED 3.1 release. Join Nick O'Leary for the 10 year celebration and here what is coming next. [Sign-up today](https://flowfuse.com/webinars/2023/node-red-10-years/) to join us on September 21. ## From our Blog - The Node-RED Dashboard 2.0 project is making excellent progress. Two updates were published in the last month. Make sure you check out the latest release and provide feedback. - [Dynamic Markdown, Tables & Notebooks with Dashboard 2.0](https://flowfuse.com/blog/2023/09/dashboard-notebook-layout/) - [Dashboard 2.0 - Community Update](https://flowfuse.com/blog/2023/08/dashboard-community-update/) - [FlowFuse announces a Node-RED stack for Industry 4.0 applications on Bosch Rexroth ctrlX Automation](https://flowfuse.com/blog/2023/09/bosch-rexroth-announce/) - a fully supported Node-RED stack for Bosch customers. - [FlowFuse is now available on the AWS Marketplace](https://flowfuse.com/blog/2023/08/aws-marketplace-announce/) - making it easier to run FlowFuse on the AWS cloud. - [Our Open Source offering is a tier, not our competition](https://flowfuse.com/blog/2023/08/open-source-is-a-tier-not-competition/) - some insight from our CEO into our open source strategy. - [Why the Automation Pyramid blocks digital transformation - The Role of Unified Namespace](https://flowfuse.com/blog/2023/08/isa-95-automation-pyramid-to-unified-namespace/) - some insight from the FlowFuse product manager into the role of a Unified Namespace. ## From the Community - A new plugin for the generation of [Matter devices witin Node-RED](https://flows.nodered.org/node/@node-red-matter/node-red-matter){rel=""nofollow""} has been published. ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - [Contract Front-End Engineer – Node-RED Dashboard](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} - [Developer Relations Engineer - Manufacturing & Industrial Automation](https://boards.greenhouse.io/flowfuse/jobs/4958271004){rel=""nofollow""} # Dynamic Markdown, Tables & Notebooks with Dashboard 2.0 Whilst we're still busy backporting through the existing Dashboard 1.0 features, we did want to highlight some new features we've built in Dashboard 2.0 released this week. In our v0.4.0 release, we've introduced a new "Notebook" layout, alongside a new Table widget. The Notebook layout is designed to allow users to create Dashboards structured like a Notebook (most often seen with the likes of [Jupyter Notebooks](https://jupyter.org/){rel=""nofollow""} or [ObservableHQ](https://observablehq.com/){rel=""nofollow""}). Here we will deepdive into the Notebook layout, and show how, alongside our new **Markdown Node** ([docs](https://dashboard.flowfuse.com/nodes/widgets/ui-markdown.html){rel=""nofollow""}), **Table Node** ([docs](https://dashboard.flowfuse.com/nodes/widgets/ui-table.html){rel=""nofollow""}) and others, it's becoming easier to create dynamic and interactive Dashboards. *Note: If you're not familiar with Markdown, it's a simple markup language that allows you to format text. You can learn more about it [here](https://www.markdownguide.org/cheat-sheet/){rel=""nofollow""}.* ## Dashboard Hierarchy As a quick introductory note ahead of our below guide, each Dashboard is structured accordingly: - **Widget**: An individual functional block, e.g. button, chart, slider - **Group**: A collection of widgets that render together - **Page**: A single page/tab in your Dashboard. Each page can have it's own Layout, in this case we'll use "Notebook" - **UI**: Contains a collection of pages, deployed from Node-RED, provides the basic side navigation to switch between Pages. ## Building a Notebook ![Example Notebook created in Dashboard](https://flowfuse.com/blog/2023/09/images/db-notebook-example.png) To get started, drop your first widget (in this case, we'll add a `ui-markdown`) onto the Node-RED canvas. This in turn will prompt us to create our first Group/Page/Dashboard which we can name and configure accordingly. Let's add the following Markdown to our first widget: ```md # Markdown Content Here we can render dynamic Markdown content that is easily _styled_. We can inject `msg.payload`. For example, here is a timestamp updating every second: {{ msg.payload }} ``` The joy of `ui-markdown` in Dashboard 2.0 is *dynamic* content, i.e. content that can be updated by passing messages to the `ui-markdown` node. We can wire an `inject` node, set it up to repeat every second, and connect it to `ui-markdown`. Now, our Markdown content will automatically update show this value. ![Screenshot to show how an inject node can drive content of a ui-markdown node](https://flowfuse.com/blog/2023/09/images/db-notebook-inject.png) Resulting in: :video{ariaLabel="Dynamic markdown with an updating timestamp every 1 second" autoPlay="true" height="318" loop="true" muted="true" playsInline="true" preload="none" width="1314"} ## Adding More Widgets Because the Notebook is *just* a layout, we can still wire together any of the available widgets and existing nodes and display them accordingly. Let's wire a `ui-button`, HTTP Request, and `ui-table` node. When we click the button, it will perform the HTTP request, and then render the response in the table. For this, we're going to use the Random Jokes API, and in particular, a call to `https://official-joke-api.appspot.com/jokes/ten` which will return 10 random jokes. ![Screenshot showing a simple Button > HTTP Request > Table flow](https://flowfuse.com/blog/2023/09/images/generate-jokes-flow.png) We can also re-order the widgets on the page using the Dashboard 2.0 sidebar (as you could in Dashboard 1.0). ![Screenshot to show how an inject node can drive content of a ui-markdown node](https://flowfuse.com/blog/2023/09/images/db-notebook-order.png) The above effort results in the following output in our Notebook: ![Screenshot to show how an inject node can drive content of a ui-markdown node](https://flowfuse.com/blog/2023/09/images/db-notebook-jokes-table.png) ## What else is new in 0.4.0? The above demonstrates just a few of the new features in the 0.4.0 Release, but we've also added [other fixes and improvements](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.4.0){rel=""nofollow""}. In particular, I want to call out Steve's great work on implementing custom class injection, the first of our new ["Dynamic Properties"](https://dashboard.flowfuse.com/user/dynamic-properties.html){rel=""nofollow""}, of which there will be more (e.g. visibility, disabled, etc.) to come. As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} # Share & Preview Flows on flows.nodered.org For years, Node-RED's website has provided functionality to share flows through [flows.nodered.org](https://flows.nodered.org){rel=""nofollow""} This week, we at FlowFuse have contributed a new feature to the site that allows users to visually preview those flows, and embed/share those flows in articles and on forum posts. ## Visual Flow Previews A huge thank you for this work needs to go Gerrit Riessen's work published on his [Open Mind Map Blog](https://blog.openmindmap.org/){rel=""nofollow""}. He recently open-sourced some great work to GitHub ([repo](https://github.com/gorenje/node-red-flowviewer-js){rel=""nofollow""}), and with some adaptation and collaboration, we've been able to utilise this as a foundation for the functionality we've added into the flows site. Adding this to [flows.nodered.org](https://flows.nodered.org){rel=""nofollow""} will make it far easier to learn how others use Node-RED, and to share your own flows with others too. The embedding functionality should also make talking about Node-RED in your own articles & forums much easier. ### Example: Simple Flow Here's a demonstration of a simple `Inject` > `Debug` node: :iframe{allow="clipboard-read; clipboard-write" height="200px" src="https://flows.nodered.org/flow/500ee13719e54e42493c8ec96fa733b6/share?height=100" style="border: none;" width="100%"} ### Example: Subflows, Groups, Links & Switches Here's a non-functional flow that just demonstrates how FlowViewer renders the range of node types available in Node-RED: :iframe{allow="clipboard-read; clipboard-write" height="500px" src="https://flows.nodered.org/flow/82a8602b615740491d30c083e5292e5f/share" style="border: none;" width="100%"} ## Sharing & Embedding Flows Any flow on [flows.nodered.org](https://flows.nodered.org){rel=""nofollow""} now has a `Share Flow` option in the `Actions` section on the right side of the flows page. Clicking this will provide you with an iframe like: ```html ``` Which you can paste/embed into any website or blog post. Nick has also [enabled the Node-RED forums to support these embeds too](https://discourse.nodered.org/t/previewing-flows-on-the-flow-library/){rel=""nofollow""}, and is also how we've embeded the above flows too. If you want more control over the sizing of the viewer, you can also include a `?height=` query parameter on the `src` value of the `iframe`. You may also need to hardcode the `height` property of the `iframe` itself to account for this change, depending on where you're embedding it to. For example: ```html ``` We know it's still not perfect, and there's plenty more we can do with it, but hopefully this is a welcome contribution to the Node-RED community. # Modernize your legacy industrial data Industrial systems generate valuable data, but legacy protocols like Modbus or non-IIoT standards often make it hard to use. Bridging this gap is essential to connect traditional systems with the modern Industrial Internet of Things (IIoT). Whether it’s Modbus registers, serial communication without a protocol, or standards like Siemens S7 and Mitsubishi MC-Protocol, the challenge lies in making sense of this raw information. With tools like Node-RED and the node-red-contrib-buffer-parser, you can turn complex, outdated data streams into usable formats that power IIoT innovation. ### Legacy Industrial Data: Modbus Modbus is one of the most widely used industrial protocols. Originally developed in the late 1970s, it has been a popular choice for industrial communication ever since. However, its data format can be challenging to work with in the context of modern IIoT applications. Modbus typically represents data in 16-bit unsigned registers, making it necessary to convert this data into more usable formats like Signed integer, Float, Signed and Unsigned Long, String, or even individual bits. ### A short primer on data types Before we dive into how to make sense of Modbus data, let's take a quick look at some of the common data types we have to deal with. #### 16-bit unsigned 16-bit unsigned data is an integer that can only be positive. It can represent values from 0 to 65535. For example, the number 12345 is represented as `0x3039` in hexadecimal or `0011000000111001` in binary. #### 16-bit signed 16-bit signed data is an integer that can be positive or negative. It can represent values from -32768 to 32767. For example, the number -12345 is represented as `0xCFC7` in hexadecimal or `1100111111000111` in binary. #### 32-bit data 32-bit data, like 16-bit data can mean many things. It could be signed or unsigned, or even a floating point number. For example, the number 12345 is represented as `0x00003039` in hexadecimal or `00000000000000000011000000111001` in binary. Typically, 32-bit data is represented as two 16-bit registers. Therefore, when dealing with 32-bit data, you need to combine two 16-bit registers to get the full value. #### Endianness Endianness, particularly in the context of data communications, refers to the order of bytes and how they are stored or transmitted. There are two types of endianness: big-endian (BE) and little-endian (LE). In big-endian, the most significant byte is first, while in little-endian, the least significant byte is first. For example, the number 12345 is represented as `0x3039` in a big-endian word and `0x3930` in little-endian word. This can often cause confusion and complicate the process of converting Modbus data into more usable formats. ### Node-RED and node-red-contrib-buffer-parser to the rescue Node-RED is an open-source flow-based development tool for visual programming. It's particularly well-suited for IIoT applications because of its versatility and extensive library of nodes. One such node, `node-red-contrib-buffer-parser`, provides a solution to the legacy data conversion challenge. This powerful Node-RED module allows you to parse a Buffer of bytes or an Array of integer data (which, by no coincidence, the popular module `node-red-contrib-modbus` outputs), and convert it into various data types. It can output pretty much any data type, including byte-swapped data, WORD swapped data, masked/shifted/scaled data, and even individual bits. Here's a quick overview of how it works: 1. **Data Parsing**: Start by setting up a Modbus READ node in Node-RED to retrieve data from your industrial device. Then, use the buffer parser node to parse the Modbus data. 2. **Data Conversion**: With the buffer parser, you can easily convert the 16-bit unsigned data into more meaningful formats. Whether you need to translate it into Float, Long, String, or even extract specific bits, this tool makes the process straightforward. 3. **Publishing to MQTT, influxDB, a dashboard, an IIoT system**: Once your data is in a usable format, Node-RED enables you to publish it to many places. MQTT (Message Queuing Telemetry Transport), a popular protocol for IIoT communication is a perfect example. This makes your data accessible to other IIoT systems and applications for further analysis and action. ### Unlocking the Potential of Legacy Data By leveraging Node-RED and buffer parser, you can bridge the gap between legacy industrial protocols and the IIoT world. This means you can extract valuable insights from your existing infrastructure without the need for costly hardware upgrades or replacements. In the era of the Industrial Internet of Things, making sense of your industrial data is no longer a daunting challenge. With the right tools approach, you can unlock the full potential of your legacy data and drive efficiency, productivity, and innovation in your industrial processes. Yey! ### 3 quick demos of Node-RED and the buffer parser node in action Here are 3 quick demonstrations that barely scratch the surface of possibilities: #### Example 1: Modbus to MQTT Converting an array of 16-bit unsigned integers to String, Float and a scaled integer and passing them to an MQTT broker in 4 nodes! :video{ariaLabel="Legacy data to MQTT" autoPlay="true" height="376" loop="true" muted="true" playsInline="true" preload="none" width="1575"} #### Example 2: Modbus to InfluxDB Converting an array of 16-bit unsigned integers to String, Float and a scaled integer for publishing to influxDB! ![Legacy data to influxDB](https://flowfuse.com/blog/2023/09/images/industrial-legacy-data-to-influx.png)![Legacy data to influxDB2](https://flowfuse.com/blog/2023/09/images/industrial-legacy-data-to-influx2.png) #### Example 3: Modbus data on a dashboard Converting an array of 16-bit unsigned integers to String, Float and a scaled integer for publishing to a dashboard! ![Legacy data to dashboard](https://flowfuse.com/blog/2023/09/images/industrial-legacy-data-to-dashboard.png) ### Simplify Your Node-RED Operations with FlowFuse While Node-RED is a fantastic tool for data collection, transformation, and analysis, integrating it into a production environment can sometimes feel like navigating a maze. Whether you’re deploying Node-RED on a server, ensuring secure remote access for your team, or managing a sprawling network of thousands of instances, it’s easy to feel overwhelmed. That’s where FlowFuse steps in to make your life easier. FlowFuse is designed to tackle these challenges head-on. It enhances Node-RED with features that simplify collaboration, strengthen security, and provide scalable deployment options. Imagine having a robust system that not only keeps your Node-RED applications running smoothly but also scales effortlessly with your needs. With FlowFuse, you gain access to a comprehensive suite of production-ready [features](https://flowfuse.com/platform/features/) designed to streamline your Node-RED workflows and boost overall performance. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} now for a free trial and experience FlowFuse's features** ### Learn More We will be publishing follow-up blog posts with more details, best practices and examples on how to use Node-RED to make sense of your industrial data. In the meantime, you can learn more about these tools by visiting the following links: - [Node-RED blog posts](https://flowfuse.com/blog/node-red/) - [Node-RED videos](https://www.youtube.com/playlist?list=PLpcyqc7kNgp09XeRx_cae1fEIOloPqM1C){rel=""nofollow""} - [Buffer Parser Node](https://flows.nodered.org/node/node-red-contrib-buffer-parser){rel=""nofollow""} # Modernize your legacy industrial data. Part 2. In [part 1 of this series](https://flowfuse.com/blog/2023/09/modernize-your-legacy-industrial-data/), I introduced the topic of working with legacy industrial data from the likes of Modbus and older, non IIoT protocols and putting it to work in an IIoT world. We looked at some of the challenges and how Node-RED with `node-red-contrib-buffer-parser` node can help. In this article, I will dive a little deeper into the topic and discuss some of the finer details. I hope to demonstrate a smarter approach that can make a huge difference to data accuracy, performance and maintainability while significantly reducing developer time. Not only that, ending with a no-code solution. ## Obtaining Industrial Data In order to convert the legacy data to a format more suited to IIoT we first need to *grab* that data. Node-RED has core nodes that can help you and many more contribution nodes exist that provide access to a wide range of industrial devices. To give you an idea, `node-red-contrib-modbus`, `node-red-contrib-s7comm`, `node-red-contrib-omron-fins`, `node-red-contrib-mcprotocol`, `node-red-contrib-df1` and `node-red-contrib-cip-st-ethernet-ip` are just some of the PLC data access nodes available. But getting the data is just the beginning, it's the methods and considerations you need to make that can make the difference between success and failure. Read on... ## Data consistency An often overlooked aspect of working with legacy industrial data is the consistency of the data being read. In the context of this article, consistency means that the multiple values that make up a related data set are read in a way that they are all valid to one another at the point in time it was read. Let's take a look at a simple example. We have a process PLC recording production metrics and wish to get this data from its Modbus interface for reporting and decision making. The PLC has 5 values that we need to read: | Register | Value | Description | Data Type | | -------- | ----- | --------------------- | ---------- | | 1, 2 | 10 | Part Count | UINT32 | | 3, 4 | 2.5 | Cycle Time (sec) | UINT32/100 | | 5, 6 | 30 | Production Time (sec) | UINT32/100 | | 7, 8 | 20 | Run Time (sec) | UINT32/100 | | 9, 10 | 11 | Stoppage Time (sec) | UINT32/100 | In the above data sample, we can see the total production time is 30 seconds and the run time is 20 seconds. The expectation is that the Stoppage Time should be 10 seconds. However, as we can see, Stoppage Time in this sample is 11 seconds. That is because this data is not consistent. ### Why is the data inconsistent? The most common reason for the data inconsistency is that the data is being read from the PLC while the PLC is running. The data is changing as it is being read. Typically this happens when a developer, unfamiliar with the protocol or end device, begins by reading the data individually. Here is how this journey might look: *Image 1: individual reads*![image showing 10 individual reads](https://flowfuse.com/blog/2023/09/images/industrial-legacy-data-pt2-demo1.png) That is a lot of nodes and a lot of duplication! What is worse, is that the developer continues down this path and begins converting the data ready for publishing to MQTT. Here is how this might evolve: *Image 2: individual reads with data processing*:video{ariaLabel="Video showing 10 individual reads with data processing" autoPlay="true" height="684" loop="true" muted="true" playsInline="true" preload="none" width="1030"} Yippie! We have the data, it works, we publish it to MQTT, job done. Right? Unfortunately, no. The data is inconsistent. ### So whats the big deal? Inconsistent data is not useful data and errors can get compounded over time. This leads to bad decisions being made and a loss of confidence in the data. Ultimately, this leads to the data being ignored and the opportunities to make improvements are lost. Loss of improvements means loss of money! Not only that, from a developer or maintainers perspective, it has many problems: 1. Lots of error prone manual configuration (yes, I made several mistakes while creating the example) 2. Hard coded register addresses 3. Duplication 4. Inextensible - what if we need to read more registers? 5. Inconsistent data - as discussed above 6. Slow - each read takes time 7. Inefficient use of network bandwidth - each read requires a request and response packet ### How can we make the data consistent? The most obvious solution is to stop the PLC before reading the data. However, I am faily certain your boss will not be super pleased with stopping the manufacturing process. The next best thing is for the PLC to sample and store the data in an internal memory buffer, waiting, unchanging, to be collected. Unfortunately, this too is not always possible either due to limited in-house skills, locked down PLCs or simply because the PLC does not have the memory to store the data. The next best thing to do is to read relative data as quickly as possible and in one block. ### A quick side-bar (timing is ... everything) In many protocols, including Modbus, data must be polled. Each poll, depending on many factors, can take a number of milliseconds. Lets say, on a relatively quiet serial network, a single register poll takes 25 milliseconds (assume 9600 baud, 1 byte/ms, request packet size 8 bytes, response packet size 7 bytes, 10ms latency for the PLC to receive, process and respond to the request) - If we read 10 registers individually, then the time taken to read the 10 registers is 250 milliseconds. - If we actually needed a more realistic number of registers, say 32, then the time taken to read them individually is a whopping 800 milliseconds. In the world of PLCs, that is an eternity. Now, lets look at that from a different approach. If we were to read the 32 registers in one go, the time taken based on the above constants would be 87 milliseconds. That is over 9 times faster than reading the registers individually. The improvements don't stop there either, the number of total bytes transferred on your network is reduced by 84% and, more importantly, the data is consistent since it was read from the same scan of the PLC scan. *Image 3: A comparison of individual reads vs block reads*![A data table comparing polls vs block read](https://flowfuse.com/blog/2023/09/images/industrial-legacy-data-pt2-table.png) OK, back to the topic at hand. ## Getting the data in a more consistent way Here is the same example as above but this time we are reading the data in one go: *Image 4: Getting data in one block*![image showing 1 read for 10 registers](https://flowfuse.com/blog/2023/09/images/industrial-legacy-data-pt2-demo1b.png) Note how much simpler this is? Not only that, it is easier to maintain, faster, more extensible and most importantly, the data is consistent. Great, lets move on. ## Processing the data in readiness for IIoT: MQTT Now that we have good data, we need to process it in readiness for IIoT. In this example, we are going to publish the data to an MQTT broker as individual topics. This is a common approach as it allows the data to be easily consumed by other systems and applications. Using `node-red-contrib-buffer-parser` we can easily convert the data into more meaningful formats. The first, instinctive approach is to fan out the data and process it individually: *Image 5: block reads, individual processing*:video{ariaLabel="Video showing 1 modbus poll with individual processing" autoPlay="true" height="636" loop="true" muted="true" playsInline="true" preload="none" width="926"} This may be fine for a handful of registers but it soon becomes unwieldy and unmaintainable. But lets be smarter about this. We know that the data is consistent and we know that we can read it in one go. So, lets process it in one go too: *Image 6: block reads, smart processing, no-code solution*:video{ariaLabel="Video showing 1 modbus poll with smart processing" autoPlay="true" height="621" loop="true" muted="true" playsInline="true" preload="none" width="886"} ## Node-RED in Production Node-RED is a powerful tool widely used in IoT and IIoT industries, including manufacturing, automotive, textiles, and building management. It excels at collecting, transforming, visualizing, and analyzing data. While integrating Node-RED into production environments offers numerous benefits, it often involves complex tasks such as deploying the server, managing security, and ensuring scalability. These initial setup challenges can be overwhelming and time-consuming. FlowFuse simplifies these tasks by providing a unified platform for managing all Node-RED instances. It enhances collaboration, ensures security, and supports scalability, making deployment and management more efficient. With features like [snapshots](https://flowfuse.com/docs/user/snapshots/), team collaboration tools, one-click deployment, and [multi-factor authentication](https://flowfuse.com/docs/user/user-settings/#security), FlowFuse streamlines the process and enhances the operational capabilities of Node-RED in production settings. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} now for a free trial and experience FlowFuse's features** ## Wrap up I hope this article has given you some food for thought and some ideas on not only simplifying your journey to IIoT but also the pitfalls to avoid along the way. A parting thought, there are times when the data is not contiguous. There are ways to deal with this too but that is for another day. P.S. I will post the flows used for the examples above in the comments below. If you have any questions or comments, please reach out there too. # Updating our branding across GitHub, npm and Dockerhub Following our rename to FlowFuse last month, we are about to take the next set of steps to complete the rebrand. This time, focussed on the technical assets we produce. Rebranding a company isn't a small undertaking, especially when your company name is also your product name. When we announced our [new name last month](https://flowfuse.com/blog/2023/08/flowforge-is-now-flowfuse/) we prioritised updating the website, our documentation and social media presences. All of the most visible things relating to the company name. But we knew that wasn't the whole job done. The name `flowforge` still appears in the technical resources we use and the artefacts we publish. Changing them is not as simple a task as changing some words on a website, so it has taken a bit more time to get our plans in place for this next step. I wanted to highlight the set of changes we'll be making in the coming days to complete this migration. For the vast majority of users, expecially those using FlowFuse Cloud, these changes will be completely transparent. However, if you are contributing to any of our open source components, or consuming our npm or Docker packages directly, then please read on. There are four areas we need to migrate. ### GitHub Organization As a company everything we do revolves around our GitHub Organization. Our source code, release planning, this website, and far more all live there. Step one of our migration will be renaming the organization to `FlowFuse`, so instead of `https://github.com/flowforge` we will now live at `https://github.com/FlowFuse`. Renaming organizations on GitHub, whilst not something done lightly, is well catered for. Many existing urls should get automatically redirected - so any existing links will still work. We will, of course, do the work to update any urls in our docs. ### NPM package names We publish a number of packages to the public Node.js Package Manager (npm) repository under the `@flowforge` name. After this week's release is done, we'll be updating all of our packages to publish under the `@flowfuse` name and no longer updating the packages under the old name. This will impact anyone who has installed any of our components directly from `npm`. For example, the Device Agent or Node-RED Dashboard 2.0. We will provide specific upgrade instructions for each of the affected components once the move is done. ### Docker Images We publish container images to Dockerhub under the `flowforge` name. Once we've updated our npm package names, we'll also be updating our container tags to use the new name. If you are using our helm or Docker Compose projects, we'll have a new release that will help get you moved over to the new image names. Likewise our Digital Ocean and AWS Marketplace offerings will be updated - and instructions provided for existing users to migrate over. ### FlowFuse Cloud The final step we have to make is to move FlowFuse Cloud over to its new home at `app.flowfuse.com`. We have to co-ordinate the update with all of our customers who use SSO to login to ensure they can continue to access the platform. Once that is done, it will be a seamless transition for everyone. Existing Node-RED instances will continue to use the `*.flowforge.cloud` domain, but then all new instances will use the `*.flowfuse.cloud` domain. # Tulip Operation Calling Event Report This week I attended the Tulip event called [Operations Calling](https://www.operationscalling.com/){rel=""nofollow""} in Boston. Here are some quick observations from the event. For those that might not know [Tulip](https://tulip.co/){rel=""nofollow""}, they are a Boston based company focused on frontline operations in the manufacturing and industrial automation industry. They provide the software to help factory workers be more efficient at their job. Some people refer to this category as MES but Tulip avoids this term since MES can carry a lot of negative baggage from legacy MES vendors. Some quick thoughts on the event: - I would estimate about 300 people attended the event. It was held at Tulip’s office, which is actually in an old Ford factory. The offices and environment were very cool. - There was a large emphasis on Tulip partners and the ecosystem. Tulip knows it needs to be open and play well with others. There were 27 different vendors showing in their partner pavillion. Pretty impressive given the size of Tulip. - Tulip has a no-code environment for setting up their software. However, they realize you also need low-code, like Node-RED, for integrating hardware and software into Tulip. I attended two sessions that discussed how Node-RED is an important part of the Tulip integration and customization strategy. Node-RED was also being used in some of their demos stations. - Tulip talks about ‘citizen developers’ and ‘composable apps’. This is the future of manufacturing. Organizations need to start thinking in terms of smaller applications that do a specific task that can also be rolled up into something bigger at a later time. These small applications need to be developed by the people closest to the problem, not the IT team or a system integrator. - During the closing panel, it was stated that ‘Today, Excel is the most popular digital automation platform used in industry.’ There are a lot of spreadsheets being used to collect and visualize data. However, the industry needs to find a better way. Node-RED is an important part of the solution. The people who can run Excel spreadsheets can easily use Node-RED. However, using Node-RED will let them do even more than what Excel allows them to do. Operation Calling was definitely an event worth attending and I hope to return next year. # What are FlowFuse Blueprints? Starting today, FlowFuse Blueprints are available on FlowFuse Cloud. Additionally, upon request, all our Teams and Enterprise Self-Hosted customers gain access to this collection. But what exactly are FlowFuse Blueprints? ## FlowFuse Blueprints FlowFuse Blueprints aim to make the Node-RED experience more accessible for newcomers, while also offering a treasure trove of fresh ideas for seasoned Node-RED users. When setting up a new Node-RED instance, you now have the option to choose a blueprint tailored for specific use cases. For example, our "ANDON Operator Terminal" blueprint can be selected, and it will automatically configure the Node-RED instance, sparing you the need to start from scratch. While these templates are powerful out-of-the-box, they're also fully customizable, allowing you to tweak them to suit your unique requirements. Ultimately, blueprints speed up the learning curve for new users and expedite the solution-building process for experienced ones. ### How to use Blueprints? All our FlowFuse Cloud users can select a Blueprint directly while creating a new Node-RED instance. Self-hosted customers can request access to our blueprints via a [support ticket](https://flowfuse.com/support/). ## The first three Blueprints In the coming weeks, we'll be releasing a multitude of blueprints tailored for diverse use cases. However, we decided to start with with three foundational manufacturing applications designed with the [Node-RED Dashboard 2.0](https://dashboard.flowfuse.com/){rel=""nofollow""}. ### ANDON Operator Terminal The Andon Operator Terminal is designed to be at the start of an Andon process, allowing end-users to report any issues with the cell to a supervisor. ![ANDON Blueprint Screenshot](https://flowfuse.com/blog/2023/10/images/ANDON1.png) ### Performance Overview Dashboard The Performance Overview Dashboard Blueprint provides a real-time snapshot of key performance metrics, delivering a comprehensive overview of manufacturing operations for a specific station or entire line. ![Performance Overview Screenshot](https://flowfuse.com/blog/2023/10/images/performance-dashboard.png) ### OEE Calculator If automatic calculations are not feasible, the OEE Calculator Blueprint enables end-users to manually input production data to compute the Overall Equipment Effectiveness (OEE) for a given machine. ![OEE Calculator Screenshot](https://flowfuse.com/blog/2023/10/images/dashboard-data.png) # What are Certified Nodes? We are thrilled to introduce a new feature for our Teams and Enterprise Tier customers - **Certified Nodes for Node-RED**. This new offering is designed to reinforce your flows's robustness by granting you access to Node-RED nodes that stand up to our rigorous quality and security standards. ## What is a certified node? A certified node is a module from the Node-RED library that undergoes a certification process, ensuring it adheres to standards that address three core pillars: **Quality** - Testing phases for each node. - Operational reliability and compatibility. **Security** - Proactive resolution of potential vulnerabilities. - Revocation of certification for nodes falling short on security, with prompt notifications to affected customers. **Support** - Ambitious aim towards effective issue resolution. - Assistance for troubleshooting. ## How to use certfied nodes? Accessing certified nodes is straightforward, they're integrated directly within your Node-RED palette manager, simplifying selection and implementation. All new instances since October 26, 2023, have automatic access to the catalogue. If you want to add Certified Nodes to one of your existing instances, just [contact us](https://flowfuse.com/support/). ![Node-RED palette manager](https://flowfuse.com/blog/2023/10/images/certified-nodes.png) ## What kind of Nodes are included? Our initial roll-out includes a curated assortment of certified nodes. We're continuously expanding our library, with upcoming weeks bringing a wider array of options. Should you find a gap in your desired functionalities, we encourage you to [reach out](https://discourse.nodered.org/c/vendors/flowfuse/24/){rel=""nofollow""}. Your feedback drives our journey forward, influencing the nodes we introduce next. - Linting for Node-RED - Enhanced debugging for Node-RED - FlowFuse Snapshot Plugin - Node-RED Dashboard 2.0 - E-Mail Communication - Base64 converter - Buffer Parser - PostgreSQL - InfluxDB - Omron PLC - MC Protocol (Mitsubishi PLCs) ### What will follow in the coming weeks? - Siemens PLC - Modbus - Mssql - Rockwell and Allen Bradley - OPC-UA - MongoDB - MySQL - WhatsAPP, Telegram, Slack # Innovate from within - Why manufacturing must embrace Citizen Developers In the early days when I was working as a solution architect, I found myself in a peculiar position, observing an intriguing relationship between Operational Technology (OT) and Information Technology (IT) departments. While OT departments struggled to develop digital solutions, the IT departments held an unspoken authority in this domain, leading to a paradoxical dynamic that often seemed to hinder rather than facilitate progress. As a solution architect working within this environment, I found myself uniquely positioned to bridge the gap between these two groups while also helping them find common ground on which they could build successful projects together. To do so effectively, I needed to understand each group's perspective and determine how best they could collaborate without compromising either side's autonomy or objectives. This task was not always easy but ultimately proved rewarding once achieved successfully. ## A Tale of Shadow IT In the best-case scenarios, IT departments considered themselves as vendors, and OT, the customers. However, the IT-driven, off-the-shelf solutions seldom proved to be the right fit for the problems at hand. This mismatch often led to the birth of shadow IT systems, solutions that were not sanctioned by IT, yet were deemed necessary to maintain operational efficiency. In this realm of shadow IT, I've observed countless solutions running on desktop PCs, hidden from the eyes of IT departments, solving problems in manufacturing quickly, a situation born of necessity, with which no one could be satisfied. I cannot deny that Node-RED, an open-source low-code programming tool, has very often been one of these shadow IT systems, enabling data modification, lightweight HMI creation, and empowering the OT workforce. However, this under-the-radar approach is fraught with dangers. With unprotected systems and without official oversight, the potential for security breaches is a real and present danger. I would wager that even today, a simple scan on port 1880 would reveal a multitude of open Node-RED environments. This double-edged sword of innovation and insecurity highlights the urgent need for a paradigm shift. ## Why are Citizen Developers Important in Manufacturing? Citizen developers are individuals who create applications and software solutions for their organizations without any background in computer programming. These citizen developers leverage low-code platforms to build apps that automate tasks, streamline processes, and improve existing systems' user experiences. Citizen developers are a growing trend in the industry, serving as a key factor in harmonizing IT and OT interests. They bring an array of benefits that can make businesses more efficient and cost-effective. From reducing development costs to increasing productivity, citizen developers offer significant value to organizations seeking ways to stay competitive. 1. **Acceleration of Software Development:** Low-code platforms significantly expedite the software development process, enabling more efficient realization of business requirements. By quickening development, these platforms free up time and resources for design and innovation, leading to higher-quality software that better serves the business's needs. 2. **Crafting Bespoke Applications:** By empowering non-professional coders with the right tools and resources, organizations can democratize digital solutions. This not only facilitates the creation of custom systems that fit seamlessly into existing infrastructures but also enables rapid ideation and development, even for those without prior coding experience. 3. **Enhancement of Operational Processes and Customer Experiences:** With the automation capabilities of low-code platforms and the innovative solutions generated by citizen developers, operational processes can be optimized, and customer experiences significantly improved. The synergy between these elements can lead to efficiency gains through automation and the delivery of more effective, customer-centric solutions. This shift toward embracing citizen development within manufacturing represents a unique opportunity where both sides benefit significantly; not only do OT departments gain access to custom applications tailored precisely for their needs, but IT teams can lead guidance for self-empowerment, resulting in greater efficiency through automation. ## Democratize Application Development with Node-RED For the past decade, Node-RED has been a pioneer in low-code tools, democratizing application development in manufacturing. It provides powerful data collection, transformation, and visualization capabilities, simplifying the application-building process. As an open-source tool with no associated costs or licensing fees, Node-RED is [increasingly popular](https://flowfuse.com/blog/2023/03/integration-platform-for-edge-computing/#the-standard-for-edge-computing-and-plcs) among manufacturers seeking to quickly develop custom applications without extensive coding knowledge or experience. FlowFuse takes things one step further, offering organizations a way to officially incorporate and scale Node-RED, making it compliant, governed, and secure within the existing solution landscape. ## The Strategic Imperative of Citizen Development For industry decision-makers, this is a call to action. It is imperative to nurture this growing community within your workforce, providing them with the tools, platforms, and, importantly, the organizational support they require. The empowerment of citizen developers could very well be the deciding factor in your organization's ability to stay competitive, agile, and innovative in a rapidly evolving market. Looking to the future, the success of modern manufacturing lies in its people and their ability to solve problems with the right tools at their fingertips. Citizen development has the potential to break down the barriers. Organizations that recognize and invest in this potential will undoubtedly lead the charge. [One large US manufacturing company has already made this bet](https://flowfuse.com/customer-stories/manufacturing-digital-transformation/), with its project leader describing Node-RED's low-code paradigm as a way to decentralize innovation and let subject matter experts build their own digital systems. # Community News October 2023 Welcome to the FlowFuse newsletter for October 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community. ## Quicker Releases for FlowFuse Cloud and New Changelog FlowFuse is committed to delivery new features to our customers and community as fast as possible. We are now rolling out new builds of FlowFuse to FlowFuse Cloud more regularly than the previous monthly release. This will allow FlowFuse Cloud users to benefit from the latest enhancements as they become available. The self-hosted version of FlowFuse will continue to have releases every 4 weeks. To allow FlowFuse Cloud users to keep up to date on the new changes, we have started a [Changelog](https://flowfuse.com/changelog/) to announce newly deployed features. ## Recent Changelog Updates - [DevOps Pipeline with action selection](https://flowfuse.com/changelog/2023/09/devops-actions/) - [Usability improvements to Device Management](https://flowfuse.com/changelog/2023/09/snapshots-devices/) - [Introducing the Enterprise tier](https://flowfuse.com/changelog/2023/09/introduction-enterprise-tier/) - [API Endpoints for DevOps Pipeline](https://flowfuse.com/changelog/2023/09/pipeline-api/) - [Private Node Support](https://flowfuse.com/blog/2023/10/use-private-custom-nodes-with-flowfuse/) ## Upcoming events ### Dashboard 2.0 - Where we are, and what’s next? The next generation of the Node-RED dashboard is starting to mature. Development work on Dashboard 2.0 started earlier this year and amazing progress has been made already. Join Joe Pavitt, leader of the Dashboard 2.0 project, who will demonstrate the new dashboard project and discuss future plans during our October webinar. [Sign-up today](https://flowfuse.com/webinars/2023/dashboard-20/) to join us on October 26. ## From our Blog - [Custom Vuetify components for Dashboard 2.0](https://flowfuse.com/blog/2023/10/custom-vuetify-components-dashboard/) - the new dashboard project allows for custom UI components - [Updating our branding across GitHub, npm and Dockerhub](https://flowfuse.com/blog/2023/09/rebranding-our-components/) - our GitHub, npm and DockerHub locations are now called FlowFuse, due to the rename from FlowForge. - [How ChatGPT improves Node-RED Developer Experience](https://flowfuse.com/blog/2023/09/chatgpt-for-node-red-developers/) - lots of interest in ChapGPT in the Node-RED community. A quick review of different integrations. - [Share & Preview Flows on flows.nodered.org](https://flowfuse.com/blog/2023/09/flow-viewer/) - A new way to visualize flows in web pages. - [Charting REST API Data in a Dashboard](https://flowfuse.com/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](https://flowfuse.com/blog/2023/09/modernize-your-legacy-industrial-data/) - [Part 2](https://flowfuse.com/blog/2023/09/modernize-your-legacy-industrial-data-part2/) ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - [Contract Front-End Engineer – Node-RED Dashboard](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} # Custom Vuetify components for Dashboard 2.0 Vuetify is a library of UI components using Vue. This saves the developers of Dashboard 2.0 a lot of time, but it can also help you, the end-user. As Vuetify is now included, it can be used to include *any* of their components. So in this post we're going to use a few of these to teach you how to use any of them. Let's install the [Dashboard 2.0 package](https://dashboard.flowfuse.com/getting-started.html){rel=""nofollow""} if you want to follow along. When that's done, let's figure out how to build custom components on dashboards. ## Custom components While going through the list of components on [Vuetify](https://vuetifyjs.com/en/components/all/){rel=""nofollow""} there's several examples that aren't natively implemented in Dashboard 2.0. One example we'll use in a dashboard in this post is the [Progress circular](https://vuetifyjs.com/en/components/progress-circular/){rel=""nofollow""} to build a count down timer. The documentation explains which elements one can change, in this case the size and width. Having set those to the values you'd want in your dashboard, the HTML is generated for you, in my case it's: ```html ``` ### Using the template node Like the [template core node](https://flowfuse.com/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){rel=""nofollow""}. 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. !["Custom widget on Dashboard 2.0"](https://flowfuse.com/blog/2023/10/images/custom-element-dashboard.png "Custom widget on Dashboard 2.0") ## Dynamic templates 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`](https://flowfuse.com/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 sends a message every second. Then we need to update the template node to the following snippet: ```html ``` The difference is subtle, but important. Instead of hard-coding the `model-value` to 20, the tag has changed name and it's set to `msg.payload`. The latter makes the value dynamic. Changing `model-value` to `v-model` is due to leaking implementation details of Dashboard 2.0. It uses VueJS to provide, among other features, easy updating of components. If components are dynamic, *always use `v-model`*. This allows VueJS to pick up changes made dynamically. :video{ariaLabel="Progress spinner, random values" autoPlay="true" height="664" loop="true" muted="true" playsInline="true" preload="none" width="462"} ### Finishing the count down timer This is mostly a programmers job, but it's not hard, so let's get to it. A button would be great to reset the timer, and for the sake of this post we can hardcode the deadline to 1m from the button press. When dragging in a button node, connect it to a [change](https://flowfuse.com/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 range between 0-100. The complete flow is: ::render-flow ```json [{"id":"ce9bb8f74e3fc934","type":"ui-template","z":"24065a0aadb305e3","group":"8fa772a709ae3316","dashboard":"e5a3f4cdb11e5e3b","page":"5bedf7f49d5a6037","name":"Progress spinner","order":0,"width":0,"height":0,"format":"\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":810,"y":80,"wires":[[]]},{"id":"8f3e6631414aa096","type":"inject","z":"24065a0aadb305e3","name":"Inject deadline","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"deadline","payloadType":"flow","x":140,"y":80,"wires":[["293cd6f9d727fa02"]]},{"id":"bd9032719d24a53d","type":"ui-button","z":"24065a0aadb305e3","group":"8fa772a709ae3316","name":"","label":"Reset","order":0,"width":0,"height":0,"passthru":false,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"date","topic":"deadline","topicType":"msg","x":170,"y":140,"wires":[["61ef83d8b06ff626"]]},{"id":"61ef83d8b06ff626","type":"change","z":"24065a0aadb305e3","name":"","rules":[{"t":"set","p":"deadline","pt":"flow","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":140,"wires":[[]]},{"id":"293cd6f9d727fa02","type":"change","z":"24065a0aadb305e3","name":"Secs since reset","rules":[{"t":"set","p":"payload","pt":"msg","to":"($millis() - msg.payload)/1000","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":80,"wires":[["9742da7e74fd3cd2"]]},{"id":"9742da7e74fd3cd2","type":"range","z":"24065a0aadb305e3","minin":"0","maxin":"60","minout":"0","maxout":"100","action":"clamp","round":false,"property":"payload","name":"Seconds to percentages","x":570,"y":80,"wires":[["ce9bb8f74e3fc934"]]},{"id":"8fa772a709ae3316","type":"ui-group","name":"Group Name","page":"5bedf7f49d5a6037","width":"6","height":"1","order":"","disp":true},{"id":"e5a3f4cdb11e5e3b","type":"ui-base","name":"UI Name","path":"/dashboard"},{"id":"5bedf7f49d5a6037","type":"ui-page","name":"Page Name","ui":"e5a3f4cdb11e5e3b","path":"/","layout":"grid","theme":"8240fbe7c09bc81c"},{"id":"8240fbe7c09bc81c","type":"ui-theme","name":"Theme Name","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}] ``` :: # Integrate your own widgets with Dashboard 2.0 With a new release, comes new features for Dashboard 2.0, and the focus of this release has been on improving the developer experience for those building third-party widgets for Dashboard 2.0. Dashboard 1.0 had a hugely popular ecosystem of third party widgets (e.g. `ui-worldmap`, `ui-svg`) and something we've been keen to support is a platform where these widgets (and more) can be built and used within Dashboard 2.0 too. Whilst we can't support the existing Dashboard 1.0 extensions directly (given that we're now VueJS-based, rather than AngularJS), we hope that the framework, documentation and this article, will help springboard the community to build new (and transfer over old) widgets for Dashboard 2.0. ## Building from `ui-template` As with Dashboard 1.0, we've utilised the flexibility of our `ui-template` node here to enable third-party integrations. If you're used the new `ui-template` in Dashboard 2.0 already, you'll know that you can provide raw Vue (HTML) content and it'll render that into your Dashboard. In 0.6.0, we've added *a lot* of new functionality to the guts of `ui-template`, which we can then extend with our third-party widgets. This new functionality includes: - **Custom Dependencies** - Injection of external widget dependencies (e.g. other JavaScript libraries) via ``. - **On Input** - `onInput` defines behaviour of the widget in Dashboard when it receives a message in Node-RED. - **On Load** - `onMounted` defines functionality when a widget first loads in Dashboard. - **Custom Functions** - Define general functions that can be called from within your widget at any point of your choosing - **Extend Built-In Events** - Our built in `send` function can be called within your widget's template, and will send a message back to Node-RED, with any content of your choosing. - **Custom SocketIO Event Handlers** - If you want to extend the communication between Dashboard and Node-RED, you can emit your own SocketIO events from Dashboard, and have respective handlers for those events in Node-RED. We also have plans to expose more of this new functionality to the `ui-template` interface itself within Node-RED, but for now it's mostly available when developing third-party widgets. ## Useful Resources If you're interested in building integrations, then we've also built a couple of resources to help you get started: - [Widget Development Guide](https://dashboard.flowfuse.com/contributing/widgets/third-party.html){rel=""nofollow""} - A guide for how to structure your own widgets, and - [Example Integration (Repo)](https://github.com/FlowFuse/node-red-dashboard-example-node){rel=""nofollow""} - We've open sourced a very simple `ui-example` node that demonstrates how you can build your own widget for Dashboard 2.0, that utilises all of the features highlighted above. ## What else is new in 0.6.0? Whilst we focussed this article on the third-party integrations, we did also squeeze quite a lot more into the 0.6.0 release too with plenty [other fixes and improvements](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.6.0){rel=""nofollow""}, including the separation of the Dash oard 2.0 nodes into a new "Dashboard 2" category in the Node-RED palette. As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} # Embracing Innovation: Build vs Buy in MES Manufacturing companies often struggle to choose between building custom MES solutions or buying ready-made software. Both options have their strengths, but they can fall short of providing the flexibility and control engineers need. A hybrid approach, combining the best of both worlds, is changing the game. In this article, we’ll explore how Node-RED and FlowFuse make it easier to create MES systems that are efficient, adaptable, and perfectly suited to your needs. ## The Hybrid Approach: A Best of Both Worlds Strategy Much like the practice of outsourcing machine installation while specifying detailed requirements, a hybrid approach to MES systems leverages the expertise of SI companies while retaining control over critical aspects of system design. This approach recognizes that automation engineers possess valuable insights into the unique needs of their manufacturing processes. They can specify crucial details such as wire coloring schemes, downtime conventions, Andon board standards, PLC types, and even the code used on PLCs. ## Node-RED: The MES Code's Best Friend Node-RED emerges as a game-changer in this paradigm. It is an open-source, flow-based development tool renowned for its ability to connect everything, from PLCs to relational databases to firewalls, empowering engineers to create MES system logic visually. Node-RED simplifies the process by enabling engineers to drag and drop nodes and wires to define the logic flow. This visual approach not only streamlines development but also enhances the transparency of the codebase. ## The Power of Standardization and Code Clarity One of the primary reasons behind Node-RED's appeal is its ability to standardize code. Just as Ladder Logic and Function Block Diagrams have long been favored for their clarity, Node-RED fosters a coding environment where engineers can easily understand and build upon each other's work. This standardization ensures that MES systems remain in sync with the rest of the plant, making troubleshooting and maintenance more efficient. ## FlowFuse: Bridging the Gap to Deployment When it comes to deploying MES systems, FlowFuse enters the scene as a vital companion to Node-RED. FlowFuse is a deployment platform that seamlessly integrates with Node-RED, allowing for effortless deployment of applications to a customer's environment. Its user-friendly interface makes it easy for automation engineers to manage and scale their MES systems. ## Pioneering the Future: The Strategic Adoption of Node-RED and FlowFuse Embracing Node-RED and FlowFuse in MES system development is not just a technical choice; it's a strategic one. By adopting this hybrid approach, manufacturing companies position themselves as thought leaders in the industry. They demonstrate a commitment to innovation, transparency, and adaptability. ## Elevate Your MES Strategy: Embrace Node-RED and FlowFuse Today In conclusion, the answer to the build vs. buy dilemma for MES systems is not one or the other, it's both. Node-RED and FlowFuse offer a dynamic partnership that empowers manufacturing companies to craft MES solutions that are tailored to their needs while harnessing the expertise of System Integration companies. The call to action is clear: Embrace Node-RED and FlowFuse as the catalysts for your manufacturing innovation journey. For more information about FlowFuse and how it can revolutionize your MES system deployment, visit FlowFuse.com Start building MES systems that are not just efficient but also future-ready, and become a trailblazer in the world of industrial manufacturing. # Service Disruption Report for October 11th, 2023 On October 11th, 2023, we had an issue where users were not able to access the Node-RED editor, recieving a 'Access Denied' error message. This post examines the issue that was hit, the timeline of events and what we've done to resolve it. ## Summary As part of our company rebranding, we planned a migration for our FlowFuse Cloud platform from `app.flowforge.com` to `app.flowfuse.com`. We applied this change in co-ordination with our customers using Single Sign On as it required an update to their configuration to match. This was done on Tuesday October 10th and all confirmed working with those customers. On Wednesday October 11th we received two reports that separate users could not access their Node-RED editors. We quickly identified the issue was related to how the Node-RED editors authenticated users against the platform for non-SSO users. A workaround was identified to ensure users were logged in via the new domain. We then looked at options to mitigate this for other users. We could not roll back the domain name migration as it would have required co-ordinated action with multiple SSO customers - who were not otherwise impacted by this issue. ## Resolution We tested various approaches of adding automatic redirection from one domain to the other. Due to the fact all existing Instances and Devices had the old domain name hardcoded into their settings, we were limited in what we could do here. We ultimately applied a single redirect for `https://app.flowforge.com` to `https://app.flowfuse.com` - without any redirecting of paths beneath either domain. This URL is only accessed by real users when coming to log into the platform. By redirecting at that point in time, it ensures they are logged into the new domain and everything works as expected. For users with active sessions on the old domain, a simple log out and log back in will get them on to the new domain. ## Next Steps Having resolved the immediate issue we looked at how this situation came to happen and why it wasn't caught in our preparation for the migration. We have a staging environment where we verify any changes before they get applied to production. We all have SSO enabled in that environment and a small number of test users without SSO enabled. Our testing had focused on the SSO users which, by virtue of the SSO process, ensured they ended up logged into the new domain. The testing done with non-SSO users was more limited and didn't hit the right combination of having existing log sessions on one or other of the domains to match the scenario hit by our customers. We also identified some items to follow up on around how the existing Instances and Devices handle HTTP redirects. Currently, the Device Agent is not configured to follow redirects. That is a change we have [added to the backlog](https://github.com/FlowFuse/device-agent/issues/182){rel=""nofollow""}. ## Timeline *All times are BST.* **Tuesday October 10th** We updated the platform's primary domain name to `app.flowfuse.com` whilst keeping `app.flowforge.com` active. This was done in co-ordination with our SSO customers who needed to make an update to their SSO configuration at the same time. Both customers reported success following the change. Our own validation demonstrated we could login via our own SSO, access editors, and devices continued to work as before (in particular, device editor and snapshot gathering). **Wednesday October 11th** - **11:50** and **11:57** - we received two support request from a user getting an 'access denied' error when trying to access an editor. - **12:09** - Workaround shared with both customers to login via the new domain first - **12:30** - We applied a blanket redirect for `app.flowforge.com` to `app.flowfuse.com`. - **12:50** - We then reduced the scope of the redirect so that devices would not be impacted - **13:14** - A secondary issue with logging into the editor when logged in on the new domain was reported internally. - **13:44** - Reverted all of the redirect handling whilst reviewing the problems with the previous redirects. - **14:20** - We applied a redirect to just the root of the domain and documented for our support channel No further reports were received after this time. # How to Use Private Custom Nodes in FlowFuse? With version 1.12 of FlowFuse, it is now possible to use your custom nodes. In this article, we'll explain how to do that. ::div{.blog-update-notes} **UPDATE:** Since this article was published, we've made this even easier on FlowFuse! Now, FlowFuse includes a private registry for all Team and Enterprise Tier customers, so there is no need to host and manage your own. You can view our documentation on this feature [here](https://flowfuse.com/docs/user/custom-npm-packages/) :: What do we mean by custom nodes? Typically, Node-RED nodes are hosted publicly on the npmjs registry, making them accessible to everyone for download and contribution. However, there are use cases where you may not want to share your developed nodes publicly. In such scenarios, it becomes necessary to run your own private Node-RED catalog and npm repository. This approach allows you to manage your custom nodes securely and efficiently. ## Step 1 - Setting Up a Private npm Repository Before you can use custom nodes, you'll need a place to store them. ### Option 1 - Service Provider Choose a public service provider, like [npmjs](https://www.npmjs.com/){rel=""nofollow""}, that allows you to host private packages and upload your node module. ### Option 2 - Verdaccio Another option is to use Verdaccio, a lightweight private npm proxy registry that allows you to run your own registry. #### Installing Verdaccio 1. Install Verdaccio using npm: ```sh npm install -g verdaccio ``` 2. Run Verdaccio: ```sh verdaccio ``` This will start Verdaccio on `http://localhost:4873` #### Configuring Verdaccio The default configuration supports scoped packages and allows any user to access all packages, although only authenticated users can publish. If necesarry you can edit the Verdaccio configuration file, usually found at **\~/.config/verdaccio/config.yaml**. Refer to the [documentation](https://verdaccio.org/docs/configuration/){rel=""nofollow""} for all configuration options. It is important that if you intend to use a private NPM registry with FlowFuse Cloud, the registry will need to be publicly exposed to the internet. Please make sure you understand how to secure it appropriately. #### Publish your package 1. Create a user ```sh npm adduser --registry http://localhost:4873/ ``` 2. Publish you package ```sh npm publish --registry http://localhost:4873/ ``` ## Step 2 - Creating Your Private Node-RED Catalog There are several ways to generate your own `catalogue.json`, which is necessary for Node-RED to understand which packages are available where. Below, we'll show you two of the many options to create and host a `catalogue.json`. ### Option 1 - Web App To create and host a Node-RED catalog, we recommend the package [`node-red-private-catalogue-builder`](https://github.com/hardillb/node-red-private-catalogue-builder){rel=""nofollow""}. The container accepts the following environment variables: - PORT - Which port to listen on (defaults to 3000) - HOST - Which local IP Address to bind to (defaults to 0.0.0.0) - REGISTRY - A host and optional port number to connect to the NPM registry (defaults to http\:/ registry:4873) - KEYWORD - The npm keyword to filter on (defaults to Node-RED) **It presents 2 HTTP endpoints** - /update - a POST to this endpoint will trigger a rebuild of the catalogue - /catalogue.json - a GET request returns the current catalogue The `/update` endpoint can be used with the Verdaccio [notification](https://verdaccio.org/docs/notifications){rel=""nofollow""} events to trigger the catalogue to automatically when nodes are added or updated. ```yaml notify: method: POST headers: [{'Content-Type': 'application/json'}] endpoint: http://localhost:3000/update content: '{"name": "{{name}}", "versions": "{{versions}}", "dist-tags": "{{dist-tags}}"}' ``` ### Option 2 - Node-RED You can also use a FlowFuse Node-RED instance and the [`node-red-contrib-catalogue`](https://flows.nodered.org/node/node-red-contrib-catalogue){rel=""nofollow""} package to generate and host your `catalogue.json` file. :iframe{allow="clipboard-read; clipboard-write" height="100%" src="https://flows.nodered.org/flow/1f01a92fdbd4172c75fcb88b44e64954/share" style="border: none;" width="100%"} ## Step 3 - FlowFuse configuration Next, you'll need to add all the details to your FlowFuse instance configuration. 1. Add the Catalog: Go to your **Instance** -> **Settings** -> **Palette**. Here, you'll have the option to add a catalogue.json. You'll need to provide the URL from which the catalogue.json can be accessed. For example: **{rel=""nofollow""}** It is import to remember that this URL must be accessible from the browser running the Node-RED editor and when used with FlowFuse (or any other Node-RED editor accessed via HTTPS) it must be served with HTTPS. 2. Modify the npmrc File: You'll need to configure where to find the packages from the catalog, possibly specifying a scope. ```text # Set a new registry for a scoped package @myscope:registry=https://mycustomregistry.example.org ``` If necessary, set authentication-related configurations. See the [documentaion](https://docs.npmjs.com/cli/v9/configuring-npm/npmrc#auth-related-configuration){rel=""nofollow""} for details. 3. Save and Restart Your Node-RED Instance: The new npm modules should now be visible in the Node-RED Palette Manager. # Integrate with ChatGPT Assistants with Node-RED ## Introduction to the World of GPTs and AI Assistants In the ever-evolving landscape of artificial intelligence, Generative Pre-trained Transformers (GPTs) have emerged as groundbreaking tools. These advanced AI models, developed by OpenAI, are capable of understanding and generating human-like text, offering vast possibilities across numerous applications. GPTs learn from various internet texts, enabling them to respond to queries with human-like understanding. Among the most intriguing developments in this field are AI Assistants. These are specialized applications of GPTs, accessible through an API, designed to enhance and streamline various tasks. Tasks that include code interpreter, functions, retrieval, and leveraging uploading files to interact with. Unlike traditional GPTs, which primarily focus on generating text, AI Assistants can interact, comprehend, and assist in real-time, making them invaluable in industries ranging from manufacturing to finance to healthcare. [TLDR: Give me the Flows](https://flows.nodered.org/flow/073548c276832e804f037f3212014e60){rel=""nofollow""} ## Node-RED and AI Assistants The integration of Node-RED with AI Assistants brings a unique set of advantages. By leveraging Node-RED's user-friendly platform, developers and citizen developers can easily harness the power of AI Assistants. This integration allows for creation of bespoke solutions tailored to specific industry needs, ranging from automated customer service to advanced data analytics. The real-world impact is substantial – imagine a manufacturing line where real-time data is seamlessly integrated with a prescriptive AI-driven decision-making prompt, enhancing efficiency and reducing downtime. In healthcare, it provides patients with real-time updates to their personal data and provides contextual information, while in retail, it could enhance customer engagement through personalized interactions. The future shaped by these technologies is one where automation and intelligence converge, leading to unprecedented levels of efficiency and innovation in various sectors. ## Experience the Integration Firsthand We invite you to explore the possibilities firsthand. Try out the flows we've created and share your feedback. This is your getting started package. In the provided flows, you can do the following: ![OpenAI Assistant integration on Node-RED](https://flowfuse.com/blog/2023/11/images/ai-flows.png) 1. **Create Assistant**: This flow creates a new assistant. It starts with an inject node that sets the assistant's name, instructions, tools, and model. The HTTP request node then sends a POST request to the OpenAI API to create the assistant. The assistant's ID is stored in the flow context for later use. 2. **List Assistants**: This flow lists all the assistants that have been created. It starts with an inject node that triggers the flow. The HTTP request node sends a GET request to the OpenAI API to retrieve the list of assistants. The results are then displayed in the debug node. 3. **Delete Assistant**: This flow deletes an assistant. It starts with an inject node that sets the assistant's ID. The template node constructs the URL for the HTTP request node, which sends a DELETE request to the OpenAI API to delete the assistant. The results are then displayed in the debug node. 4. **Adjust Assistant Instructions and Models**: This flow adjusts the instructions and model of an assistant. It starts with an inject node that sets the assistant's ID, new instructions, and new model. The change node prepares the payload for the HTTP request node, which sends a POST request to the OpenAI API to update the assistant. The results are then displayed in the debug node. 5. **Create Thread and Run**: This flow creates a new thread and runs it. It starts with an inject node that sets the assistant's ID and the message to be sent. The subflow node then handles the creation of the thread, sending of the message, and retrieval of the response. The results are then displayed in the debug node. How do you envision leveraging this integration in your day-to-day operations or within your industry? Your insights are valuable in shaping the future of our industry. Begin your journey [here.](https://flows.nodered.org/flow/073548c276832e804f037f3212014e60){rel=""nofollow""} ## Embracing the Future of AI and Automation Integrating Node-RED with OpenAI's Assistants is a testament to the ever-evolving landscape of technology. It represents a step towards a future where powerful AI tools are within reach of a wider audience, enabling the creation of bespoke, flexible, and resilient applications across industries. By embracing this integration, we open doors to innovation and efficiency previously unimagined. *Always consult with management before uploading company data to public services like ChatGPT.* # Node-RED Builder a GPT (Alpha) by FlowFuse When ChatGPT was first released, my expectations were quite low. I had grown accustomed to the usual industry buzz for AI and ML that often led to underwhelming solutions. Naturally, I approached ChatGPT with similar reservations. It wasn't until a few months after its announcement that I decided to give it a try. To my surprise, within just 10 minutes, I found myself so captivated that I decided to purchase the pro version. On November 6th, OpenAI unveiled a new offering: GPTs. These function as custom ChatGPT environments, allowing the author to provide additional context, giving it a specific and focused purpose. With new content emerging daily for both Node-RED and FlowFuse, the ability to update and provide essential documentation became increasingly valuable. ChatGPT is already a fantastic tool for building Node-RED flows, and if you haven't tried it yet, I highly recommend giving it a go. Now, let me introduce you to Node-RED Builder, a preconfigured environment where all the necessary prompts are already set up to ensure your success. Furthermore, the latest knowledge on Node-RED and FlowFuse is readily available within the GPT, allowing you to tap into the most up-to-date documentation for your prompts. Node-RED Builder streamlines the development of Node-RED flows, making it more accessible, especially for those new to this environment. We've even provided context to emphasize the use of default nodes over function nodes. Imagine being able to simply drag and drop elements, connect nodes, and create functional flows without delving deep into complex coding. This is precisely what Node-RED Builder makes easier, effectively opening the doors of Node-RED to a wider audience. [Access to the GPT - Node-RED builder by FlowFuse](https://chat.openai.com/g/g-V5Kyn4omE-node-red-builder-by-flowfuse-v1-0-2){rel=""nofollow""} # Community News November 2023 Welcome to the FlowFuse newsletter for November 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community. ## FlowFuse Team Summit - Barcelona by Grey The first week of November in Barcelona, the FlowFuse team embraced a blend of culture, collaboration, and creativity at our biannual summit. As a global remote-first company, these gatherings are key to nurturing the camaraderie and clear communication that fuels our work. My first summit was a vibrant tableau of wit and good-natured sarcasm, a perfect fit for my sense of humor. But it wasn't just about the fun; we delved deep into aligning our strategies to streamline communication with the vast community that recognizes the value of Node-RED. From segway tours through historic streets to culinary competitions, we bonded and built memories. During the day, we crafted go-to-market strategies, fortified our team dynamics through board games, and honed our sales approaches. The latest dashboard visualizations and product roadmaps were discussed, with our shared vision to simplify the complex for companies worldwide. **“The easy things should be easy, and the hard things should be possible.”** Our goal remains steadfast: to enhance bidirectional communication with you, our valued community, and make Node-RED's power more accessible than ever. ![](https://flowfuse.com/blog/2023/11/images/IMG_6334.jpg){width="500"} ## Announcements TLDR We're excited to share some significant enhancements that will elevate your experience with FlowFuse and Node-RED. We've announced that Dashboards 2.0 are in Beta version. Recent additions to Dashboards 2.0 include integrating Vuetify and Mermaid into Dashboard 2.0 unlocking a suite of custom UI components to enrich your dashboards. Our step-by-step guide will walk you through creating dynamic elements like countdown timers, leveraging the power of VueJS for seamless updates. We're also thrilled to introduce FlowFuse Blueprints - a game-changer in building bespoke, flexible, and resilient manufacturing applications. Our initial set of Blueprints, including ANDON Operator Terminal, Performance Overview Dashboard, and OEE Calculator, provide preconfigured Node-RED applications that streamline your development process. Dive into the world of Blueprints and discover how they can accelerate your project's journey from concept to operational reality. Join the [webinar](https://flowfuse.com/webinars/2023/blueprints/) to find out more. ## Recent Changelog Updates - [Integrate your own widgets with Dashboard 2.0](https://flowfuse.com/blog/2023/10/dashboard-integrations/) - [Blueprints](https://flowfuse.com/changelog/2023/10/blueprints/) - [Enhanced Snapshot Selection](https://flowfuse.com/changelog/2023/10/device-snapshot-selection/) - [Device Agent path bug fix](https://flowfuse.com/changelog/2023/10/path-bug-fix/) - [Resource Monitoring in Audit Log](https://flowfuse.com/changelog/2023/10/resource-alerts/) - [Certified Nodes](https://flowfuse.com/changelog/2023/10/certified-nodes/) ## Upcoming events ### FlowFuse Blueprints: Your Pathway to Enhanced Manufacturing Explore building manufacturing applications with FlowFuse Blueprints in our upcoming webinar. Blueprints make it easy to get started building applications with Node-RED. [Sign-up today](https://flowfuse.com/webinars/2023/blueprints/) to join us on November 30th. ## From our Blog - [Meet FlowFuse at SPS Nuremberg](https://flowfuse.com/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](https://flowfuse.com/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](https://flowfuse.com/blog/2023/10/citizen-development/) - Empower your Operational Technology teams as Citizen Developers - [Embracing Innovation: Build vs Buy in MES](https://flowfuse.com/blog/2023/10/mes-build-buy/) - Bridging the Gap: Uniting MES Development with Automation System Practices - [What are FlowFuse Blueprints?](https://flowfuse.com/blog/2023/10/blueprints/) - Preconfigured Node-RED Applications - [Integrate your own widgets with Dashboard 2.0](https://flowfuse.com/blog/2023/10/dashboard-integrations/) - With the 0.6.0 Release of Dashboard 2.0, we now support third-party widget integration. Read more in this deep dive. - [What are FlowFuse Blueprints?](https://flowfuse.com/blog/2023/10/blueprints/) - Preconfigured Node-RED Applications ## Join Our Team FlowFuse is expanding our team. Check out the current openings: - [Contract Front-End Engineer – Node-RED Dashboard](https://boards.greenhouse.io/flowfuse/jobs/4911532004){rel=""nofollow""} # Chart Improvements & Migrating to Dashboard 2.0 It's been a little while since we've done an update, since we last posted we've moved into the 0.7.x releases for Dashboard 2.0. With these we're making big strides in improving the UX for charting your data, as well as starting to focus on migration paths from Dashboard 1.0 to 2.0. ## Package Name Changes Firstly a bit of news regarding the `npm` package we publish. Inline with our own [company name change](https://flowfuse.com/blog/2023/08/flowforge-is-now-flowfuse), we've had to update Dashboard 2.0's npm package, and so, we've changed from `@flowforge/node-red-dashboard` to `@flowfuse/node-red-dashboard`. In the short term, we'll be keeping @flowforge available on the Node-RED Palette Manager, but it will be removed soon, and the associated NPM Package will be put into a "deprecated" mode. ### NPM Package Migration Unfortunately, this migration from `@flowforge/` to `@flowfuse/` requires a little bit of manual work, and isn't as easy as just clicking "update" in the Node-RED Editor. In any case, there is no need to update your flow in this migration, you'll just need to uninstall `@flowforge/node-red-dashboard` and install `@flowfuse/node-red-dashboard` instead. #### Running Locally If you're running Node-RED locally, or in your own infrastructure, you'll need to manually uninstall the old package: ```bash npm uninstall @flowforge/node-red-dashboard ``` and re-install the new one: ```bash npm install @flowfuse/node-red-dashboard ``` #### Running in FlowFuse Navigate to your Instance > Settings > Palette, and then change the `@flowforge/node-red-dashboard` entry to `@flowfuse/node-red-dashboard` (the latest version as of this post is `0.7.2`). Restart your instance, and the new package will automatically install. ## Dashboard 1.0 to 2.0 Migration Guide As part of our mission to ensure a smooth transition from Dashboard 1.0 to Dashboard 2.0, we have published a first draft of a [Migration Guide](https://dashboard.flowfuse.com/user/migration.html){rel=""nofollow""}. As a starting point, we have comprehesively covered the Dashboard 1.0 widgets and their associated properties. We've then detailed which properties are already supported, which have partial support, and where appropriate, we do not support and *why* (most of the time, it's just because we haven't got round to it yet!) ![Migration Guide Snippet](https://flowfuse.com/blog/2023/11/images/migration-guide-snippet.png) We fully appreciate that the migration path is not yet complete, and we that we are missing some features and properties, but please know that we are working hard to ensure that as many of the features from Dashboard 1.0 are available in Dashboard 2.0. We will be updating this guide as we progress. We will also be adding "event" and "dynamic properties" sections to the guide, to detail how you can update and control elements via runtime messages (e.g. dynamically change the label of a button), and how this differs (if at all) from Dashboard 1.0. Whilst we aren't quite there yet, this guide offers a comprehensive breakdown on our progress in backporting all of the properties from Dashboard 1.0 ### Automated Script An ambitious plan that we have is to also provide a [Migration Script](https://github.com/FlowFuse/node-red-dashboard/issues/261){rel=""nofollow""}. Any feedback, ideas or concerns are most welcome as comments on the issue. Whilst this will never provide 100% perfect migration, we hope to be able to provide a script that can be run against your flows to automatically convert as much as possible from Dashboard 1.0 to 2.0. In most cases, as you can see in the Migration guide, we match most properties 1:1, so this should do a lot of the heavy lifting for you. ## Updates to UI Chart ### Key Mapping One of the core purposes of Node-RED Dashboard has always been to provide low-code access to charting your data. With the 0.7.x releases we've made some big improvements to the UI Chart node to make it easier to use and more powerful. In Dashboard 1.0, it was common place to have to regularly re-format your own data into `{x, y}` structure to be chart-friendly. In `0.7.0` we've introduced the concept of **key mapping**, where you can specify which keys in your data object should be used for the x and y axes. This means you can now pass in data in the format you want to use, and the chart will do the rest. For example, when rendering a chart of our weekly npm downloads, we have a data structure: ```json [{ "day": "YYYY-MM-DD", "downloads": 128 }, { "day": "YYYY-MM-DD", "downloads": 256 }, { "day": "YYYY-MM-DD", "downloads": 512 }] ``` Rather than having to pipe this into a `function` node and re-map the properties to `{ x, y }`, we can now use the `ui-chart`'s key mapping properties: ![ui-chart-key-mapping](https://flowfuse.com/blog/2023/11/images/ui-chart-keymap-properties.png) Resulting in the following chart: ![ui-chart-key-mapping](https://flowfuse.com/blog/2023/11/images/ui-chart-mapping.png) ### Multiple Lines Note above, another new option has been added to define *"Series"*. In Dashboard 1.0 this was fixed as `msg.topic` at all times, and defined which line/series data points rendered too. Now, this is configurable, and can even be set as a `key:` type too, whereby each data point being treated individually, and grouped based on a given key/property. Another great new feature here is the type `JSON` for this property. We can provide a *list* of series labels, and the chart will render each value from a single data point as separate lines. For example, if we consider the data: ```json [{ "day": "2023-10-23", "temperature": 28, "humidity": 16 }, { "day": "2023-10-24", "temperature": 26, "humidity": 19 }, { "day": "2023-10-25", "temperature": 27, "humidity": 24 }] ``` We can provide a series: `["temperature", "humidity"]` like so: ![ui-chart-key-mapping](https://flowfuse.com/blog/2023/11/images/ui-chart-series-property.png) Which would result in the following plot: ![ui-chart-key-mapping](https://flowfuse.com/blog/2023/11/images/ui-chart-multipoint.png) We appreciate this offers a new way of working with data in Dashboard, but hopefully, once you've tried it, you'll find it much easier to work with, and see the value it brings when working with your own data sets. ## What else is new in 0.7.x? Whilst we focussed this article on the migration paths and new UI Chart features, we did also squeeze quite a lot more into the 0.7.x releases too with plenty other fixes and improvements: - [Re-architecture of Server-side State Management](https://github.com/FlowFuse/node-red-dashboard/pull/279){rel=""nofollow""} - [Y Axis Min/Max Options](https://github.com/FlowFuse/node-red-dashboard/pull/327){rel=""nofollow""} - ["Focus" button for widgets added to Sidebar](https://github.com/FlowFuse/node-red-dashboard/pull/320){rel=""nofollow""} - [No more "blue screen" and improved error reporting](https://github.com/FlowFuse/node-red-dashboard/pull/310){rel=""nofollow""} - [Better route handling](https://github.com/FlowFuse/node-red-dashboard/pull/301){rel=""nofollow""} You can also read the more comprehensive release notes for each release here: - [0.7.0 Release Notes](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.7.0){rel=""nofollow""} - [0.7.1 Release Notes](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.7.1){rel=""nofollow""} - [0.7.2 Release Notes](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.7.2){rel=""nofollow""} ## Follow our Progress As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} # Overhauling the Dashboard 2.0 Build Pipeline As a developer, sometimes you have to hold up your hands and realise something you've spent two weeks building needs to be thrown away and restarted. Having shipped the [third-party widget support for Dashboard 2.0](https://flowfuse.com/blog/2023/10/dashboard-integrations/) in line with Dashboard 1.0's approach, we then had the [feedback](https://github.com/FlowFuse/node-red-dashboard/issues/307){rel=""nofollow""} that the way Dashboard 1.0 did things really wasn't good, and asking us to consider re-building the process to make the developer experience of working with Dashboard 2.0 far more seamless. So, that's exactly what we've done with the `0.8.0` release, amongst a few other things. ::div --- style: "background-color: #fff4b9; border:1px solid #ffc400; color: #a27110; padding: 12px; border-radius: 6px; font-style: italic;" --- Reminder: all new releases of Dashboard are now under the `@flowfuse`{style="background-color: transparent;"} namespace, so you'll need to update to use `@flowfuse/node-red-dashboard`{style="background-color: transparent;"}, and not `@flowforge`{style="background-color: transparent;"}. :: ## Migrating our Build Pipeline Without getting *too* technical, as part of this work in supporting third-party widgets, we overhauled our build pipeline for Dashboard 2.0. This pipeline is responsible for taking our source code, and compiling it into a format that then gets deployed by Node-RED when running Dashboard. Previously, we used ***Webpack***, but now, we've switched over to ***Vite***. This is a newer build tool, and is much faster than Webpack. It's also what we've now updated out [Example Node](https://github.com/FlowFuse/node-red-dashboard-2-ui-example){rel=""nofollow""} to use too. So now, when working with a third-party widget, Vite builds up all of your code, wraps it into a single `umd.js` file, and Node-RED then serves that file up for Dashboard 2.0 to load in. ![Vite Build Process](https://flowfuse.com/blog/2023/11/images/dashboard-build.png) We've also re-written our ["Building Third Party Widgets"](https://dashboard.flowfuse.com/contributing/widgets/third-party.html){rel=""nofollow""} guide to reflect this change. ## Debugging Dashboard A new feature we've added in `0.8.0` is also for those developing Dashboard's core and third-party widgets. You can now navigate to `/dashboard/_debug` to explore the full configuration that Dashboard receives from Node-RED. This is particularly useful when you're trying to debug why a widget isn't loading, showing the correct data, or generally isn't behaving as you expect. ![Dashboard's Debug View](https://flowfuse.com/blog/2023/11/images/debug-view.png) Note in the above example, where we can see that the `ui-dropdown` has had it's options overriden by `msg.options` on injection. You can read more about the debugging view [here](https://dashboard.flowfuse.com/contributing/widgets/debugging.html){rel=""nofollow""} ## What else is new in 0.8.0? Whilst we focussed this article on the build pipeline overhaul, changes to third-party wdgets and debugging Dashboard, we did also squeeze quite a lot more into the 0.8.0 releases too with plenty other fixes and improvements: - [Dynamic setting of msg.options for UI Dropdown](https://github.com/FlowFuse/node-red-dashboard/pull/345){rel=""nofollow""} - ["Date" type for UI Text Input](https://github.com/FlowFuse/node-red-dashboard/pull/346){rel=""nofollow""} - [Finer grain controls of Text Input event emissions](https://github.com/FlowFuse/node-red-dashboard/pull/365){rel=""nofollow""} - [Control over when UI Slider emits events](https://github.com/FlowFuse/node-red-dashboard/pull/367){rel=""nofollow""} - [Improved documentation for Bar Charts](https://github.com/FlowFuse/node-red-dashboard/pull/364){rel=""nofollow""} You can also read the more comprehensive release notes for the release here: - [0.8.0 Release Notes](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.8.0){rel=""nofollow""} ## Follow our Progress As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} # Tracking Who Has Opened a Dashboard As we continue to add features to the Node-RED Dashboard v2 one feature request that came in was to track which users had visited a Dashboard. Multi user support for the Dashboard is on the backlog but this could be solved with the parts that are currently available. ## FlowFuse Authentication One of the features we offer on FlowFuse is the ability to protect HTTP endpoints and Dashboards using the same FlowFuse user authentication that protects access to the FlowFuse Application and the Node-RED instances. We even offer a specific RBAC 'viewer' role that just allows access to these endpoints but not the FlowFuse application. FlowFuse authentication can be enabled from the Instance Settings page on the Security tab This can be used to secure access to a Dashboard hosted in a Node-RED Instance. At the moment the Dashboard while protected by this authentication, it is not aware of which user is accessing it. But if we include an element in the Dashboard loaded via a HTTP-in/HTTP-response node we gain access to details of the authenticated user. ## Implementation First we will create a HTTP-in/HTTP-response pair to serve up a single pixel SVG image. I chose SVG as it doesn't require creating a binary image file to load. The following flow snippet includes both the HTTP-in/HTTP-response nodes and a change node to set the `msg.payload` to the SVG content and to set the HTTP headers to include the correct mime type. There is also a second change node which extracts the user information. ::render-flow ```json [{"id":"7f22dc81d8192d4d","type":"http in","z":"98c8d7ea66149291","name":"","url":"/tracker","method":"get","upload":false,"swaggerDoc":"","x":210,"y":460,"wires":[["7d36739c02cd04ec","5f4647c97917cce1"]]},{"id":"58fd30516a077e29","type":"http response","z":"98c8d7ea66149291","name":"","statusCode":"","headers":{},"x":630,"y":460,"wires":[]},{"id":"7d36739c02cd04ec","type":"change","z":"98c8d7ea66149291","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":" Sorry, your browser does not support inline SVG.","tot":"str"},{"t":"set","p":"headers","pt":"msg","to":"{\"Content-Type\":\"image/svg+xml\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":460,"wires":[["58fd30516a077e29"]]},{"id":"5f4647c97917cce1","type":"change","z":"98c8d7ea66149291","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"req.session.user","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":520,"wires":[["ddc02b4e9c30c807"]]},{"id":"ddc02b4e9c30c807","type":"debug","z":"98c8d7ea66149291","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":600,"y":520,"wires":[]}] ``` :: Next we need to add the SVG to the Dashboard, this can be done by adding a Template node with the following HTML content. ```html
``` This will load the image every time the Dashboard page loads and hence trigger the earlier flow allowing the user to be logged. ## Linking to SSO Users With the release of FlowFuse v1.14.0 the session object will also include the users email address which is the shared identifier between FlowFuse and the SSO system. This will allow the logging to use a single unified identifier. # Deploying the FlowFuse Device Agent via Balena As part of the FlowFuse Staff Summit this year in Barcelona we met up with Marc Pous from [Balena Io](https://www.balena.io/){rel=""nofollow""}. Balena is a platform for managing fleets of Edge Devices and it felt like the perfect fit for deploying the FlowFuse Device Agent. To do this you install the Balena OS on the devices, this is a stripped down Linux distribution that includes a client that connects back to Balena's platform and creates a VPN tunnel. As well as the Balena client it includes Docker and users can select containers to push to the devices. These Docker container are hosted on Balena's own container registry and are built by doing a git push to Balena's git server. A GitHub repository with all the required files [has been published](https://github.com/FlowFuse/balena-device-agent){rel=""nofollow""}, a one click deploy button to allow you to quickly try this out. ## Building FlowFuse Device Agent for Balena We already build a FlowFuse Device Agent Docker container so it was pretty simple to modify the existing `Dockerfile` for Balena. ```docker FROM balenalib/%%BALENA_MACHINE_NAME%%-alpine-node RUN mkdir /opt/flowfuse-device RUN npm install -g @flowfuse/device-agent COPY entrypoint.sh /usr/src/entrypoint.sh ENTRYPOINT ["/usr/src/entrypoint.sh"] CMD ["flowfuse-device-agent"] ``` There were 2 main changes from the default Device Agent [`Dockerfile`](https://github.com/FlowFuse/device-agent/blob/main/docker/Dockerfile){rel=""nofollow""} 1. Change the base image to Balena's image, this is because the `Dockerfile` is actually a template that can be used to build images optimized for all Balena's supported hardware platforms (We currently build the FlowFuse Device Agent containers for AMD64, ARMv7 and ARM64) 2. Adding a custom `entrypoint.sh`. This is to ensure that the hostname seen in the container matches the Balena device name, making it easier to match it up with what is seen in the FlowFuse application. It also generates the configuration file from the passed in environment variable (see [below](https://flowfuse.com/#configuring-devices)) As well as the `Dockerfile` there is also a `docker-compose.yml` because Balena applications can be made up of multiple services packaged as container. In this case we just need a single container but the compose file contains all the information about what ports to expose and what volumes need creating to persist state. ## Configuring Devices The FlowFuse Device agent can be configured in 2 ways. 1. You can provide a configuration file that is provided by the FlowFuse application when you create a new Device. This file contains the unique identifiers for the Device and details of where to find the FlowFuse Application. This file can be provided to a Balena device by adding a device specific environment variable as described [below](https://flowfuse.com/#environment-variable). 2. You can provide a fleet of devices with a configuration file that contains details of where to find the FlowFuse application and a Provisioning token. Multiple Devices can all have the same Provisioning token and this will cause them to connect to the FlowFuse application on first start up and create a new Device bound to an existing team (and optionally an Application or Instance). This file can be passed to Balena devices by way of a Fleet wide environment variable as described [below](https://flowfuse.com/#environment-variable). You can create a Provisioning Token file under the Team -> Settings page on the Devices tab. ### Environment Variable Because the `device.yml` file is multi line it needs to be base64 encoded, you can do this with the following ```bash $ base64 -w 0 device.yml ``` You can then use the Balena console to create either a device specific or a fleet wide environment variable called `FF_DEVICE_YML`. ![balena-env-var](https://flowfuse.com/blog/2023/11/images/balena-env-var.png) # Meet FlowFuse at SPS Nuremberg FlowFuse is excited to be exhibiting at the SPS in Nuremberg next week. We will be located in Hall 5 Booth 145. At the SPS, we will be showcasing our latest FlowFuse platform, which is a powerful and user-friendly tool for creating and managing Node-RED flows. There's also an option to get a demonstration of how FlowFuse can be used to solve a variety of industrial automation problems. We are also keen to meet with Node-RED users and FlowFuse prospective customers at the SPS. If you would like to book a meeting with us, please visit our [contact us](https://flowfuse.com/contact-us/) page. We look forward to seeing you at the SPS! ### About FlowFuse FlowFuse is a software company that develops tools for creating and managing Node-RED flows. Node-RED is a popular open-source platform for flow-based programming. FlowFuse makes it easy to create and manage complex Node-RED flows, even for users with no prior programming experience. FlowFuse is used by a wide range of organizations, including manufacturers, utilities, and research institutions. FlowFuse is used to solve a variety of industrial automation problems, such as data acquisition, data enrichment, and process monitoring. ### About SPS Nuremberg SPS Nuremberg is the world's leading trade fair for electric automation. The fair takes place annually in Nuremberg, Germany. SPS Nuremberg is a showcase for the latest innovations in industrial automation. We hope to see you at the SPS! # Beyond Automation - AI Use Cases that are shaping the next manufacturing frontier Are we standing on the brink of a Fifth Industrial Revolution? The manufacturing industry has been in a state of flux for some time, with the rise of automation and digital transforming the way factories operate. But today, we are witnessing something even more profound: AI is pushing manufacturing to a whole new level. Some have even referred to it as “the fifth industrial revolution” due to its potential for disruption. But the question lingers for many plant managers and decision makers: in which AI-powered capabilities should one invest to bring about transformative changes in the manufacturing environment? As we navigate this question, I want to focus on three AI uses that are not only ripe for investment but also pivotal in driving manufacturing success in this competitive market. ## Empowering Citizen Developer Strategy with AI The concept of citizen development stands as one of the most significant fields in my opinion, a sentiment I've detailed in my [previous article](https://flowfuse.com/blog/2023/10/citizen-development/) about Citizen Developers. This approach is revolutionizing the manner in which applications are crafted and deployed across various industries. By empowering individuals, irrespective of their coding knowledge, to create applications, AI is dramatically hastening this process. Investing in AI capabilities that bolster your citizen developer strategy can fast-track application development, offering intuitive, template-driven platforms that employ AI to navigate users through the creation process. As we've seen over recent months and years, AI can significantly assist in code generation, thereby granting your citizen developers an even smoother initiation into application development. An excellent instance of this is the [article and Node-RED Node](https://flowfuse.com/blog/2023/09/chatgpt-for-node-red-developers/) describing the potential for integrating Node-RED with ChatGPT to assist you in building applications. This integration highlights the practical, user-friendly solutions made possible through AI, making the realm of app development accessible to a broader range of innovators. ## Refining Warehouse Management through AI-Driven Demand Forecasting In an era marked by complexities in supply chains and customer demand, AI's role in warehouse management becomes a game-changer. AI algorithms analyze historical data and market trends to predict future demand with astonishing accuracy, a step beyond traditional forecasting methods. For decision makers, investing in AI for demand forecasting means significantly minimizing overproduction or stock outs, optimizing inventory levels, and improving customer satisfaction. The advanced analytics offered by AI not only predict what products are in demand but also when and where they are needed, thereby facilitating strategic planning and resource allocation. ## Elevating Predictive Maintenance and Quality Control Unplanned downtime and quality inconsistencies are two of the biggest profit drains in manufacturing. AI's predictive capabilities are setting new standards in both maintenance and quality control protocols. By continuously monitoring equipment performance and production processes, AI can predict and identify machinery failures before they occur and detect quality deviations in real-time, allowing for immediate correction. Investing here means less downtime, reduced maintenance costs, improved product quality, and ultimately, an enhanced bottom line. ## Your Digital Infrastructure & Architecture is key While understanding where concrete Use Cases are is crucial, it’s equally important to ensure that your digital strategy and architecture can support and quickly adapt to these advanced AI implementations. See also [my article](https://flowfuse.com/blog/2023/08/isa-95-automation-pyramid-to-unified-namespace/) about the Unified Namespace. A flexible system that integrates a Unified Namespace is critical for seamless data exchange across various systems and applications. Moreover, fostering a citizen developer environment is fundamental in ensuring that these AI investments are maximally utilized, empowering your workforce to contribute actively to the company's innovation cycle. # Building a Custom Video Player in Dashboard 2.0 Dashboard 2.0 just got *a lot* more powerful with our new updates to the `ui-template` node. New features added to the node include: - Support for a full Vue component to be defined using the VueJS Options API. - Running of raw JavaScript within ` ``` Some quick gotchas to note: - `
{{ msg }}
` - is an example of how you render variables into the HTML. - `
` - lets you conditionally show/hide content based on a variable. - `
` - lets you loop over an array of items and render them into the HTML. - `
` - lets you bind a method to an event, in this case, when the user clicks on the div. - `
` - `:` is a way to define a "bound" property. In this case, the class `my-class` will be applied when `isActive` is true. - `console.log(this.myVar)` - when you're writing code inside the ` ``` With this functionality in place, we can wire the `ui-template` node to a `debug` node, and see the following when we play/pause the video: ![Example debug output when our custom build video player is played/paused](https://flowfuse.com/blog/2023/12/images/dashboard-video-2.png) #### 2. Remote control of play/pause from Node-RED We can use the built-in `$socket` variable to listen for incoming events from Node-RED. When Dashboard 2.0's nodes receive a `msg` inside Node-RED, they send a `msg-input:` event to the Dashboard client. We can listen for this event and then call the `play()` and `pause()` methods on the video element, depending on any properties of that message, in this case, the `msg.payload.event` value. ```html ``` #### 3. Seeking to a specific point in the video from within Node-RED With the `on('msg-input')` listener in place, we can now extend our handler to handle seeking to a specific point in the video. ```html ``` and with that, we now have a Dashboard 2.0 widget to display a video, that can be controlled from Node-RED, and logs details of user activity back into Node-RED. Other features available with the UI Template are detailed in the online documentation, and include: - [Loading External Dependencies](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#loading-external-dependencies){rel=""nofollow""} - [Running raw JavaScript](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#writing-raw-javascript){rel=""nofollow""} ## Follow our Progress You can also read the more comprehensive release notes for `v0.10.0` release here: - [0.10.0 Release Notes](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.10.0){rel=""nofollow""} As always, thanks for reading and your interest in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} # Run Node-RED as a service on Windows FlowFuse's device agent allows you to manage and run your Node-RED instances on your own hardware such as a Raspberry Pi or Windows computer. This can be very useful where an application you've written needs to run flows with direct access to hardware sensors. In this article, we're going to explain the steps to configure our device agent to run as a service in Windows using the [nssm](https://nssm.cc/){rel=""nofollow""} utility. ## Why run the device agent as a service? The standard process for running FlowFuse's device agent is to start it on the command line using the command `flowfuse-device-agent`. This works fine for testing but for long-term installations it's useful to run the device agent as a service. Once running as a service, the device agent will continue to run even if you log off or the computer is restarted and no user is logged in. ## Summary The aim of this how-to is to install the FlowFuse device-agent as a service on a Windows computer. There will be two main parts to this: 1. Install the device-agent 2. Setup the device-agent to run as a Windows service Additionally, two user accounts will be needed for this configuration: 1. A **user** account that will be used to run the device-agent (typically, non-admin account) 2. An **admin** account that can run elevated commands and will be used to setup the service We will create a directory for the device-agent files and set the permissions on that directory so that the **user** account can read and write files in that directory. *This will be `c:\opt\flowfuse-device`* To make the device-agent run as a service, we will (in this example), use [nssm](https://nssm.cc/){rel=""nofollow""} but you are free to choose an alternative tool to run the device agent as a service. Finally, we set the service to run under the **service** account. *NOTE: The instructions in this how to were written on **Windows 11 Pro 22H2*** ### TIP: Using domain accounts If the account is a domain account, append the domain name to the **user** e.g. `user@domain` whenever the **user** name is used in the instructions below. ### TIP: Launching an elevated command prompt window (e.g. as the admin user) ```bash powershell -Command "Start-Process 'cmd' -Verb runAs ``` :cta-image{alt="Power Workplace relies on FlowFuse for scalability, reliability and security audits - book a demo" cta="demo" src="https://flowfuse.com/images/cta/power-workplace-book-demo.png"} ### TIP: Launching an elevated powershell prompt window (e.g. as the admin user) ```bash powershell -Command "Start-Process 'powershell' -Verb runAs ``` ## Pre-requisites ### Install Node.js The device-agent requires Node.js to be installed. You can download the latest version from {rel=""nofollow""}. It is recommended to install the LTS version and to check the "Automatically install the necessary tools" option. This is especially important if you intend on using any nodes that require native modules (like serialport). ### Create a New Windows User If you need to create a new **user** account follow these [instructions](https://support.microsoft.com/en-us/windows/create-a-local-user-or-administrator-account-in-windows-20de74e0-ac7f-3502-a866-32915af2a34d#:~\:text=Select%20Start%20%3E%20Settings%20%3E%20Accounts%20and,other%20user%2C%20select%20Add%20account.){rel=""nofollow""}. ## Prepare the device-agent files directory As the admin user, open an [elevated](https://flowfuse.com/#tip-launching-an-elevated-command-prompt-window-eg-as-the-admin-user) command prompt, create the files directory and setup access permissions. ```bash # In an elevated command prompt mkdir c:\opt mkdir c:\opt\flowfuse-device # grant full access to the service account that will run the device-agent icacls c:\opt\flowfuse-device /grant "user":F /T ``` *where `"user"` is the service account (not the admin account)* ## Install nssm `nssm` can simply be downloaded and executed from any path. We will download it to the `c:\opt` directory, extract the files and copy the 64 bit version to the current directory. ### `cmd` version [elevated](https://flowfuse.com/#tip-launching-an-elevated-command-prompt-window-eg-as-the-admin-user) command prompt ```bash # starting in the device-agent files directory cd c:\opt # download the nssm zip file curl -LJO https://nssm.cc/release/nssm-2.24.zip # extract the files tar -xf nssm-2.24.zip # copy the 64 bit version to the current directory copy nssm-2.24\win64\nssm.exe . # clean up del nssm-2.24.zip rmdir /s /q nssm-2.24 ``` ### `powershell` version [elevated](https://flowfuse.com/#tip-launching-an-elevated-powershell-prompt-window-eg-as-the-admin-user) powershell prompt If you don't have `cURL` installed, then powershell can be used to download the file. Here is how to do it: ```powershell # starting in the device-agent files directory cd c:\opt # download the nssm zip file Invoke-WebRequest -Uri https://nssm.cc/release/nssm-2.24.zip -OutFile nssm-2.24.zip # extract the files Expand-Archive -Path nssm-2.24.zip . # copy the 64 bit version to the current directory Copy-Item -Path .\nssm-2.24\win64\nssm.exe -Destination . # clean up Remove-Item -Path nssm-2.24.zip Remove-Item -Path nssm-2.24 -Recurse ``` ### Manual download If you prefer, you can download the nssm zip file manually from {rel=""nofollow""} and extract the files to the `c:\opt` directory. Then copy the 64 bit version to the current directory. Ultimately, you should end up with a file named `nssm.exe` in the `c:\opt\` directory. ## Install and configure the device-agent As the **service** account, to do so open a command prompt window and run the following and authenticate: ```bash runas /user:{serviceuser} cmd # e.g. runas /user:winserv cmd ``` *where `{serviceuser}` is the service account (not the admin account)* ### Check the users npm global path is set in the Users Environment Variables NOTE: The recommended flowfuse-device-agent instructions will result in the flowfuse-device-agent being installed in the NPM global directory. And the instructions to launch the device-agent expect the NPM global directory to be in your user path. This section will instruct you to a) find the NPM global path, then b) check the user’s path setting and, if necessary c) add the NPM global path to your user path. First, make a note of the path currently set for npm global. You can do this by running the following command: ```bash npm config get prefix ``` Next, ensure the `Path` Variable under the "User variables for *user*" contains the npm global path that we obtained in the previous step. Use the below command, to check the user’s `Path` setting. If it is not present, edit the path to include it. ```bash # This commands opens the environment variables editor, # look for the "Path" variable under "User variables for user", # and ensure it contains the npm global path rundll32 sysdm.cpl,EditEnvironmentVariables ``` If you did have to add the npm path to the users `Path` variable, you will need to **restart** the command prompt for the change to take effect and relogin as **user**. ### Install the device agent Note: you may have already installed the device-agent, however, **we strongly recommend** you do this step again as the service account and ensure that account has the latest version. ```bash npm i -g @flowfuse/device-agent ``` ### Link the device-agent to your flowfuse team First, we must run the device-agent and link it to our FlowFuse team. This will generate a "device configuration" details that we will use to configure the device-agent. Below is how to run the device-agent with the UI enabled. This will allow you to configure the device-agent via its web UI. ```bash flowfuse-device-agent --ui --ui-port 8080 --ui-user admin --ui-pass admin -d c:\opt\flowfuse-device -p 1880 ``` The device-agent will now be running and you can access the UI at {rel=""nofollow""} with the user and password both "admin" (you can change these in the command line if required). *NOTE: These credentials are temporary and only valid during the device setup* Proceed to configure the device-agent and link it to your flowfuse team. Full instructions can be found [here](https://flowfuse.com/docs/device-agent/register). Once you have linked the device-agent to your team, you can stop it by pressing `ctrl+c` in the command prompt window. ## Create the device-agent service As the admin user, open an elevated command prompt see [TIP](https://flowfuse.com/#tip-launching-an-elevated-command-prompt-window-eg-as-the-admin-user) above ### Install device-agent as a service ```bash cd c:\opt .\nssm.exe install flowfuse-device-agent "flowfuse-device-agent.cmd" .\nssm.exe set flowfuse-device-agent AppDirectory "c:\opt\flowfuse-device" .\nssm.exe set flowfuse-device-agent Description "FlowFuse Device Agent" # set the AppParameters (cli options) to tell the agent where its home directory is # in our case, this is c:\opt\flowfuse-device and is set with the -d option .\nssm.exe set flowfuse-device-agent AppParameters "-d c:\opt\flowfuse-device -p 1880" ``` ### Check the service is installed Run the following command to check the service is installed: ```bash services.msc ``` (look for a service named \`flowfuse-device-agent'). Alternatively, you can use the `sc` command: ```bash sc query flowfuse-device-agent ``` ### Set the user account that will run the service Some things are easier to edit in the UI, so we will edit the service via the NSSM UI. ```bash nssm edit flowfuse-device-agent ``` ![nssm editor](https://flowfuse.com/blog/2023/12/images/nssm_service_editor.png) In the UI, you can edit the service name, description, startup type, etc. The most important thing to check is the `Application` tab. This includes the path to the flowfuse-device-agent.cmd and its arguments. Select the "Log on" tab, select "This account" and enter the service account name and password that will run the device-agent. Click the "Edit Service" button to save the changes. Now you have a service that will run the device-agent as the **service** account 🎉 ### Controlling the service You can start the service with the command: ```bash sc start flowfuse-device-agent ``` You can check the current status with the command: ```bash sc query flowfuse-device-agent ``` You can stop the service with the command: ```bash sc stop flowfuse-device-agent ``` ### Further reading If you'd like to learn about windows services via the `sc` command you can access the help text by running `sc` from a command prompt. # Thank you for an incredible 2023! At the end of the year there’s always an opportunity to review how the year went, and I'm gonna take this opportunity to share the review of 2023 for FlowFuse. It's been an incredible year for FlowFuse and we've achieved a lot with our team. ### FlowFuse branding First off; in 2023 we were known as FlowForge. But, due to some trademark challenges, we went through a [rebranding phase](https://flowfuse.com/blog/2023/08/flowforge-is-now-flowfuse/) over the summer. It's been a bit of an adjustment, and you might still catch us – and even some of our customers – occasionally slipping up with the old name. But overall, we're really pleased with how smoothly everything's transitioned. ### Product adoption On the product side: Growth in adoption of the FlowFuse platform has been tremendous, in many dimensions; revenue generated, customers onboarded, and how many users are now professionalizing their usage of Node-RED. Our development platform has been used by thousands of developers to acquire data from various sources and visualize it to build rich applications for their use cases. And at every step of the way we’ve been able to improve their experience. Data acquisition was always possible without FlowFuse, however we’ve improved on the status quo through the [Certified Nodes](https://flowfuse.com/integrations/?certified=1) program. A nascent program that vets often used custom nodes from the community to ensure business readiness and validate nodes to ensure there’s no malicious code installed. Further, this year we’ve started [Dashboard 2.0](https://dashboard.flowfuse.com/){rel=""nofollow""}. The development of the successor of [node-red-dashboard](https://flows.nodered.org/node/node-red-dashboard){rel=""nofollow""}, which is built on deprecated technology and effectively on life-support. The development of the new Dashboard technology has taken massive steps and it’s very stable. Feature parity is not yet achieved, though we’re happy with the adoption of Dashboard 2.0 and the community [reporting issues and improvements](https://github.com/FlowFuse/node-red-dashboard/issues){rel=""nofollow""}. With the product improvements to FlowFuse, we’ve empowered large audiences to try and adopt the product. We’ve seen adoption in many areas: 1. Process manufacturing – Mining, oil & gas, beverages. 2. Discrete manufacturing – Ranging from automotive to logistics use-cases. 3. Digital transformation – Digital only integration, from website back-ends to workflow engines for up-skilled employees. 4. Agriculture – From environment monitoring to controlling sprinklers, water-pumps, and more. 5. Education & Research – Hundreds of students have registered for FlowFuse to learn how IIoT works, start building solutions, and prepare themselves for employing these skills in their first jobs. We’re always happy to [support you](https://flowfuse.com/contact-us/) in any of these or other industries where you may find value in how we streamline operations, manage your data acquisition logic and roll out, and remain compliant during your journey. ### Pricing changes A driving force behind the horizontal nature of adoption of the FlowFuse development platform has been the introduction of product tiers earlier this year. We’ve introduced Team and Enterprise tiers. A user can be promoted to the [Team or Enterprise tier](https://flowfuse.com/changelog/2023/09/introduction-enterprise-tier/) when they’re ready to further professionalize their adoption and have access to enhanced compliance, faster time to value for their developers, among other features. ### What 2024 will bring FlowFuse will continue to support the Node-RED community at large. In 2024 we’re looking to further grow into the main development platform for low-code developers. While our main focus will remain around Node-RED, there’s more to be done to become the defacto low-code development platform. Core of which are a few pillars: 1. Time to value for developers 2. Empowering more employees to automate the software layer of the solution 3. Enhanced compliance and enforcement We’re hoping to continue to serve our customers and grow the customer base in 2024! ### Holiday Season Support Over the next couple of weeks (22nd December - 2nd January), most of our team will be taking some well deserved time off. Don't worry, we will still be available if you need emergency support. The best way to contact us is via our website's [support page](https://flowfuse.com/support/). # Introduction to the Unified Namespace (UNS) – 2026 Updated Guide As your organization is generating more data there’s key architectural decisions to be made to ensure the full value can be unlocked and you’re leveraging not just the tip of the iceberg. The [Unified Namespace (UNS)](https://flowfuse.com/use-cases/uns/) provides a blueprint to allow data to be consumed by many data-consumers. FlowFuse helps you manage this migration and the operationalization of your data. To facilitate a many to many connection between data producers and data consumers, there are two changes to be made to your architecture: 1. Data transport through a hub-and-spokes model 2. Set structure of the Data ## Hub and spokes model replaces Point to Point Traditionally, for example web servers serving web pages, the client requests a page from a server. This is a point to point connection between those two parties. ![Point to point graphic](https://flowfuse.com/blog/2023/12/images/uns-point-to-point.png "Point to Point connection") For the same data to be transmitted to a new data consumer, the consumer needs to make another request to obtain the data. This works great when you know what data you need for building your solution, and if you know where to get it. However, in manufacturing it’s not always possible to know up front who will need your data. Some machines are built and placed years before another machine would like to interact with the generated data. There might be many consumers for the same data set. Lastly; consumers might not know when to fetch new data points, and thus will try on a cycle or need another mechanism to understand if new data is available. Also, there are many challenges in point-to-point connections. For a deeper explanation, read [Why point-to-point connection is dead](https://flowfuse.com/blog/2024/11/why-point-to-point-connection-is-dead/). This is why a hub and spoke model should be employed. For each data source or data producer, a connection is made to a central hub; generally called a broker. ![Hub and spoke graphic](https://flowfuse.com/blog/2023/12/images/uns-hub.png "Unified Namespace Hub and Spokes communication") ## Structured data When many producers are connected to many data consumers, but not directly, the data producer needs to provide insight into what the events can be and will contain. It cannot, nor should even if it could, tailor the event’s data structure for a consumer so there’s decoupling on an architecture level. Structured data makes information, without structure the consumer receives mere bytes. This means that a schema for each event should be created and maintained. A schema which both the producer and consumer can read and validate each event against. As such a common Schema Definition Language (SDL) is chosen to provide clarity of how the data is structured, how it can be parsed, and in some cases also what it means for the developer. ## How Node-RED Fits In Node-RED excels in implementing a Unified Namespace with its flexible and powerful capabilities. It can act as both a central hub and a data consumer within a hub-and-spokes model, simplifying the integration of various data sources and consumers. By leveraging Node-RED's extensive library of nodes and its easy-to-use flow-based programming interface, organizations can efficiently manage data ingestion, transformation, and distribution. Node-RED also supports structured data through its support for JSON, XML, and other standard formats, allowing for clear and consistent data schemas. With its built-in nodes for MQTT, HTTP, and other protocols, Node-RED can seamlessly integrate with existing systems, enabling real-time data exchange and visualization. This makes it an ideal tool for operationalizing the Unified Namespace, ensuring that data flows efficiently and is readily available to all relevant stakeholders. Read this article to learn how you can build your Unified Namespace using Node-RED and FlowFuse: [Building a Unified Namespace with FlowFuse](https://flowfuse.com/blog/2024/11/building-uns-with-flowfuse/) You can also watch this webinar that explores the core concepts of the Unified Namespace, explains why it is essential for Industry 4.0, and demonstrates how FlowFuse and HiveMQ can be used together to build a scalable Unified Namespace. ::lite-youtube --- params: rel=0 style: "margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title: YouTube video player videoid: z62O5RrOK8o --- :: ### How FlowFuse Can Help While Node-RED is highly effective for implementing UNS, managing and deploying it can be complex. FlowFuse provides a unified platform that simplifies deployment with one-click operations, secure management, and scalable Node-RED applications. It also includes features that enhance collaboration, alongside offering centralized management of all Node-RED instances to ensure streamlined operations and increased efficiency. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} now for a free trial and experience FlowFuse's features** # Data Modeling for your Unified Namespace In the realm of industrial manufacturing, the concept of a Unified Namespace (UNS) emerges as a pivotal instrument for enhanced communication within a manufacturing network framework. Predicated on an event-driven architectural model, this approach advocates for the universal accessibility of data, irrespective of the immediate presence of a data consumer. This paradigm allows for a flexible role allocation within the network, where nodes can dynamically switch between being data producers and consumers, contingent upon the fluctuating requirements of the system at any specific juncture. For those unfamiliar with UNS, I recommend revisiting my [previous article](https://flowfuse.com/blog/2023/08/isa-95-automation-pyramid-to-unified-namespace/) on the subject. This article aims to explain the process of data modeling for your UNS, highlighting the role of tools like the FlowFuse Team Library in schema management. **Overview of Steps:** 1. [Connection to your Operational Technology (OT) equipment](https://flowfuse.com/#step-1-connection-to-your-operational-technology-ot-equipment) 2. [Structuring your payload](https://flowfuse.com/#step-2-structuring-your-payload) 3. [Building your Topic Hierarchy](https://flowfuse.com/#step-3-building-your-topic-hierarchy) 4. [Connection to your Unified Namespace](https://flowfuse.com/#step-4-connection-to-your-unified-namespace) ## Step 1 - Connection to your Operational Technology (OT) equipment The journey begins with establishing connections to OT equipment, which may include Programmable Logic Controllers (PLCs), Historian databases, and sensors. It is essential to facilitate compatibility with a diverse array of protocols. In this context, Node-RED emerges as a pivotal tool, bolstered by its expansive community-generated catalog featuring over 4500 nodes. In my example, the focus is on integration with a RevolutionPi. To achieve this, the FlowFuse Device Agent was deployed on a RevolutionPi (see our [documentation](https://flowfuse.com/docs/hardware/raspbian/)), and specific RevolutionPi nodes were installed. These nodes enable direct interaction with all interfaces of the PLC and are available through the [Node-RED library](https://flows.nodered.org/node/node-red-contrib-revpi-nodes){rel=""nofollow""}. Subsequent steps involved acquiring temperature data directly from the PLC. ![](https://flowfuse.com/blog/2023/12/images/revpi_nodes.png) For optimal data accuracy and integrity, it is recommended to timestamp data at the point of origin. In our scenario, the PLC outputs lack inherent timestamping. Consequently, I integrated a timestamp at the data acquisition stage within Node-RED, which runs on the same hardware. A general recommendation is the imperative of maintaining data integrity during transmission from OT systems to the message broker. This is particularly salient in regulated sectors such as pharmaceuticals, where standards like GxP mandate the preservation of unaltered data during transfer to the UNS. ## Step 2 - Structuring your payload The payload is the core of transmitted data. Transforming the payload for mutual intelligibility between sender and receiver, even within the same protocol, is sometimes necessary. Standardizing payload formats ensures consistent data storage and transmission. I recommend including schema type information with the data to cater to diverse use cases. Utilizing FlowFuse and Node-RED can enforce schema consistency. Node-RED's template node lets you define JSON schemas for your flows, while the FlowFuse Team Library facilitates schema sharing and consistency across your organization. In my example, I use a very simple JSON schema as a structure for measurements for `StationA`: ```json { "$schema": "Enterprise/Site/Line1/StationA/measurements/_schema", "title": "Measurement Schema for StationA", "type": "object", "properties": { "value": { "description": "The actual value being measured", "type": "number" }, "unit": { "description": "The unit of the measurement value", "type": "string" }, "timestamp": { "description": "The timestamp of the measurement in ISO 8601 format", "type": "string" } }, "required": [ "value", "unit", "timestamp" ] } ``` Example Data: ```json { "value": msg.payload, "unit": "Celsius", "timestamp": msg.timestamp } ``` ![Node-RED template node](https://flowfuse.com/blog/2023/12/images/template_node.png) The FlowFuse Team Library acts as my schema registry within my organization, allowing me to reuse my schemas and ensure consistency. ![FlowFuse Team Library](https://flowfuse.com/blog/2023/12/images/team_library.png) ## Step 3 - Building your Topic Hierarchy Your topic hierarchy should reflect your physical plant structure or align with existing asset naming systems. This approach improves data visibility and eases navigation for OT engineers. Many enterprises opt for the [ISA-95 part 2](https://www.isa.org/products/ansi-isa-95-00-02-2018-enterprise-control-system-i){rel=""nofollow""} model to structure their topics. In our example, we follow the structure of: Enterprise/Site/Line1/StationA ![MQTT Topic Tree](https://flowfuse.com/blog/2023/12/images/mqtt_topic_tree.png) ## Step 4 - Connection to your Unified Namespace Finally, transfer your data to the UNS, using protocols like MQTT or Kafka, depending on your UNS setup. While MQTT can handle up to 256 MB per payload, Kafka's default is 1MB, expandable to 10MB. These capacities suffice for most data types. In our example, we'll employ MQTT. ## Conclusion In conclusion, implementing a Unified Namespace (UNS) with efficient data modeling is a transformative step for any industrial manufacturing setup. By leveraging tools like FlowFuse Team Library, Node-RED, and protocols such as MQTT or Kafka, organizations can achieve a harmonious data ecosystem where information flows seamlessly across various nodes. As illustrated through practical examples, including the integration with a RevolutionPi, the importance of standardizing data schemas, maintaining data integrity, and structuring topic hierarchies cannot be overstated. Embracing these practices not only enhances operational efficiency but also paves the way for more advanced analytics and machine learning applications. ### The complete Node-RED flow :iframe{allow="clipboard-read; clipboard-write" height="225px" src="https://flows.nodered.org/flow/f6c783c6e9c1863145e0c63418eb5fe5/share?height=100" style="border: none;" width="100%"} # Capture Data from edge devices with Node-RED While cloud computing has revolutionized data access and analysis, not all data can be accessed from the cloud. In many scenarios, data collection from the edge – the location where data is generated – is essential for real-time decision-making or process observability. FlowFuse enables data to be collected through Node-RED. Data can be processed locally on the edge or sent on to other services. FlowFuse doesn’t rely on continuous connections to the cloud, making it a good choice for locations with unreliable internet connectivity. Use cases like real-time monitoring of critical systems, proactive maintenance, and improved operational efficiency are now possible to implement. ## Installing the FlowFuse agent To manage the capturing of data on the edge we’re going to first install the FlowFuse agent. It’s installed on your device to manage the communication between the edge device and the FlowFuse server, manage the installation of Node-RED, its execution environment, and facilitate communication between devices and the cloud. The device agent can run anywhere you can run a Docker container or Node.JS runtime (version 16.0+) can be installed. ### Registering a device on FlowFuse For the edge device to know what it’s supposed to do, it needs to listen to the FlowFuse commands. The agent's configuration is provided by a `device.yml` file from FlowFuse. Go to the team you’d like to add an edge device to, and select “Devices” on the left-hand menu, followed by the “Add Device” button. ![Setting up a FlowFuse agent](https://flowfuse.com/blog/2024/01/images/flowfuse-agent-setup.png "Setting up a FlowFuse agent") FlowFuse will prompt you to add a name (required), and a type (not required). When you’ve clicked `Add` you’ll get a new dialog to download the required file. ![Configuration file for the FlowFuse agent](https://flowfuse.com/blog/2024/01/images/device-yml-flowfuse.png "The contents of a device.yml file") ### Install the FlowFuse agent through Docker If your device supports it, the fastest way to run the FlowFuse agent is with containers. Assuming you’ve already got Docker installed, there are two steps to follow: first, move the device YAML file downloaded from FlowFuse to the edge device and save it in `/opt/flowfuse/device.yml`. Start the agent by running: ```text docker run --mount type=bind,src=/path/to/device.yml,target=/opt/flowfuse-device/device.yml -p 1880:1880 flowfuse/device-agent:latest ``` Note that for production cases, ensure the container is restarted on reboot. Docker can do this for you, [please follow their guide](https://docs.docker.com/config/containers/start-containers-automatically/){rel=""nofollow""}. ### Install the FlowFuse agent with npm To install the agent through NPM, you’ll need a Node.JS version of 18.0 or later. Open a command prompt and run: `npm install -g @flowfuse/device-agent`. This will install the FlowFuse Device Agent as a global npm module, making the flowfuse-device-agent command available in any directory on your system. Once the installation is complete, you must configure the Device Agent to connect to your FlowFuse instance. In this guide, you’ve previously downloaded the `device.yml` file that’s needed now. On Linux or Mac, move the file to `/opt/flowfuse-device/device.yml`, and for Windows-based systems, move the file to `c:\opt\flowfuse-device\device.yml`. Afterward, start the agent with: `flowfuse-device-agent`. This will launch the Device Agent and connect it to your FlowFuse instance. The Device Agent will wait for instructions on which flows to run. ### Programming flows for the edge Now the agent is running, the FlowFuse platform will show it has contacted back to the platform and is ready to do some work. First, add it to the application and start the developer mode. That enables the device editor and provides you secure access to the editor anywhere in the world for everyone in the FlowFuse team with the right access role. When the development is done, be sure to create a snapshot of the developed flows to create a point-in-time backup, or to roll the snapshot out to many other devices later. # Node-RED Dashboard 2.0 is Generally Available! Back in [June 2023](https://flowfuse.com/blog/2023/06/dashboard-announcement/) we announced that FlowFuse would be investing into building out the next generation of Node-RED Dashboard, the most popular UI framework for Node-RED. We followed this up with the [first release](https://flowfuse.com/blog/2023/07/dashboard-0-1-release/) (`0.0.1`) in July, just one month later, and today, we are pleased to announce that we have reached a major milestone in this journey, with the release of our first major version (`1.0.0`) of Node-RED Dashboard 2.0. ![Dashboard 2.0 Example showing weather data](https://flowfuse.com/blog/2024/01/images/dashboard-ga-example.png) With our `1.0.0` release, you can now build your dashboards on a reliable and stable package, and we invite you to to start contributing your own third-party [widgets](https://dashboard.flowfuse.com/contributing/widgets/third-party.html){rel=""nofollow""} and [plugins](https://dashboard.flowfuse.com/contributing/plugins/){rel=""nofollow""}. We're excited to see what the community can contribute and build on top of this new Dashboard 2.0 framework, and we'll be continuing development to the core collection of widgets too. With Node-RED Dashboard 2.0, we have re-built the original Node-RED Dashboard from the ground up. It is now extensible due to it being VueJS-based, completely responsive down to mobile, and we've made many quality of life improvements across the board to the existing widget collection, as well as adding a few new ones too. ## What's new in Dashboard 2.0? We've shared plenty of updates since we started, detailing the feature parity with the original Node-RED Dashboard, as well as some of the new widgets and features we've added to the new Dashboard, such as Markdown, Mermaid Charts and new Layout Options, you can read more about those here: - [Dynamic Markdown, Tables & Notebooks](https://flowfuse.com/blog/2023/09/dashboard-notebook-layout/) - [UI Chart Improvements](https://flowfuse.com/blog/2023/11/dashboard-0-7/) - [Building a Custom Video Player](https://flowfuse.com/blog/2023/12/dashboard-0-10-0/) Furthermore, the most requested feature for the legacy dashboard has been implemented in Dashboard 2.0, the ability to hide charts and forms based on the user that's viewing the dashboard. ![Dashboard 2.0 Example showing personalised dashboard](https://flowfuse.com/blog/2024/01/images/multi-user-dashboard-user2.png) Read more about it here: - [Personalised Multi User Dashboards](https://flowfuse.com/blog/2024/01/dashboard-2-multi-user/) If that wasn't enough, we also have [rich documentation](https://dashboard.flowfuse.com/){rel=""nofollow""} for Dashboard 2.0 too, detailing all of the available nodes, details on how Dashboard 2.0 is built and how to contribute to the project too if you're that way inclined. ## Upcoming Webinar If you're interested in learning more about Dashboard 2.0 and in particular, personalised multi-user dashboards, we're hosting a webinar on Thursday, 29th February. You can find out more information [here](https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/) ## Follow our Progress We aren't stopping here, we'll continnue to push Dashboard 2.0 forward with future development, with a [new UI Gauge](https://github.com/FlowFuse/node-red-dashboard/issues/12){rel=""nofollow""} next on the list. You can track that progress of that particular issue, and the rest of the work we have lined up on our GitHub Projects: - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} - [Dashboard 1.0 Feature Parity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/5){rel=""nofollow""} If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. # Personalised Multi-user Dashboards with Node-RED Dashboard 2.0! This week has seen the release of the [first major version of Node-RED Dashboard 2.0](https://flowfuse.com/blog/2024/01/dashboard-2-ga), with it, we've made available a new FlowFuse-exclusive feature, personalised multi-user dashboards. This new feature will allow you to build applications that provide unique data to each user, build admin-only views, and track user activity, to name but a few. We're really excited to see what the Node-RED Community and our FlowFuse customers can do with such a powerful and flexible framework. ## Personalised Multi User Dashboards The original Node-RED Dashboard was built with a "single source of truth", no matter how many users interacted with the dashboard, each user would always see the same data. This is great for prototyping, or hobby projects, but as you scale up your Node-RED usage, you'll want to be able to have unique dashboard experiences for each user. ### Getting Started To enable personalised multi-user dashboards, you'll need to be using FlowFuse, and complete two steps: #### Step 1: Enable "FlowFuse User Authentication" All instances on FlowFuse can be configured with *"FlowFuse User Authentication"* in the "Security" Settings. This option requires any user that wants access to your Editor or dashboard to be authorized by FlowFuse first. !["Screenshot of the 'Security' settings available for any Instances running in FlowFuse"](https://flowfuse.com/blog/2024/01/images/multi-user-dashboard-ffauth.png "Screenshot of the 'Security' settings available for any Instances running in FlowFuse"){dataZoomable=""} **"Screenshot of the 'Security' settings available for any Instances running in FlowFuse"** #### Step 2: Install FlowFuse's User Addon ##### FlowFuse Cloud :cta-image{alt="Wenco deploys new dashboard pages in days with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/wenco-book-demo.png"} *Note: Every instance created from today onwards automatically comes with the necessary configuration. Already created instances need to be manually restarted.* The Personalised Multi-User Dashboard plugin, `@flowfuse/node-red-dashboard-2-user-addon`, is available in our [Certified Nodes](https://flowfuse.com/integrations/?certified=1) catalogue, accessible to our Teams and Enterprise customers. Once the "FlowFuse User Authentication" option has been enabled on your instance, you can then install our plugin, `@flowfuse/node-red-dashboard-2-user-addon`, through the "Manage Palette" option in the Node-RED Editor. For your devices, we provide the necessary configuration and access token upon request, so that your Node-RED devices can also benefit from a Personalised Multi-user Dashboard. ##### FlowFuse Self-Hosted For all our Teams and Enterprise Self-Hosted customers who also want to use the Certified Nodes and the Multi-User Dashboard, we provide all necessary configurations upon request to get started. Alternatively, if you're looking to elevate your Node-RED infrastructure, [book in a chat with us](https://flowfuse.com/contact-us) to talk about how FlowFuse can help. ### Using the Plugin Once enabled, any messages emitted by a Dashboard 2.0 node will contain a new `msg._client.user` object, e.g: ```js { "userId": "", // unique identifier for the user "username": "", // FlowFuse Username "email": "", // E-Mail Address connected to their FlowFuse account "name": "", // Full Name "image": "" // User Avatar from FlowFuse } ``` Then, when running Node-RED Dashboard 2.0 on FlowFuse, you'll have a new sidebar option in the Node-RED Editor, which allows you to control which node types allow for "client constraints". ![The new 'FF Auth' options available in Node-RED to allow for client constraints](https://flowfuse.com/blog/2024/01/images/multi-user-dashboard-ff-settings.png "The new 'FF Auth' options available in Node-RED to allow for client constraints"){dataZoomable=""} **A screenshot of the new 'FF Auth' options available in Node-RED to allow for client constraints on different node types.** In the original Node-RED Dashboard, this was *always* enabled for the `ui-notification` and `ui-control` nodes, whereby you could include `msg.socket` data and it would only then send that message to the specified client. For Dashboard 2.0 we've extended this concept so that as a Node-RED Developer, you can now include `msg._client.user` data in any message sent to a Dashboard 2.0 node. Under the covers, our FlowFuse-exclusive plugin will then automatically filter messages to only send to the relevant user's connection. Utilising this feature, below you can see an example where we send data to a `ui-template` to render a custom table for each user. Under the covers this is a `ui-event` node (triggered on a page view), which then uses the `msg._client.user` object to make a REST API call to retrieve a list of todo items for that specific user. We then wire the response into the `ui-template`, which has been configured to "Accept Client Constraints", and so only sends this data to User 2's dashboard. ![Showing Admin Task View](https://flowfuse.com/blog/2024/01/images/multi-user-dashboard-admin-tasks.png "Showing Admin Task View"){dataZoomable=""} **Example of a dashboard that displays user-specific content.** Note too that we're also utilising the new [Teleport](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#teleports){rel=""nofollow""} option available in a `ui-template` which allows us to define content to show in the top-right of the dashboard, in this case, a little *"Hi {username}"* message. ### Examples #### Rendering Logged In User Data In the previous example, you may have noticed that we're also displaying a welcome to the authenticated user on our dashboard, this means that we have access to the full User object within any `ui-template` that we render too. ![Showing User Unique Data](https://flowfuse.com/blog/2024/01/images/multi-user-dashboard-user2.png "Showing User Unique Data"){dataZoomable=""} **The "Admin" view that is only made available to users registered as an "admin".** Under the covers, we're appending our `user` object to the `msg` object, via the SocketIO `auth` option. We make the `socketio` object available via a computed `setup` variable, this means that we can access this data in any `ui-template` node, and render like so: ```html ``` To enable custom user-by-user content in a `ui-template` though, we must allow it to "Accept Client Constraints". This means that if a `.msg._client.user` value is included in any messages sent to a `ui-template` node, then the underlying SocketIO message will be filtered to only send to the relevant user's connection, and no others. #### Admin Only Views With this new functionality we can also now show/hide content based on the authenticated user. We recently introduced the option to [set default visiblity & interaction states](https://github.com/FlowFuse/node-red-dashboard/pull/484){rel=""nofollow""}. This was partly introduced because it's a good practice to set the default "Visibility" option for any admin-only pages to "Hidden", and then use a `ui-control` node to show the content only to the relevant admins. :iframe{allow="clipboard-read; clipboard-write" height="100%" src="https://flows.nodered.org/flow/2fe8e6f1e7002f1ff6a9195ad1a153b6/share" style="border: none; margin-bottom: 12px;" width="100%"} Let's breakdown the above flow: 1. We wire a `ui-event` node (which emits each time a user views a page) into a switch node 2. Our switch node checks the `user.username` against a known list of admin users and branches "admin:" and "non-admin" users 3. For admin users, the `change` node defines a message for our `ui-control` node to dynamically show content, in this case an "Admin" page, when appropriate. ```json { "pages":{ "show": ["Admin View"] } } ``` All events going into `ui-control` are automatically filtered based on the `msg._client.user` object, so only the Admin users will receive the message to show the "Admin View" page, resulting in: ![Showing Admin Only View](https://flowfuse.com/blog/2024/01/images/multi-user-dashboard-admin.png "Showing Admin Only View"){dataZoomable=""} **The "Admin" view that is only made available to users registered as an "admin".** Further extensions of this could also check `ui-event` in case a non-admin user tries to access the `/admin` page directly, in which case we can utilise `ui-control` to navigate the user away from the page immediately. See the [ui-control documentation](https://dashboard.flowfuse.com/nodes/widgets/ui-control.html#navigation){rel=""nofollow""} for more details on this. ## Upcoming Webinar If you're interested in learning more about Dashboard 2.0 and in particular multi-user Dashboards, we're hosting a webinar on Thursday, 29th February. You can find out more information [here](https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/) ## Follow our Progress We aren't stopping here, we'll continue to push Dashboard 2.0 forward with future development, and you can track that progress on our GitHub Projects: - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} - [Dashboard 1.0 Feature Parity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/5){rel=""nofollow""} If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. # FlowFuse 2.0 Release Following the release of FlowFuse 1.0 end of 2022, we're excited to release FlowFuse 2.0, marking a significant step in managing Node-RED remote instances, which we call Devices. FlowFuse already was the best place to operate Node-RED at scale in the cloud or on-premise, now it's able to manage Node-RED where ever it's run. Many organizations position Node-RED instances on remote servers like edge or industrial devices. This way they can meet network requirement, interact with analog protocols, and overcome other infrastructure requirements. Management of remote instances is crucial for the overall success of closing the gap between IT and OT. A key enhancement was the introduction of Device Groups (from version 1.15) and the new feature to assign target snapshots. This allows for direct and streamlined management of Node-RED Device fleets, setting the stage for future advancements in device management capabilities. For our FlowFuse users, this means it is no longer necessary or recommended to assign devices to an instance. Node-RED devices can be managed independently, and snapshots can be assigned via DevOps pipelines. ## Enterprise-Readiness FlowFuse is committed to augmenting the enterprise-readiness of Node-RED with introductions like [Single Sign-On (SSO)](https://flowfuse.com/docs/admin/sso/), [Multi-Factor Authentication (MFA)](https://flowfuse.com/docs/user/user-settings/#two-factor-authentication), and [High Availability](https://flowfuse.com/docs/user/high-availability/) since version 1.0. Furthermore, we recently achieved [SOC2 Type 1 compliance](https://flowfuse.com/blog/2024/01/soc2/). With these advancements, Node-RED, in combination with FlowFuse, is genuinely ready for enterprise and production use. ## Enhanced Integration Capabilities The Node-RED Flow Library has always been a cornerstone, offering over 4800 connectors (nodes) for various OT and IT protocols. Thanks to the community and the Node-RED library. Building on this foundation, FlowFuse introduced "Certified Nodes" and "Blueprints". These [Blueprints](https://flowfuse.com/blog/2023/10/blueprints/) are designed to provide an easier start with Node-RED, showcasing its full potential, while Certified Nodes ensure the security of the nodes used. Learn more about our new Certified Nodes [here](https://flowfuse.com/blog/2023/10/certified-nodes/). ## Developer Velocity Node-RED team development is made possible with FlowFuse. Different development team members are able to share and collaborate on the same Node-RED instance. This makes for much easier collaboration between Node-RED developers. We've worked hard on maturing our [snapshot capabilties](https://flowfuse.com/docs/user/snapshots/) and introduced [DevOps Pipelines](https://flowfuse.com/docs/user/devops-pipelines/) that can be set up to stage Node-RED instances that have different development stages, e.g. test, development and production. ## Looking Ahead At FlowFuse, our mission is to empower bottom-up innovation and enable organizations to transform their workflows into business-critical applications with unprecedented efficiency. As we move forward, we are excited to invite our users to actively engage with our future developments. Our [Roadmap](https://flowfuse.com/changelog/) lays out the advancements we're targeting, offering a glimpse into the features and enhancements that are on the horizon. We also encourage our users to stay informed and involved by checking out our latest updates in our detailed [changelog](https://flowfuse.com/changelog/). Your insights and feedback are crucial to us; they fuel our commitment to continuous improvement and innovation. We warmly invite you to [share your thoughts and suggestions](https://flowfuse.com/contact-us/), as your input is a vital part of our journey in shaping the next steps for FlowFuse. ## How to get started You can install FlowFuse yourself via a variety of install options. You can find out more details [here](https://flowfuse.com/docs/install/introduction/). If you'd rather use our hosted offering: [Get started for free](https://app.flowfuse.com/account/create){rel=""nofollow""} on FlowFuse Cloud. ## Upgrading FlowFuse \[FlowFuse Cloud]\({{ site.appURL }}) is already running version 2.0. If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for [upgrading your FlowFuse instance](https://flowfuse.com/docs/upgrade/). The version 2.0 release of the FlowFuse Helm Chart includes a breaking change for deployments making use of the `forge.localPostgresql` setting when upgrading. This is where the helm chart installs a dedicated PostgreSQL database instance. With version 2.0 we have updated the version of the Bitnami PostgreSQL Helm sub-chart we bundle and the upgrade process will require some manual intervention to ensure things work correctly. A fresh install should not require any extra steps. The steps are documented on the [Upgrade instructions](https://flowfuse.com/docs/install/kubernetes/#upgrade) page, please read them carefully before upgrading ## Getting help Please check FlowFuse's [documentation](https://flowfuse.com/docs/) as the answers to many questions are covered there. Additionally you can go to the [community forum](https://discourse.nodered.org/c/vendors/flowfuse/24){rel=""nofollow""} if you have any feedback or feature requests. # Step-by-Step Guide to Deploying Node-RED with FlowFuse in balenaCloud In a [recent webinar with balena](https://flowfuse.com/webinars/2024/balena/), we explored the dynamic capabilities of deploying FlowFuse to a fleet of devices using [balenaCloud](https://www.balena.io/cloud){rel=""nofollow""}. This blog post serves as a practical guide to replicate that process, specifically tailored for those aiming to streamline their deployment of FlowFuse in an efficient and user-friendly manner. ## How to Implement FlowFuse with balenaCloud on a Fleet of devices [![Deploying Node-RED with FlowFuse in balenaCloud](https://i.ytimg.com/vi/cKFu1ljUlKE/hqdefault.jpg)](https://www.youtube.com/watch?v=cKFu1ljUlKE "Deploying Node-RED with FlowFuse in balenaCloud"){rel=""nofollow""} ### Preparation Steps Before diving into the deployment process, it's crucial to familiarize yourself with key resources. We recommend reviewing our previous [blog post](https://flowfuse.com/blog/2023/11/device-agent-balena/) on deploying the FlowFuse Device Agent via balena. This post contains a vital link to the GitHub repository, essential for deploying FlowFuse with balena, laying the groundwork for the steps ahead. ### Creating a New Fleet in balenaCloud 1. Navigate to the [FlowFuse git](https://github.com/FlowFuse/balena-device-agent){rel=""nofollow""} repository. Click on the **Deploy with balena** button. 2. Name your fleet. 3. Select your default device. 4. Click **Create and Deploy**. ### Adding Devices to the Fleet Once your fleet is created, the next step is to add devices. To add a device to your fleet, follow these [instructions](https://docs.balena.io/learn/getting-started/var-som-mx6/rust/#add-a-device-and-download-os){rel=""nofollow""}. ### Setting Up FlowFuse Setting up FlowFuse correctly is essential for seamless operation: 1. Create a new instance within FlowFuse or use an existing one if you prefer. Follow these [instructions](https://flowfuse.com/docs/user/introduction/#creating-a-node-red-instance) to create a new instance. 2. Create a **Device Provisioning Token** by following these [instructions](https://flowfuse.com/docs/device-agent/register/#bulk-registration). 3. Ensure you add the FlowFuse Node-RED application you want the devices to provision. If left at default, devices will need to be manually added to applications. ### Using the Device Provisioning Token 1. First, convert the contents of the Device Provisioning Token to base64. Follow these [instructions](https://flowfuse.com/blog/2023/11/device-agent-balena/#environment-variable) to convert the file to base64. 2. Once converted, import this string into balena as a **Fleet** level variable, not a device level variable. Follow these [instructions](https://docs.balena.io/learn/manage/variables/#fleet-wide-variables){rel=""nofollow""} to import the Fleet level variable with the Name `FF_DEVICE_YML`. 3. This action will provision any new device added to the fleet with the yaml file configuration, automatically adding the device to a FlowFuse instance. ### Deploying and Testing the FlowFuse Instance Deploying the FlowFuse instance brings everything together: 1. Navigate to your FlowFuse application created earlier. 2. Go to your devices and you should now see your newly provisioned devices from balena. 3. If this is your first time setting up your fleet, the device will not have a snapshot. You will need to deploy a snapshot. Follow these [instructions](https://flowfuse.com/docs/user/snapshots/#create-a-snapshot) to do so. Ensure that you select **Set Target Snapshot**. 4. Once complete, the FlowFuse instance will deploy to your device(s). ## Integrating InfluxDB (Optional) Integrating InfluxDB enables effective data storage and management: 1. Similar to the previous steps, navigate to this [Github repository](https://github.com/mpous/flowfuse-agent-influx-balena/tree/main?tab=readme-ov-file){rel=""nofollow""} and click **Deploy with balena**. 2. This time, instead of creating a new fleet, select **Use an existing fleet instead**. 3. Choose your fleet for deployment and select **Deploy to fleet**. ### Data Generation and Management For testing, we have created a flow to get you started. Follow this [link](https://flows.nodered.org/flow/66f37bb739b6cdb0c7ad3a4e2edd68ef){rel=""nofollow""} 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](https://flowfuse.com/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](https://flowfuse.com/docs/user/snapshots/#create-a-snapshot). 3. Ensure that you **Set Target Snapshot**. ### Finalizing and Testing the Setup The final steps ensure that your setup is fully operational: 1. Once deployed, navigate to the device. 2. [Enable Developer Mode](https://flowfuse.com/docs/device-agent/deploy/#editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application). 3. Next, click the newly revealed button, **Open Editor**, to access the deployed Flow. ### Conclusion Implementing FlowFuse with balenaCloud significantly enhances your device management and data processing capabilities. This guide provides a foundational approach, but don't hesitate to delve deeper into each step to tailor the setup to your specific needs. # Import a File into Node-RED with Dashboard 2.0 Need to get a file into Node-RED, but don't want to over complicate things. This article outlines how you can leverage Dashboard 2.0 to import a file directly into Node-RED via a Dashboard. ## Why would you need to import a file to Node-RED? Often times it is necessary to update lookup tables in a SQL database, but you don't necessarily want to give access to everyone to edit the database, nor do you want to have to do it all yourself. This can often be seen when new products are introduced into a manufacturing facility. It may not be often, but enough that it warrants its own application. This process will guide you in a way that will enable your teammates to upload the files to the system themselves. Furthermore, on the management layer of most companies, Excel and Google Sheets are the go-to tools to perform data collection tasks. Getting management involved in processes might require you to build an import feature for them. Asking your manager to "Save as" CSV is much easier than teaching them SQL! ### Node-RED Dashboard (FlowFuse) ![csv dashboard](https://flowfuse.com/blog/2024/01/images/csv-dashboard.png) This simple flow allows the user to visualize data from a CSV in the Node-RED Dashboard. The button then allows the user to initiate a request to send the data to the next step. This next step could be anything from loading into a SQL database to saving it. ### Instructions 1. Install Node-RED Dashboard 2.0. Follow these [instructions](https://flowfuse.com/blog/2024/03/dashboard-getting-started/) to install. 2. Import Flow - to import the flow into your Node-RED instance follow these [instructions](https://flowfuse.com/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. :iframe{allow="clipboard-read; clipboard-write" height="225px" src="https://flows.nodered.org/flow/8c505039ac1b8dbed2bee1e22ee2975a/share?height=100" style="border: none;" width="100%"} ## Need to Send a File to Node-RED from another application or source? Check out this [blog](https://flowfuse.com/blog/2024/01/send-a-file) on how to send a file from either a stand alone web application or use the sample python script to imbed it into your current application. # AI and ChatGPT - Revolutionizing the Manufacturing Industry The application of artificial intelligence (AI) in various industries, particularly in manufacturing, is a topic of growing interest. The evolution of technologies like ChatGPT is driving significant changes in this sector. For a more nuanced understanding, we reference four informative blog posts from our team members. The first post, ["AI Use Cases that are shaping the next manufacturing frontier"](https://flowfuse.com/blog/2023/12/ai-use-cases/), offers an insightful overview of AI's role in diverse areas. Following this, ["ChatGPT AI Assistants with Node-RED"](https://flowfuse.com/blog/2023/11/ai-assistant/) examines the specific impact of AI assistants. The third article, ["Node-RED Builder a ChatGPT GPT"](https://flowfuse.com/blog/2023/11/chatgpt-gpt/), discusses the capabilities of generative pre-trained transformers like ChatGPT. Lastly, ["How ChatGPT improves Node-RED Developer Experience"](https://flowfuse.com/blog/2023/09/chatgpt-for-node-red-developers/) explores ChatGPT's application in Node-RED development, an important aspect for many in manufacturing. ## How AI and ChatGPT are Impacting Manufacturing AI and ChatGPT's integration into manufacturing indicates a shift in production process management. Here are some key impacts: ### Boosting Efficiency and Productivity AI, especially ChatGPT, enhances manufacturing efficiency by analyzing data to improve production lines, predict maintenance, and aid in design and development. ### Automating Routine Tasks AI is adept at handling repetitive tasks, speeding up manufacturing and allowing human workers to engage in more complex production aspects. ### Elevating Quality Control AI algorithms consistently ensure high-quality standards, quickly identifying and fixing product defects or deviations. ### Enabling Customization and Flexibility AI's learning and adaptability make it suitable for customizing production, allowing manufacturers to meet specific customer demands more efficiently. ### Transforming the Workforce AI in manufacturing necessitates skilled workers to operate and maintain these systems, altering job roles and responsibilities. ## Pros and Cons of AI and ChatGPT in Manufacturing ### Pros 1. Boosted Productivity: AI-driven automation increases production efficiency. 2. Consistent Quality Assurance: AI ensures ongoing product quality. 3. Reduced Costs: AI optimizes resource use and minimizes waste. 4. Encouraging Innovation: AI facilitates new manufacturing methods and products. 5. Enhancing Safety: AI reduces human exposure to hazardous manufacturing conditions. ### Cons 1. Initial Investment Costs: AI technology implementation can be expensive. 2. Need for Skilled Labor: Demand for workers proficient in AI technologies is growing. 3. Job Role Changes: Automation might decrease the need for certain labor roles. 4. Security Concerns: AI systems can be susceptible to cyber threats. 5. Technological Dependence: Excessive reliance on AI could limit problem-solving abilities in workers. ## Frequently Asked Questions (FAQs) 1\. How is AI changing manufacturing? :br**AI is altering manufacturing through automation, optimizing efficiency, and fostering production innovations.** 2\. What is ChatGPT's role in manufacturing? :br**ChatGPT aids in data analysis, automates processes, and improves communication and documentation in manufacturing.** 3\. Are manufacturing jobs at risk due to AI? :br**While AI may automate some repetitive jobs, it also creates opportunities for skilled labor in technology management and development.** 4\. Can AI enhance manufacturing product quality? :br**Yes, AI's continuous monitoring and analysis significantly boost quality control.** 5\. What are the main challenges of integrating AI in manufacturing? :br**Challenges include high implementation costs, the need for skilled labor, and transitioning to automated processes.** 6\. Is AI cost-effective in manufacturing? :br**Despite high initial costs, AI can lead to long-term savings through improved efficiency and waste reduction.** 7\. How does AI affect manufacturing worker safety? :br**AI reduces risk by taking over hazardous tasks, improving overall workplace safety.** 8\. Can small manufacturers benefit from AI? :br**Yes, AI solutions are increasingly accessible for small-scale manufacturers.** 9\. What training is required for AI-enabled manufacturing workers? :br**Training in AI system operation, data analysis, and potentially programming skills is needed.** 10\. What does the future hold for AI in manufacturing? :br**The future suggests more integrated, intelligent, and adaptable manufacturing processes driven by AI advancements.** # Send a File to Node-RED Have you ever needed to send a CSV file to your Node-RED instance? This file can go on to populate a shift schedule, product specifications, or some other configuration file that is used. In this guide, we provide a couple of options to upload the data to your Node-RED for further processing and to organize the data to be sent on or used. ## Why would you need to send a file to Node-RED? Often times it is necessary to update lookup tables in a SQL database, but you don't necessarily want to give access to everyone to edit the database, nor do you want to have to do it all yourself. This can often be seen when new products are introduced into a manufacturing facility. It may not be often, but enough that it warrants its own application. This process will guide you in a way that will enable your teammates to upload the files to the system themselves. Furthermore, on the management layer of most companies, Excel and Google Sheets are the go-to tools to perform data collection tasks. Getting management involved in processes might require you to build an import feature for them. Asking your manager to "Save as" CSV is much easier than teaching them SQL! ## 2 Ways to send a file to Node-RED There are many approaches that can be taken when solving this. We are going to go over 2 here. 1. [Simple Python Script](https://flowfuse.com/#simple-python-script) - Simple script that will be shared below. It is a simple Python application that allows the user to send a file with a simple command, but this might require a little more technical skills that the end user may not feel comfortable with. 2. [Stand Alone Web Application](https://flowfuse.com/#stand-alone-web-application) - A web-based application that allows the user to upload files to a browser with a selectable endpoint. ### Simple Python Script This simple Python script sends a file to a Node-RED flow. The flow that will work with this script can be seen [here](https://flowfuse.com/#node-red-ingress). The script requires **requests** and **Python 3.x**. Install requests: ```bash pip install requests ``` Create a file called run.py and paste the contents into the file. ```python import requests def send_file(nodered_url, file_path): # Open the file in binary mode with open(file_path, 'rb') as file: files = {'file': (file.name, file, 'multipart/form-data')} response = requests.post(nodered_url, files=files) return response # Update the ip address and port of your Node-RED instance nodered_url = 'http://localhost:1880/fileupload' # Update the location of your file file_path = 'C:/Users/myUser/Downloads/shiftSchedule.csv' response = send_file(nodered_url, file_path) print(f"Response Status Code: {response.status_code}") print(f"Response Body: {response.text}") ``` Update the **nodered\_url** to the location of the Node-RED instance. Be sure to adjust the port if the default port of 1880 isn't being used. Update the **file\_path** with the path to where the file to be uploaded will be located. **Save** To run: ```python python run.py ``` ### Stand Alone Web Application ![csv upload application](https://flowfuse.com/blog/2024/01/images/csv_upload_app.png) This stand-alone web application can be run on either Windows or Linux, .bat for Windows, and .sh for Linux. #### Installation Clone the repository and navigate to the directory: ```bash git clone https://github.com/gdziuba/FF_Send-File-to-NR.git && cd FF_Send-File-to-NR ``` #### Configuration Edit the lines in the body of [index.html](https://github.com/gdziuba/FF_Send-File-to-NR/blob/21214f88c6c4536f49efb88cf5f84bf52071a88b/templates/index.html#L69){rel=""nofollow""} to include the endpoints to which you would like to send the files. ```text ``` ### Operating Systems #### Windows Run the script: ```bash .\start_app.bat ``` This will install if necessary, start the Flask Application, and take you to localhost:5000 on the browser. #### Linux Make the script executable by running running: ```bash chmod +x setup_and_run.sh ``` Then run the application with: ```bash ./setup_and_run.sh ``` To access the application, open a browser to the **\:5000** of the running application. #### Node-RED Ingress :iframe{allow="clipboard-read; clipboard-write" height="225px" src="https://flows.nodered.org/flow/effb53752e5d6f767b3c7e5d41a4a6e8/share?height=100" style="border: none;" width="100%"} Once we have a file ready to be sent, we now need to configure the receiving side in Node-RED. In this example, we are leveraging a CSV formatted file and then converting it to be used at a later time. A link to the flow can be found [here](https://flows.nodered.org/flow/effb53752e5d6f767b3c7e5d41a4a6e8){rel=""nofollow""}. To import the flow, follow these [instructions](https://flowfuse.com/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. ## Wanna import it directly into your Node-RED instance via a Dashboard? Check out this [blog](https://flowfuse.com/blog/2024/01/import-a-file) on how to directly import a file into a Node-RED instance via Dashboard 2.0. # Sentiment Analysis with Node-RED Have you ever built a sentiment analysis system to extract insights from text content? If yes then I don’t think you'll need an explanation of how complex it is to build. In this guide, we will build a sentiment analysis system with Node-RED using Dashboard 2.0 in a few easy steps. ## What exactly is sentiment analysis? Sentiment analysis is a context-mining technique used to understand emotions and opinions expressed in text, often classifying them as positive, neutral, or negative. There are many real-world applications of this technique. - **Analysing Feedback:** Customers, or other stakeholders like employees, are periodically requested to fill out a feedback form. Analysis of such feedback is the most widespread application of sentiment analysis. - **Campaign Monitoring:** Another use case of sentiment analysis is a measure of influence which is crucial in any marketing campaign. - **Brand Monitoring:** Brand monitoring is another great use case for sentiment analysis. Companies can use sentiment analysis to check the social media sentiments around their brand from their audience. ## Building a Form in Dashboard 2.0 In this system, we will analyse the sentiment of text content obtained from the user. For this we are going to build a user interface using Dashboard 2.0 and Node-RED. 1. Install Node-RED Dashboard 2.0. Follow these [instructions](https://flowfuse.com/blog/2024/03/dashboard-getting-started/) to install. 2. Drag a ui form widget to the canvas and select the created group. 3. Add an element in the form widget and give it a name and label, select the type as multiline, and set the number of rows according to your need. !["Taking user input for Sentiment analysis using form"](https://flowfuse.com/blog/2024/01/images/sentiment-analysis-form.png "Taking user input for Sentiment analysis using form") ## Normalizing the data We need to normalize the payload before sending it to the next node because the form widget always returns an object containing the property of values of form elements. 1. Drag a change node to canvas. 2. Set `msg.payload.$FORM_ELEMENT_NAME` to `msg.payload`, replace the `$FORM_ELEMENT_NAME` with the name of the form element that you have added to the form to obtain user input. 3. Connect the UI form nodes output to the change node’s input. !["Normalizing the payload using change node"](https://flowfuse.com/blog/2024/01/images/sentiment-anlaysis-change-node\(1\).png "Normalizing the payload using change node") ## Installing custom node Now it’s time to install a custom node that can perform sentiment analysis for us. In this guide, we will use the `node-red-node-sentiment` which is a Node-RED node that uses the AFINN-165 wordlists for sentiment analysis of words. It returns a sentiment object containing a score and other properties but we will only use the score property. Score property typically ranges from -5 to 5. 1. Install the `node-red-node-sentiment` package by the Node-RED palette manager. 2. Drag a sentiment node to canvas. 3. Connect the change nodes output to sentiment node input. ## Calculating percentage Why do we need to calculate the percentage? We will show the final result with the help of a circular progress bar and three different emojis. Ideally we should show the progress bar based on a percentage of score instead of negative values. 1. Drag another change node to canvas. 2. set `msg.payload` to `((msg.sentiment.score - (-5)) / (5 - (-5))) * 100` as a JSONata expression, it will calculate the percentage of the score. !["Calculating the percentage based on the score using the change node"](https://flowfuse.com/blog/2024/01/images/sentiment-analysis-change-node\(2\).png "Calculating the percentage based on the score using the change node") ## Displaying result on Dashboard 2.0 Finally, we are going to display the result on Dashboard 2.0 with the help of the Vuetify circular progress bar and emojis. To do that we will build a Vue component by using our ui template widget. 1. Drag a ui template widget to canvas and create another group for it. 2. Paste the below Vue component snippet into the template widget. We're aware that not everyone coming into Dashboard 2.0 will be familiar with VueJS. We have a more detailed guide [here](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#building-full-vue-components){rel=""nofollow""}, but we'll also give a quick overview of the component that we'll use to display the result: ```html ``` - v-progress-circular is a Vuetify component to display a circular progress bar, for a detailed guide refer to our blog on [Custom Vuetify components for Dashboard 2.0](https://flowfuse.com/blog/2023/10/custom-vuetify-components-dashboard/). - `rotate` is an attribute that lets you specify the rotation angle of the progress bar. - `size` and `width` allow you to set the size of the circular progress bar, and another `width` attribute allows you to set the stroke width of the circular progress bar. - v-if, v-else-if, and v-else, allow dynamic rendering of elements based on specified conditions, in this component we are rendering emojis based on percentages calculated by score. Your final flow should look like this: !["Node-RED flow to do sentiment analysis"](https://flowfuse.com/blog/2024/01/images/sentiment-anlaysis-flow.png "Node-RED flow to do sentiment analysis") ## Deploying the Flow !["Deploying Sentiment analysis Node-RED flow"](https://flowfuse.com/blog/2024/01/images/sentiement-analysis-flowfuse-editor.png "Deploying Sentiment analysis Node-RED flow") Finally, we have successfully built our sentiment analysis system. Now it's time to deploy the flow, to do that click on the red deploy button which you can find in the top right corner. After that go to `https://.flowfuse.cloud/dashboard` :video{ariaLabel="Sentiment analysis on Text using Node-RED Dashboard 2.0" autoPlay="true" height="306" loop="true" muted="true" playsInline="true" preload="none" width="600"} ## Conclusion In this post, a sentiment analysis system is built with Node-RED in which the user has a form field to paste text content. After submitting the form, it calculates the percentage based on the output score, which ranges from -5 to 5. The output will be displayed on dashboard 2.0 by a circular progress bar and three different emojis based on percentage. # FlowFuse is now SOC 2 Type 1 Compliant FlowFuse achieved SOC 2 type 1 compliance! SOC 2, governed by the American Institute of Certified Public Accountants (AICPA), is a crucial framework for organizations handling customer data. An independent audit assessed that FlowFuse's controls are effectively designed and operationally applied. Achieving SOC 2 Type 1 compliance validates our practises as an business and provides our customers assurances we apply the highest standards to ensure their data is protected. ## Improving Our Security Posture At FlowFuse, we understand that professionalizing Node-RED deployments for our clients means adhering to the highest standards, including SOC 2 requirements. This commitment is at the core of our security philosophy. In a world rife with cybersecurity threats and data breaches, taking information security seriously isn't just an option, it's a critical necessity. Our SOC 2 audit was far more than just a procedural step. It represented a comprehensive, independent third-party validation of our robust controls and processes. We believe in transparency and accountability, which is why we document our policies in our open handbook, inviting scrutiny from vendors and reinforcing trust with our customers. Providing this level of independent audit not only serves our customers better and more efficiently but also offers FlowFuse valuable insights into enhancing our security measures and identifying any gaps in our policies. This proactive approach ensures we continue keeping your data safe and secure at all times. As we continue to grow and evolve, ensuring the security of our systems and data becomes ever more critical. The next step on FlowFuse's journey to provide independant proof we're on the right track: We're currently in the observation phase of the SOC2 type 2. SOC 2 Type 1 assesses the design of an organization's security controls at a specific point in time, while SOC 2 Type 2 evaluates the effectiveness of those controls over a period of time, typically three to twelve months. ## FlowFuse's Journey to SOC 2 Compliance ### Compliance Partners The independent audit was performed by Advantage Partners. Their expertise played a large role in our successful attainment of this certification. Before the audit was performed the company went through an extensive process to uncover what policies were missing, required updating, or were already in place. Further, lots of tribal knowledge has been written down and is now enforced by internal policies. For example 1. [Data Management Policy](https://flowfuse.com/handbook/company/security/data-management/#data-management-policy) 2. [Access Control Policy](https://flowfuse.com/handbook/company/security/access-control/#access-control-policy) 3. [Incident Response Policy](https://flowfuse.com/handbook/company/security/incident-response/#incident-response-plan) 4. [Human Resources Security Policy](https://flowfuse.com/handbook/company/security/human-resources/#human-resources-security-policy) It's been a team effort from engineering to updated HR polices! # Speech-Driven Chatbot System with Node-RED Have you ever wanted to integrate speech recognition and synthesis into your Node-RED project and thought it was too complex? Often it has required external services or APIs. However, in this guide, we show you how you can use speech recognition and synthesis in your Node-RED projects without needing an external service or API. In addition, we make things more interesting by building a system that can listen to us and respond like humans using the Chat-GPT API. Let's get started! ## What exactly is speech recognition and synthesis? Speech recognition is a technology where a device captures spoken words through a microphone, checks against grammar rules and vocabulary, and returns recognized words as text. On the other hand, speech synthesis converts app text into speech and plays it through a device's speaker or audio output. There are many benefits and real-world applications of this technology. - **Hands-Free Operation:** Using speech recognition technology is often used today to perform tasks such as making calls, sending messages, or controlling smart home devices without the need for physical interaction. - **Accessibility:** It allows individuals with visual impairments to access digital content through spoken words and as discussed above, to control devices without physical interaction. - **Efficient Content Consumption:** It allows us to listen to information instead of reading. For example, in the audiobook industry by using speech synthesis technology they create audio versions of books which helps users to be more productive. ## Installing Dashboard 2.0 Install Dashboard 2.0. Follow these [instructions](https://flowfuse.com/blog/2024/03/dashboard-getting-started/) to get up and running. ## Building Speech-to-Text Vue component In this section, we will build a Vue component that will perform a speech-to-text conversion operation using Web speech API, and display results on the dashboard. While we did say previously that we won't need any external API for speech recognition, this [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API){rel=""nofollow""} is not an external API. This will process your speech locally as it is a JavaScript API that allows us to use speech-related functionalities, such as speech recognition and synthesis, in a web browser directly. It is widely present in modern browsers (except Firefox) which eliminates the need for external APIs to implement these features. Let's now start to build that component. 1. Drag a ui template widget to canvas and select the created group. 2. Paste the below Vue snippets into the template widget step by step. If you are unfamiliar with Vue, we have added comments that will help you to understand the code better. We are going to start by pasting a user interface’s snippet which will allow us to interact with our system. This snippet adds a button that triggers our system to listen, an Icon, and a paragraph to display speech recognition results on the dashboard. ```html ``` Now paste the below script right after the HTML in the template widget, This script adds functionality of speech recognition in our system. ```html ``` ## Adding an Environment variable Why do we need to add an environment variable? In this guide, we will build a speech-driven chatbot that involves integrating the Chat-GPT AI model. For this we need openAi’s API key. An API key is a form of private data that needs to be protected from being exposed. That is why we need the environment variables. It provides a secure way to store and access the API key without revealing it directly in the flow. For more details see [Using Environment Variables in Node-RED](https://flowfuse.com/blog/2023/01/environment-variables-in-node-red/) 1. Navigate to the instance's setting and then go to the environment section. 2. Click on the `add variable` button and add a variable for Chat-gpt API. !["Setting environment variable for Chat-gpt token"](https://flowfuse.com/blog/2024/01/images/speech-driven-chatbot-environment-section.png "Setting environment variable for chat-gpt token") ## Setting msg property Now let’s set that added environment variables as msg's property. 1. Add a change node to canvas. 2. Set environment variable to ms.token property. 3. Connect the change node’s input to the template widget’s output. !["Setting msg's property for Chat-gpt token"](https://flowfuse.com/blog/2024/01/images/speech-driven-chatbot-change-node.png "Setting msg's property for Chat-gpt token") ## Installing and configuring custom node In this section, we will install a custom node that will allow us to interact with the Chat-gpt AI model. 1. Install `@sumit_shinde_84/node-red-contrib-node-gpt` by pallet manager, you can use other nodes according to your preference. 2. Drag a ChatGPT node to canvas. 3. Connect the ChatGPT node’s input to the change node’s output. ## Building Text-to-Speech Vue component We will build a Vue component that converts text received from ChatGPT into speech. 1. Drag another template widget to canvas and select the added group, alternatively, you can create a separate group for this component according to your preference. 2. Paste the below Vue snippets into the template widget. 3. Connect the template widget’s input to the ChatGPT node’s output. Paste the below snippet in the template widget which displays chat-gpt response on the dashboard ```html ``` Paste the below snippet right after the HTML, This snippet adds the functionality of text-to-speech into our system, which triggers when msg received by the previous node. ```html ``` Your final flow should look like this: !["Speech Driven Chatbot system flow"](https://flowfuse.com/blog/2024/01/images/speech-driven-chatbot-flow.png "Speech Driven Chatbot system flow") ## Deploying the Flow !["Deploying Sentiment analysis Node-RED flow"](https://flowfuse.com/blog/2024/01/images/speech-driven-chatbot-flowfue-editor.png "Deploying Sentiment analysis Node-RED flow") We have successfully built our Speech-Driven Chatbot System. Now it's time to deploy the flow, to do that click on the red deploy button which you can find in the top right corner. After that go to `https://.flowfuse.cloud/dashboard` :video{ariaLabel="Speech Driven Chatbot using Node-RED Dashboard 2.0" autoPlay="true" height="400" loop="true" muted="true" playsInline="true" preload="none" width="648"} ## Conclusion In this guide, we have built a Speech-Driven Chatbot System which allows us to understand how we can add speech recognition and synthesis features into our project without any external API or custom node. It also provides a brief overview of how we can integrate chat-gpt into our system. # Selecting a broker for your Unified Namespace When starting to roll out a new data distribution architecture for the unified namespace (UNS), one of the first questions you'll ask is, "What broker should I select for my UNS? The broker must implement a publish-subscribe (pub-sub) pattern, though that leaves plenty of options. ## Technology selection ### Two protocols frontrunners Currently, there are two protocols that are front runners for becoming the de facto data transfer choice in (industrial) IoT: [MQTT](https://flowfuse.com/blog/2024/06/how-to-use-mqtt-in-node-red/) or [Kafka](https://flowfuse.com/blog/2024/03/using-kafka-with-node-red/). They’ve been designed for different use cases and have different properties. At this time, MQTT is more often deployed as a broker in the unified namespace and is generally the best choice when starting to implement a unified namespace, it also features better support from hardware vendors. First and foremost, MQTT has been designed to enable IoT use cases. The main design objectives were to be lightweight to enable low-bandwidth communication, enable low-power devices, and handle unreliable networks. MQTT enables a large number of data producers and consumers to collaborate. Kafka is designed as an event streaming platform. Its initial adoption was mostly for data brokers between microservices all part of the same web backend for large sites like LinkedIn. When communicating data between servers or just a few data centers around the world, there’s less of a concern around the reliability of the connection or to enable constrained devices to participate in the shift towards a unified namespace. Generally, MQTT is more often seen deployed in practice as data distribution architecture. ### The Cloud route Cloud message queue brokers like AWS Kinesis and GCP Pub/Sub offer a high level of convenience. Scaling the infrastructure for real-time data processing and communication is their concern, the customer is mostly concerned about paying the bill. These brokers are fully managed, meaning they are maintained and updated by the cloud provider, reducing the burden on developers. However, this convenience comes with the tradeoff of vendor lock-in. When selecting these brokers, the cloud vendor has usually adapted their technology to support many protocols, and these offerings are usually jack-of-all-trades solutions – master of none. It creates a situation where the unified namespace implementation will change in subtle ways to accommodate the vendor instead of the other way around. An organization might become so reliant on a particular vendor's products or services that they are unable to easily switch to another vendor or protocol that serves their business objectives better. The cost of changing is exacerbated by having to train personnel on new and open-source protocols. In addition to vendor lock-in, cloud message queue brokers also introduce reliance on the network to the cloud providers. Network reliability for (industrial) IoT is a major concern due to the physically distributed nature, adding external dependencies creates more variability. ### 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](https://flowfuse.com/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](https://flowfuse.com/blog/2024/06/how-to-use-mqtt-in-node-red/), [Kafka](https://flowfuse.com/blog/2024/03/using-kafka-with-node-red/), and [AMQP](https://flowfuse.com/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. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} now for a free trial and experience how FlowFuse can streamline your Node-RED deployments and management.** ## Conclusion An MQTT broker is currently recommended as a broker solution for your unified namespace. There are many different implementations of the protocol available. At FlowFuse, we’re using [Mosquitto](https://mosquitto.org/){rel=""nofollow""}, due to its efficiency on resources and flexible authentication layer. Further, our customers are reporting to be happy with [EMqX](https://www.emqx.io/){rel=""nofollow""}, which is written in Erlang – itself a messaging-oriented programming language – and has been put through its paces in practice. If you’re dipping your toes into the unified namespace, either of those or another MQTT broker is currently recommended. Note that it’s recommended to allow yourself flexibility in the broker, and treat it as a message-passing system, and your organization will be able to easily swap it out later if any other broker is a better fit later on. # Unified Namespace: When to Use It, and When to Choose Something Else At FlowFuse, we're convinced of the Unified Namespace (UNS) architecture for IoT cases. It's a powerful tool that can make information much more readily available and easy to consume. However, as with any architecture, there are times when it's not the best choice. In this blog post, we'll discuss when to use the UNS and when to consider other options. ### Latency sensitivity When automating strictly digital tasks, there's generally no or very lenient requirements on latency. Requesting APIs from another server, normalizing data, and sending it towards another service takes very little time, though it hardly matters if you’re 100ms later than usual. In industrial automation or other cases where physical safety is safeguarded, there’s a requirement for low latency to ensure that data is transmitted and processed quickly enough to maintain real-time control of processes. Extending this beyond the UNS – For controlled use cases, use specialized software and do not rely on third parties to broker the information correctly. However, while control was used as an example here, the general case here is not the UNS for real-time, low-latency communication. ### Large files or binary data Sending large blobs of binary data, such as pictures and archives, through UNS is not recommended due to several drawbacks. First, UNS is optimized for small, mostly text-based communication, and sending binary data through it can significantly increase message size and processing overhead. As the sender of the data, there’s also generally no control over the number of receivers of the data, so if you send a large file to the broker once, it might need to be copied many times for every receiver. To overcome these limitations, it is more efficient and practical to store binary data elsewhere, such as on a shared storage location or a cloud service. Once the data is stored externally, a reference to its location can be sent through UNS. ### Data security and data access The UNS becomes more useful the more hubs are connected, and it stands to reason that better business outcomes are achieved when everything is connected. Not just the sensor data (Level 1 of the [automation pyramid](https://flowfuse.com/blog/2023/08/isa-95-automation-pyramid-to-unified-namespace/#automation-pyramid-visualization) *except actuators*), but also the customer-facing systems like your ERP (Level 4 or the automation pyramid), and even your CRM, Customer Relationship Management. This would allow better insight and communication with the customer when, for example, their car is done with production and will be shipped to them. The CRM contains personal details about the customer. Publishing what a customer ordered might thus disclose personally identifiable information (PII). Connecting a CRM is certainly possible, and could streamline the supply chain, but PII shouldn’t be published. Consider if there’s a unique ID for the customer instead to use. Does your CRM, for example, keep a customer ID? If not, mask the information instead. Use strong hashing algorithms like SHA-512 to hash their email addresses. This way, other systems can cross reference messages received in the UNS, without ever knowing the customer PII. Another venue to explore is topic-based authentication for data receivers, which most data brokers provide. However, stripping and masking PII is still highly recommended either way. ### In Conclusion The Unified Namespace is a powerful architecture that can make IoT data more accessible and easier to consume. However, it's not always the best choice for all applications. When considering whether to use the UNS, be sure to weigh the benefits against the potential risks. If you need low latency, strong data security, or fine-grained control over data access, you may need to adjust your architecture pattern or instantiate point-to-point connections through REST or other interfaces. # Connect Node-RED to KepserverEX OPC server. KepserverEX, often referred to as Kepware, is an OPC server that has been the important tool many manufacturing companies have used on their digital transformation journey. It plays an important role for many to extract data from PLCs, Programmable Logic Controllers, without having to directly interact with them. ## PTC's KepserverEX PTC's [KEPServerEX](https://www.ptc.com/en/products/kepware/kepserverex-ppc){rel=""nofollow""} is a versatile connectivity platform designed to securely manage, monitor, and control diverse automation devices and software applications. Central to its functionality is the OPC standard, which enables universal communication across industrial hardware and software, facilitating data exchange. This makes KEPServerEX particularly valuable in a variety of use cases, such as real-time data monitoring, machine-to-machine (M2M) communication, and industrial Internet of Things (IIoT) applications. It serves as a critical bridge in the automation and controls engineering space, offering a robust solution for integrating disparate systems, thereby enhancing operational efficiency and enabling data-driven decision-making. Integrating KEPServerEX with Node-RED extends this functionality, by allowing bidirectional communication for sending, storing, and or manipulating data. ### Scope The goal of this blog is for a quick start guide on the configuration for collecting data from a KepserverEX OPC server. We are going to be leveraging the [node-red-contrib-opcua](https://flows.nodered.org/node/node-red-contrib-opcua){rel=""nofollow""} node. We will assume that you already have [KepserverEX](https://www.ptc.com/en/products/kepware/kepserverex-ppc){rel=""nofollow""} install and ready for the integration. We will be using Basic256Sha256 security in this guide with anonymous authentication. Assumptions of the installation include allowing Default configuration of the installation of KepserverEX 6.15 and allowing dynamic tag addressing. ### Configure Connection from Node-RED to Kepserver #### Step 1: KepserverEX The first thing we need to do is check our **OPC UA Configuration Manager** for the security requirements for our environment. In the tray at the bottom, click on the KepserverEX symbol and select **OPC UA Configuration** ![kepware tray](https://flowfuse.com/blog/2024/02/images/kepserverex-tray.png) If your Node-RED instance lives on the same server that your KepserverEX is on, pick accordingly or click add if you need to define by ip address. This is for setting different credential requirements for localhost vs remote host access. Also note, that if you have multiple network adapters, make sure to select the adapter that is in use. :cta-image{alt="Aperia Technologies stopped reprogramming controllers station by station with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/aperia-book-demo.png"} ![kepware endpoint definition](https://flowfuse.com/blog/2024/02/images/kep-endpoint-definition.png) We are testing locally on the server, so we will use the one selected for loopback addressing. We will be leaving the OPC server port as default and select **Basic256Sha256** with **Sign and Encrypt**. Click **OK**. #### Step 2: Node-RED Next, navigate to your Node-RED instance and install the [node-red-contrib-opcua](https://flows.nodered.org/node/node-red-contrib-opcua){rel=""nofollow""} node if you haven't already done so. Import the flow below into your Node-RED environment. :iframe{allow="clipboard-read; clipboard-write" height="225px" src="https://flows.nodered.org/flow/04a84fe5b0db7cda9e74ba811e7b0ca5/share?height=250" style="border: none;" width="100%"} Next, let's configure the **OPC UA Client**. Click the **pencil** to add a new OPCUA-Endpoint. ![kepware node-red encrypted opc ua node](https://flowfuse.com/blog/2024/02/images/opcua-endpoint-node-red-encrypted.png) For the endpoint, **copy** the endpoint definition from the KepserverEX OPC UA Configuration Manager. In our example, it is `opc.tcp://127.0.0.1:49320`, and paste it into the Endpoint. For SecurityPolicy select **Basic256Sha256**. For SecurityMode, select **Sign\&Encrypt**. Lastly, we will be selecting **Anonymous**. Click **Update**, then **Deploy**. Trigger the flow by **clicking** on the inject node. The server may not connect at this time, and it is expected. ![kepware node-red invalid endpoint](https://flowfuse.com/blog/2024/02/images/node-red-opc-ua-invalid-endpoint.png) #### Step 3: KepserverEX Moving back over to KepserverEX, Click on the tray again in the bottom of the screen and select **Configuration**, then select **Edit** from the file menu, then **Properties**. Next, Select **OPC UA** and ensure that **Allow Anonymous login** is set to **Yes**. Click **OK**. ![kepware node-red anonymous login](https://flowfuse.com/blog/2024/02/images/node-red-kepware-anonymous-login.png) Select the tray at the bottom of the screen again, and select **OPC UA Configuration**. Select the **Trusted Clients** tab. ![kepware node-red trusted client before](https://flowfuse.com/blog/2024/02/images/kepserverex-trusted-client-before.png) Now select the **NodeOPCUA-Client** and then **click** Trust. ***If you don't have the client option, trigger the inject node again from the Node-RED flow and check the logs*** ![kepware node-red trusted client after](https://flowfuse.com/blog/2024/02/images/kepserverex-trusted-client-after.png) #### Step 4: Node-RED Lastly, Navigate back to Node-RED and **trigger** the inject node. This node will now browse the project from the KepserverEX and display all of the existing tags. ![kepware node-red browsed tags](https://flowfuse.com/blog/2024/02/images/kepware-opc-browsed-tags-node-red.png) ### Read Tags We will be leveraging the default Simulated Examples for reading tags from KepserverEX. Let's move on to the next set of flows. ![kepware node-red read tags](https://flowfuse.com/blog/2024/02/images/node-red-kepware-read-tag.png) Edit the OPCUa-Item node and note the item. ```text ns=2;s=Simulation Examples.Functions.Ramp1 ``` Let's break down the syntax, ns stands for namespace that will coincide with the project. In this case, it is namespace 2. Once the namespace has been selected, we are use **Dynamic Addressing** to select the tag through the variable **s**, which stands for string type of NodeId. **Click** Done. Now let's **trigger** the Read inject node and view the debug output. The debug node is set to show a complete msg object. Note the payload as the value of the variable. ![kepware node-red read tags output](https://flowfuse.com/blog/2024/02/images/node-red-debug-output-opc.png) ### Write Tag Writing a tag is a similar process. The only difference is that a variable is set in the **OPCUa-Item** node and the **OPCUa-Client** action is set to Write. In this example, we created a new variable in KepserverEX under **Simulation Examples > Functions** called myInt of Date Type Long. ![kepware node-red write tags](https://flowfuse.com/blog/2024/02/images/node-red-kepware-write-tag.png) View the OPCUa-Item node and note the item. ```text ns=2;s=Simulation Examples.Functions.myInt ``` **Click** Done and **Deploy** Open up the **Quick Client** within KepserverEX and navigate to the address of **Simulation Examples.Functions** and look for myInt. It should by default, be 0. **Trigger** the inject node within Node-RED to see the Value change within the Quick Client. ![kepware node-red write tags output](https://flowfuse.com/blog/2024/02/images/node-red-debug-output-write-opc.png)![kepware node-red quick client](https://flowfuse.com/blog/2024/02/images/kepware-quick-client.png) ### Conclusion This guide was designed to help you easily connect your Node-RED instance to KepserverEX with security. For more examples of how to do more advanced configuration, please watch the past [webinar](https://flowfuse.com/webinars/2023/getting-started-opcua-node-red/) going over these [examples](https://github.com/mikakaraila/node-red-contrib-opcua/tree/master/examples){rel=""nofollow""} in detail. # History of Node-RED In January 2013, I could have never foreseen that my fun little proof-of-concept project would become Node-RED, an open source low-code environment with millions of deployments in IoT and automation. Long before IoT became the ubiquitous term it is today, I was working in IBM’s Emerging Technology Group playing around with capturing data from devices and doing interesting things with it. The team focused on very fast-paced, short, proof-of-concept projects and was afforded time to learn new skills, innovate and work on side projects. My background working with the MQTT protocol space before it was known outside of IBM led me to a side project: I wanted some way to visualize mapping messages on an MQTT infrastructure to see how they come in on one topic and get sent out on another. Using it as an excuse to also start playing the relatively new Node.js runtime, I spent a day or two putting together a little demo of an application that would connect to an MQTT broker and visualize the topic mappings in a web browser. Showing it to my colleague, Dave Conway-Jones, I mentioned it wouldn't take much to make it more interactive; to let you draw the mappings and apply them. He sent me on my way to do just that and, 24 hours later, I had a simple browser-based application that could define and apply mappings between MQTT topics. It very quickly became useful. ![](https://flowfuse.com/blog/2024/02/images/history-nr-screenshot.png) An early screenshot of Node-RED The projects Dave and I were working on became rich sources of requirements; each project needing to access data from some other source such as a device plugged into a serial port, or being able to modify the data with a bit of JavaScript code. I spent a few days redesigning the code to make it easier to write in new nodes, unlocking the ability to quickly add in the function node, change node, and switch node, which became the basic building blocks of the tool. The utility of the application was clear, and more colleagues started making use of it - all grounded in real client projects. But it was still a tool largely known only to our team. ### Going Open Source As we considered how best to move the project forward, we essentially had two choices; keep it to ourselves and see if we could get an IBM product group to back turning it into a fully-fledged product, or to go the open source route. In my mind, the OSS route was the natural fit and that is what we ultimately chose to do, getting it published in late 2013. I demoed Node-RED in late 2013 at a London IoT meetup and word spread among my peers in that community. A week later, I was at an open source hardware conference and attended a workshop on home automation. I was surprised to see Node-RED on everyone’s screens! The facilitator had seen Node-RED and reworked his workshop so that people didn’t have to worry about writing lines of code and were able to do useful things much more quickly. ![](https://flowfuse.com/blog/2024/02/images/history-ibm-lab.jpg) The IBM Emerging Technology Demo Lab - many of the demos were built on Node-RED ### Building an audience A key step forward was when IBM was preparing to launch its new IBM Cloud service. In the months before the launch, they were looking for innovative ideas that could help expand the offering. We put forward a proposal to use this tool we'd created as a way to visualise the mapping of web services within the cloud. This generated some great interest from a wider audience within the company, and whilst that concept didn't ultimately come to anything, we had gotten our project noticed. Over time, we started seeing Node-RED being picked up by more than just the OSS community. Companies started using it with their own hardware devices and online services. By this time, IBM Cloud had launched, and from our previous conversations with them, we got Node-RED included as one of the 'starter applications' in the catalogue that gave users a one-click option for getting Node-RED running in the cloud. ![](https://flowfuse.com/blog/2024/02/images/history-cloud-catalog.png) Node-RED in the original IBM Cloud catalog ### Moving to a Foundation As we saw the project grow, discussions were had around the longer-term future of the project. Some companies voiced a concern about it being a single-vendor open source project. This also came at a time when IBM was actively working with the Node.js project to help relaunch the Node Foundation (which has since become the OpenJS Foundation). This culminated in Node-RED joining the foundation as one of its founding projects, alongside other well-established projects such as Node.js itself, jQuery and many others. Having an independent governance structure around the project gave companies more confidence to get involved, knowing they had an equal voice in its development. Hitachi became big supporters of the project and had a team dedicated to working on it. For software developers, time spent writing boilerplate code is not time adding value to the application they’re building. With low-code, Node-RED abstracts all that boilerplate so they can focus on the business problem. Device manufacturers paid attention when Node-RED was installed on the Raspberry Pi image, with its low-code accessibility attracting a broad range of people from systems engineers building automations to IoT hobbyists. Now with millions of deployments, Node-RED continues to collect, transform, and integrate data through visualized dashboards. And, as this open source community grows it remains rooted in the two pillars of its low-code user experience, and its extensibility. # Node-RED: The perfect adapter and middleware for your UNS Digitalization is at the inflection point where it’s been adopted enough that the additional investments provided better and better ROIs for organizations. The next bump in ROI will be achieved through the UNS. A torrent of information is more useful when structured and adapted for new use-cases. Either a [performance dashboard](https://flowfuse.com/blueprints/manufacturing/performance-overview/), Artificial Intelligence, or station metrics – each is built faster when the data is readily available and well structured? As a company started around Node-RED, we’ve not spoken a lot where FlowFuse fits into the picture, which is what this post is about. ## Adapting legacy machines to the UNS The digitalization effort in traditional industries like manufacturing, agriculture, and beyond, has additional challenges due to the high capex assets that need to join in the effort. As these assets will not be replaced, the only way is to adapt them. Adaptation will require tooling that can interact with sensor data regardless of the protocol, data format, and data structures. Node-RED bridges the gap between analog and digital data acquisition by seamlessly integrating with a vast array of protocols including serial bus support, Modbus, MQTT, and OPC-UA. Its format agnostic nature allows it to handle diverse data formats, from parsing binary data, to JSON, Protobuf (Sparkplug B), making it a versatile tool for extracting and manipulating data from various sources. With its widespread adoption, Node-RED ensures compatibility with almost every protocol, enabling users to connect and process data from a wide range of devices and applications. ## Contextualisation of the data When data is captured and parsed, it needs to be contextualized. For example; in a UNS the topic hierarchy on which to publish and subscribe to is based on location – that is; context. Furthermore, a raw sensor reading might miss details like the unit of measurement, what message version is required, or doesn’t supply the information in a proper type. All these minor niggles are actual blocking issues for adopting all sensors to the UNS. In some cases makes and models of the sensor might influence the tolerances of readings, it’s a good idea in those cases to include that information in the message. ## Filtering and preprocessing Not all messages are created equal, which was discussed in [an earlier post](https://flowfuse.com/blog/2024/01/unified-namespace-when-not-to-use/.). In two of the three examples provided in that post, Node-RED can preprocess or filter information before sending it to the UNS. In the case of big files or binary data, Node-RED can store it in S3 or a network attached storage layer, or even store it locally through a REST interface. The distribution of the event that created the binary data is still published through the UNS though. Filtering of data is also a great use-case for Node-RED, generally just a `change` node and the data is ready to be published. The flexibility to do virtually anything with captured data is what makes Node-RED such a strong partner for your UNS. ## Continuous improvement While it would be ideal if data schemas were stable, changes are frequent and unpredictable. It’s a non-obvious requirement for your UNS edge to be adaptable. Message structures can change, to add or remove data from them. Though also the format, from JSON to Sparkplug B, or maybe to XML. Not to say that standardization of messages will continuously require updates to leverage the UNS for higher business value. A swiss-army knife as both data sender and receiver is not just a nice-to-have, it’s a requirement. ## Scalable operations While there are other tools available that can adapt to a few protocols, or parse a handful of data formats, there’s no alternative for Node-REDs breadth and depth of integration level. This is why many organizations have already adopted Node-RED for their edge cases, which their current standard solution doesn’t handle. There’s no situation where a vendor provided, off-the-shelf solution handles protocols and formats across vendors, modern and legacy OT, that also satisfies the IT requirements unless the extensibility is handled through an Open-Source community, with compliance and security controls from a professional entity surrounding the open source project. ### How FlowFuse Enhances Node-RED While Node-RED is powerful for implementing UNS, its management and deployment can be complex. FlowFuse simplifies this process with a unified platform that offers one-click deployment, secure management, and scalability for Node-RED applications. It enhances collaboration through centralized management of all Node-RED instances, ensuring streamlined operations and increased efficiency. **[Sign up](https://app.flowfuse.com/account/create){rel=""nofollow""} now for a free trial and explore how FlowFuse can transform your Node-RED experience.** # Node-RED in a Unified Namespace Architecture As we embark on the journey toward a more interconnected industrial environment, the emergence of a Unified Namespace (UNS) as the fundamental framework for facilitating communication among various systems and devices has become a focal point of discussion. I've often been queried about the role of Node-RED in a UNS architecture. To illuminate this, let's delve into an exemplary UNS architecture, underlining the classic use cases for Node-RED within this framework. In this Article: [Node-RED: The perfect adapter and middleware for your UNS](https://flowfuse.com/blog/2024/02/node-red-perfect-adapter-middleware-uns/), where classic use cases for Node-RED are delineated. Building upon that foundation, this discussion aims to present a tangible architectural example, showcasing the practical implementation of these use cases. ## Simplifying Complexity: The Two-Layered Approach For ease of understanding, consider the architecture split into two principal layers: the Shopfloor layer and the Service layer. **The Shopfloor Layer** This is where the physical assets dwell, requiring connectivity through a network layer, which in our example, is the UNS. It acts as the foundation for data flow from the operational technology (OT) on the shop floor to the information technology (IT) in the Service layer. **The Service Layer** Here resides the applications and software that analyze data, transforming raw metrics into actionable insights. It's where the data becomes meaningful through analytics, dashboards, and decision-support tools. ## The Roles in UNS Architecture Within this bifurcated architecture, we have two general categories of actors: Indirect Consumer/Producers and Direct Consumer/Producers. **Indirect Consumer/Producers**: These actors cannot natively communicate with our UNS broker. The communication barrier could be due to protocol differences, such as not using MQTT, or payload structures incompatible with your UNS's schema. This is a common challenge in manufacturing, especially in "brownfield" scenarios where legacy machines and equipment from various eras must be integrated. In such cases, Node-RED shines as a middleware for protocol conversion and data contextualization. Take, for example, the topic hierarchy in UNS based on location for context. A raw sensor reading might lack necessary details like measurement units or message versions. Node-RED steps in to enrich this data, ensuring compatibility with the UNS. In our [concrete architecture example](https://flowfuse.com/blog/2023/12/unified-namespace-data-modelling/), we have an indirect PLC producer. With Node-RED, we can convert and contextualize the data from this PLC for the UNS, ensuring smooth communication and effective integration. **Direct Consumer/Producers**: Contrastingly, direct actors can interact with the UNS out of the box. Modern industrial equipment usually falls into this category, equipped to speak the language of the UNS directly. However, the challenge remains not just in protocol communication but also in data contextualization. Merely speaking the same language is not enough; the data must also carry the correct context to be fully understood and utilized. ![Example Architecture](https://flowfuse.com/blog/2024/02/images/unified-namespace-architecture.png) ## Harnessing Node-RED for Actionable Insights Node-RED's prowess extends beyond middleware capabilities; it can also derive actionable insights. Our example architecture includes Dashboards for both the Human Machine Interface on the Shopfloor and an OEE Dashboard in the Service Layer. These dashboards engage with the UNS, calculating KPIs directly within Node-RED. For manufacturing applications, our [Blueprint Library](https://flowfuse.com/blueprints/) serves as a robust starting point, offering one-click deployment to your Node-RED managed instance. Node-RED emerges not just as a translator between machines and UNS but as an interpreter and analyst, generating real-time insights that drive decision-making and operational efficiency. The role of Node-RED in UNS architecture, therefore, is not ancillary; it is central to realizing the vision of a connected, intelligent industrial ecosystem. ![Andon Live Dashboard](https://flowfuse.com/img/ANDON-Screenshot-D4DBvWieJZ-650.avif) # Should You Invest in Professional Services for Your Node-RED Development? Professional services come in many different forms. Anything from setup and configuration, flow development, to node development. In this article, we will cover a few examples of how you can leverage professional services for your Node-RED applications. ## Should You Invest in Professional Services for Your Node-RED Development? Node-RED is multifaceted, and Professional Services (PS) can apply to many use cases. We will break it down into three categories that will help identify whether PS (Professional Services) is right for you. Those categories will be setup, flow development, and node development. Each takes a different skill set and has different levels of complexity. Before we jump into things, we need to understand the value proposition of Node-RED. Why Node-RED? Node-RED is a tool that makes Citizen Development a reality. It should be treated that way throughout the development journey regardless of Professional Services or not. Following standard procedures that minimize complexity, for example: reducing the use of function nodes when other nodes would be more ideal. Don’t overcomplicate things that don’t need to be or better said by this quote. > Easy things should be easy, and hard things should be possible. > -Larry Wall ### Setup It is often recommended to use PS for the setup and installation of products, and Node-RED and FlowFuse aren't different. This becomes especially true if you desire to professionalize your Node-RED instance. The reason for this is that PS teams focus on minimizing the risk of application failure, security compromise, and data/ip loss. This is an area of PS that can easily be overlooked, mainly there aren't any negative consequences in the short term. The work these teams focus on prevents catastrophic issues in the future. With Node-RED and FlowFuse, these teams will focus on installing FlowFuse based on system requirements and prerequisites, migrating existing Node-RED instances, securing your environment, and integrating with your existing ecosystem of applications. ### Flow development Oftentimes, many think of Flow development when they think of PS for Node-RED. There will be a process flow that needs to be accomplished, but it is leveraging standard nodes built within the Node-RED platform. Occasionally, there will be a need to install a custom node from the pallet manager, but the complexities in the development are adhering to a process and or visualization. There may be complex data translation, but for the most part, access to the command line of the Node-RED instance is unnecessary. Professional services may be used in situations like this where knowledge of specific systems that Node-RED is integrating into is complex. For example, a complex backend database for specific industry-wide used applications. ### Node Development At the core of some integration projects lies a critical requirement: the need for specific integrations that align precisely with your application's demands. This is where Node-RED's open-source nature becomes its most significant asset. Node-RED allows for the development of custom nodes, catering to specialized protocols unique to your organization or to serve niche applications. Such node development isn't just another feature; it represents the foundational element of Node-RED, embodying the platform's core value proposition. Whether addressing the need for custom integration with internal systems or expanding functionality to include less common applications, node development stands as the pivotal mechanism for enabling these capabilities. This critical development can be undertaken in-house, leveraging your team's expertise, or through PS specialized in node development. #### Conflating Flow and Node Development It is important when scoping out a project to identify all of the potential nodes needed and identify any potential missing ones. The skills for developing Nodes and Flow development, while similar, are often different skill sets. Having these identified will help prevent scope creep and setting expectations on timelines. ## Conclusion As we've explored throughout this article, Professional Services for Node-RED can significantly enhance the efficiency, security, and scalability of your automation and integration projects. From the initial setup and configuration to the intricacies of flow and node development, the expertise PS teams offer can be invaluable. The decision to engage with Professional Services should be informed by your project's specific needs, internal capabilities, and strategic priorities. Whether it's leveraging PS for the foundational setup of your Node-RED instance, outsourcing flow development to expedite project timelines, or seeking expert assistance for node development, the goal is the same: create a coding environment that is conducive to citizen development. If you are interested in professional services or consultation, [please reach out](https://flowfuse.com/professional-services/). # Bringing Software Development practices to Node-RED I'm always thinking about how we can continue to improve the Node-RED experience. One area I like to explore is to make sure we learn the right lessons from the Software Development world. In this post, I'm going to look at some of the common practices in modern Software Development and show how they translate to the Node-RED world. ### Linting Linting is a way of examining some code and automatically spotting things that need attention. This can range from stylistic errors ("Use tabs, not spaces") to real bugs that will prevent the code from working as intended. This is all about spotting problems *before* the code runs. It also helps ensure consistency when you have multiple people contributing to the code. Having code that is consistently formatted and free of syntactic mistakes makes it much easier to maintain. Applying this concept to Node-RED, we have the [`nrlint` tool](https://github.com/node-red/nrlint){rel=""nofollow""}. This is a linting tool that can run either on the command-line or within the editor directly to spot potential problems with the flows. On the stylistic side, for example, it can highlight nodes that aren't properly aligned to the grid. Whilst this doesn't have any bearing on the runtime operation of the flow, it encourages keeping the flows tidy and orderly. It can help identify potential infinite loops in flows, and highlight Debug nodes without a name set. ![](https://flowfuse.com/blog/2024/02/images/node-red-linter.png) ### Debugging Whether you are writing lines of code or not, eventually you will need to figure out why your application isn't doing what you think it should. In Software Engineering there are two typical approaches. One is to add debug statements through the code to print out bits of information as the program runs. Then, depending on what output you got, you'd move the debug statements around, add some more, print out different bits of information - all until you'd nailed down the problem. This is the Debug node approach in Node-RED; adding nodes at different points of your flow to capture some piece of information and then iterating as you go. This is probably how most Node-RED users go about it today. The downside is you end up leaving the Debug nodes in place, capturing information long after it is needed. The alternative approach is Step-by-Step debugging. This is why you are able to pause the program and then step it forward one statement at a time - examining the state at each point. But what's the equivalent for low-code? Pretty much exactly that when you have the [Node-RED Debugger plugin](https://flows.nodered.org/node/node-red-debugger){rel=""nofollow""} installed. This allows you to set 'breakpoints' on any node input or output that are triggered when a message arrives at that point of the flow. The Debugger will then pause the whole runtime and shows you all of the queued up messages in the flow. You can then examine those messages and tell the Debugger to 'release' them one at a time - seeing how the flow progresses. ![](https://flowfuse.com/blog/2024/02/images/node-red-debugger.png) ### Testing Testing code is a critical part of developing software. You want to make sure it does what you want. But it's more than just manually testing it once and then letting it go; you want to have tests you can run regularly, whenever you make changes, to ensure you don't break something that was working previously. In the Software Development world, there are all sorts of testing methodologies and techniques; unit testing individual components, system testing larger sections, stubbing out components to simulate different conditions, integrating test suites into the whole development process. They each have their own place in the process of software development. The question is, how does this apply to Node-RED? Most Node-RED users today will of course be testing their flows whilst developing them - iterating until the flow does what is needed. It's far less common to have a set of repeatable tests including the flows. That is certainly achievable with Node-RED today, albeit with some limitations. For example, a typical test will be to verify that, given a set of particular inputs, the outputs look correct. This can be done using Inject nodes to quickly trigger messages with different values, and use Debug nodes to examine the results. That isn't ideal as you end up littering your flows with these extra Inject nodes, and it still requires manually verifying the results. It also doesn't work well if your flows need to interact with external systems - such as saving values to a database. You don't want to pollute your system with test data. So what would be the ideal workflow? This is something the project has spent some time exploring in the past, and the start of a design was put together. The concept would be to introduce a Testing sidebar to the editor. Within that, you can define a set of test cases. Then for each test case, you can customise the behaviours of individual nodes. For example, a test case may disable an MQTT node at the start of a flow, and tell the runtime to inject a message in its place. For each subsequent node in the flow, the test case would then be able to either to bypass it (to avoid interacting with external systems), or to add checks on what the node outputs. Each test case would then define some criteria for what it means to pass the test. As with the Debugger plugin, the Test Runner would be disabled by default - so the 'production' flows aren't modified in anyway. When the Test Runner is enabled, it would then take care of running each test in return and reporting back the results. The main challenge is providing a user experience that makes it easy to create these tests in a way that is consistent with the low-code nature of Node-RED. Whilst this is very much a future roadmap item for Node-RED, it is one I hope we can start moving forward soon. Having a good, repeatable, testing strategy in Node-RED will make it stand-out from many of the other low-code tools and platforms available today. ### Low-Code vs Lines-of-Code The low-code nature of Node-RED means it is easily accessible to a wide range of users. You don't need to be a seasoned software engineer to get started. If you have a task to solve, and understand it well enough to break it into the right set of steps, translating that into a Node-RED flow can be much easier than having to write all of the corresponding code from scratch. Just because you aren't writing code in Node-RED, it doesn't mean you shouldn't be able to benefit from ways of working that are proven to improve the end result - whilst keeping true to the low-code nature of the project. ### FlowFuse Cloud Both `nrlint` and Node-RED Debugger are already pre-installed in all [FlowFuse Cloud](https://app.flowfuse.com){rel=""nofollow""} hosted instances. You can start using them today via the sidebar menu. ![](https://flowfuse.com/blog/2024/02/images/node-red-sidebar.png) # Storing Data: Getting Started with Node-RED It's quite straightforward to pass plenty of useful data with each message (msg) in your flows. Not only can you store information in msg.payload, but you can also place information in any other named object, for instance, msg.store. In this article, we will explore some of the better solutions for storing and retrieving transactional information in your flows. #### Storing data outside of msg.payload In this example, we have data in msg.payload as well as in msg.later. :video{ariaLabel="Storing data outside of msg.payload" autoPlay="true" height="362" loop="true" muted="true" playsInline="true" preload="none" width="1040"} If you want your debug to display the full content of the message, change the output to 'complete message object' as shown above. You can import the flow using this code. ::render-flow ```json [{"id":"aaa1e17e5f158004","type":"inject","z":"67746003c844dbc4","name":"Inject the message","props":[{"p":"payload"},{"p":"later","v":"A string I want to be able to use later in my flow","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World","payloadType":"str","x":690,"y":140,"wires":[["bce1bf09736125b7"]]},{"id":"bce1bf09736125b7","type":"debug","z":"67746003c844dbc4","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":880,"y":140,"wires":[]}] ``` :: Storing data outside of msg.payload can be very useful when you need access to that data later in your flow. You may notice that many nodes overwrite the content of msg.payload, so putting your data elsewhere is essential otherwise, it will be overwritten and lost. #### Tidying your messages You may also want to remove data you don't need from your messages to optimize the speed of your flows. It's easy enough to remove data you don't need, wherever it sits within your messages using the Change Node. In this example, we are going to delete the content of msg.other while leaving the rest of the message to be passed to the next Node. :video{ariaLabel="Deleting data from msg.other" autoPlay="true" height="344" loop="true" muted="true" playsInline="true" preload="none" width="1136"} The Change Node is configured as follows. ![Change Node configuration](https://flowfuse.com/blog/2024/02/images/delete.png "Change Node configuration") You can import the flow using this code. ::render-flow ```json [{"id":"1ac51e71153f7c1f","type":"inject","z":"67746003c844dbc4","name":"Inject the message","props":[{"p":"payload"},{"p":"other","v":"We don't need this string anymore","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World","payloadType":"str","x":530,"y":100,"wires":[["5d3a978ad9eab443","cd7609101328caa2"]]},{"id":"5d3a978ad9eab443","type":"debug","z":"67746003c844dbc4","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":700,"y":60,"wires":[]},{"id":"cd7609101328caa2","type":"change","z":"67746003c844dbc4","name":"","rules":[{"t":"delete","p":"other","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":100,"wires":[["be2f7f68dee570be"]]},{"id":"be2f7f68dee570be","type":"debug","z":"67746003c844dbc4","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":900,"y":100,"wires":[]}] ``` :: #### Storing data outside of msg.payload so you can access it later in your flows Storing data outside of msg.payload allows you to access it later in your flows. In this example, we inject geographical coordinates and use an API to get the sunset time for each location. We can then output the result as a sentence. As the HTTP Node, which we are using to interact with the weather API, overwrites msg.payload with the response, we will store the submitted city name and coordinates in msg.store for later use. You can see the flow working below. :video{ariaLabel="Example flow which gets the sunset time for a given location" autoPlay="true" height="448" loop="true" muted="true" playsInline="true" preload="none" width="1142"} You can import the flow using this code. ::render-flow ```json [{"id":"809cc8f4678767b7","type":"inject","z":"67746003c844dbc4","name":"London","props":[{"p":"payload.city","v":"London","vt":"str"},{"p":"payload.lat","v":"51.5072","vt":"str"},{"p":"payload.lng","v":"0.1276","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170,"y":80,"wires":[["6b5a6a2ef7a64f1a"]]},{"id":"b86d2d558eebbd7e","type":"inject","z":"67746003c844dbc4","name":"Washington DC","props":[{"p":"payload.city","v":"Washington DC","vt":"str"},{"p":"payload.lat","v":"38.9072","vt":"str"},{"p":"payload.lng","v":"77.0369","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":140,"y":160,"wires":[["6b5a6a2ef7a64f1a"]]},{"id":"aaecc81a2de233d6","type":"debug","z":"67746003c844dbc4","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":820,"y":160,"wires":[]},{"id":"6b5a6a2ef7a64f1a","type":"change","z":"67746003c844dbc4","name":"","rules":[{"t":"set","p":"store","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":120,"wires":[["273ee743f8d1a5f1","af43cea5866e11f5"]]},{"id":"273ee743f8d1a5f1","type":"template","z":"67746003c844dbc4","name":"create the URL","field":"url","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"https://api.sunrisesunset.io/json?lat={{store.lat}}&lng={{store.lng}}","output":"str","x":500,"y":120,"wires":[["52e8913233f379eb","1d94053c790791ee"]]},{"id":"af43cea5866e11f5","type":"debug","z":"67746003c844dbc4","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":480,"y":160,"wires":[]},{"id":"52e8913233f379eb","type":"http request","z":"67746003c844dbc4","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":670,"y":120,"wires":[["aaecc81a2de233d6","a245b322ade3a7e9"]]},{"id":"1d94053c790791ee","type":"debug","z":"67746003c844dbc4","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":660,"y":80,"wires":[]},{"id":"a245b322ade3a7e9","type":"template","z":"67746003c844dbc4","name":"Create the sentence","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"The sun will set at {{payload.results.sunset}} in {{store.city}}.","output":"str","x":860,"y":120,"wires":[["3e4b2d3644845f91"]]},{"id":"3e4b2d3644845f91","type":"debug","z":"67746003c844dbc4","name":"debug 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1040,"y":120,"wires":[]}] ``` :: Using this technique, we can build the content of message.store (or any other name you'd like to use) and then output the content at the end of a flow, even if you use msg.payload to interact with APIs and other custom nodes. #### Why not store the values in context? Where possible, it's more robust to store all the information related to a particular message within the message rather than saving it to context and retrieving it later. Using context risks a [race condition](https://en.wikipedia.org/wiki/Race_condition#:~\:text=A%20race%20condition%20or%20race,to%20unexpected%20or%20inconsistent%20results){rel=""nofollow""} within your flow that could result in data corruption. In this example, we simulate how a race condition can make context a bad choice for transactional data storage. The flow passes in the name and age of two people then moves the age to context. The flow then adds a random delay for each message so that, in some cases, the messages do not reach the debug in the order they were created. After the delay, the age is pulled back from context and added to each msg.payload. :video{ariaLabel="Example of how a race condition can make context a bad place to cache data" autoPlay="true" height="356" loop="true" muted="true" playsInline="true" preload="none" width="1042"} If there is a race condition in play, we should intermittently see Rob and John's stored ages being assigned to the wrong person. We can see in the image above that Rob is showing the incorrect age. You can import the flow using this code. ::render-flow ```json [{"id":"7ac4e7165d99f041","type":"inject","z":"67746003c844dbc4","name":"Rob","props":[{"p":"payload.name","v":"Rob","vt":"str"},{"p":"payload.age","v":"46","vt":"str"}],"repeat":"2","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":840,"wires":[["1e0575bd662f9277"]]},{"id":"e9c0baba4f50b0b2","type":"inject","z":"67746003c844dbc4","name":"John","props":[{"p":"payload.name","v":"John","vt":"str"},{"p":"payload.age","v":"29","vt":"str"}],"repeat":"2","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":570,"y":880,"wires":[["1e0575bd662f9277"]]},{"id":"d5f688682206d316","type":"change","z":"67746003c844dbc4","name":"","rules":[{"t":"set","p":"age","pt":"flow","to":"payload.age","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":860,"wires":[["d56225e59bde93cf"]]},{"id":"d56225e59bde93cf","type":"change","z":"67746003c844dbc4","name":"","rules":[{"t":"delete","p":"payload.age","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1050,"y":860,"wires":[["d5a119b311bf8377"]]},{"id":"e3ec11b4e038e564","type":"debug","z":"67746003c844dbc4","name":"debug 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1080,"y":940,"wires":[]},{"id":"d5a119b311bf8377","type":"delay","z":"67746003c844dbc4","name":"","pauseType":"random","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"0","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":720,"y":940,"wires":[["b329d7f2685d0ed6"]]},{"id":"b329d7f2685d0ed6","type":"change","z":"67746003c844dbc4","name":"","rules":[{"t":"set","p":"payload.age","pt":"msg","to":"age","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":900,"y":940,"wires":[["e3ec11b4e038e564"]]},{"id":"1e0575bd662f9277","type":"delay","z":"67746003c844dbc4","name":"","pauseType":"random","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"0","randomLast":"1","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":700,"y":860,"wires":[["d5f688682206d316"]]}] ``` :: #### Conclusion Storing transactional data in msg rather than context in Node-RED offers advantages in modularity, scalability, simplicity, and also helps prevent race conditions. By using msg, data transfer between nodes becomes more seamless, allowing for independent node reuse across flows without additional configuration. This approach ensures scalability by avoiding the imposition of large datasets on the global context. Moreover, storing data outside of msg.payload within the msg object enhances flexibility. It separates metadata and other relevant information from the main payload, promoting a cleaner and more organized structure. This practice not only aligns with Node-RED's visual programming paradigm but also improves code readability. Steering clear of context for transactional data, while also organizing it within the msg object, provides a comprehensive and reliable solution within the Node-RED framework. Thanks to [SunriseSunset](https://sunrisesunset.io/){rel=""nofollow""} for creating the useful API we've used in this article. # Citizen Development: Unleashing Domain Experts Citizen development has taken the spotlight recently, but the concept itself isn't entirely new. Remember the era of "hackers"? They weren't malicious actors, but individuals who tinkered with technology, pushing boundaries and creating solutions. Today, these same problem-solving personas have evolved into "[citizen developers](https://www.gartner.com/en/information-technology/glossary/citizen-developer){rel=""nofollow""}" – business users empowered to build applications without relying solely on professional coders. ## Why Now? While the drive for user-friendliness has always been present in programming languages, several factors have converged to push citizen development to the forefront: - Democratization of Technology: Cloud computing and advancements in low-code/no-code platforms have lowered the barrier to entry, making development tools accessible to individuals with minimal technical expertise. - Business Agility: The need for rapid innovation necessitates solutions that bypass lengthy IT backlogs. Citizen developers can bridge the gap, creating applications and internal tools quickly and efficiently. - Domain Expertise: Business users possess deep insights into specific processes and challenges. By giving them the tools to build solutions, organizations can tap into a new knowledge source by putting domain experts into the driver's seat. ### Beyond Spreadsheets: Platforms of Empowerment Reports and Dashboards are often created by a BI teams that have to take information from specialists to create custom visualizations that may or may not come out to be exactly what they asked for. Queue old faiful, spreadsheets, where a domain expert can create their own reports that suit their needs. Spreadsheets are powerful for analysis, but they often lack the real-time secure connectivity needed for enterprise applications. Transform the knowledge created in a spreadsheet into a personalized real-time visualization. To do that, you must first select a citizen development platform and those platforms should offer: - Visual Interfaces: Drag-and-drop functionality and pre-built components eliminate the need for complex coding, allowing users to focus on logic and functionality. - Integration Capabilities: Seamless connection with existing data sources and systems ensures that citizen-built applications integrate seamlessly into the overall workflow. Plus, the ability to create custom integrations. - Governance and Security: IT governance establishes guardrails while empowering users, ensuring data security and application stability through features like Role Based Access Control and SSO integration. - Collaboration Tools: Built-in collaboration features enable teams to share ideas, iterate on solutions, and ensure knowledge transfer. - Reseliency: Backup management. ### Citizen Developers: Not Rube Goldberg Machines, But Value Creators The key to successful citizen development lies in empowerment, not abdication. By providing the right tools, training, and governance, organizations can avoid creating complex, fragile solutions. Instead, citizen developers become powerful problem-solvers, building value-driven applications that address specific needs and contribute to overall business goals. ## Lean In Remember, citizen development isn't about replacing professional developers. It's about creating a collaborative environment where everyone can contribute their unique skills and perspectives. By removing the coding barrier, we unleash a wider pool of innovators, accelerating progress and driving organizational success. # Getting Started with Node-RED Dashboard 2.0 With our latest release of Node-RED Dashboard 2.0, we've made some big improvements to the onboarding experience. We're seeing over 2,000 people download Dashboard 2.0 per week, and are seeing a great buzz in the community of brand new Node-RED users, experienced Node-RED users that haven't explored a UI solution previously and existing users migrating from Dashboard 1.0. So, with that in mind, we wanted to offer a new "Getting Started" guide that will help you get up and running with building custom user interfaces and data visualizations in Node-RED. ## How to Install Node-RED Dashboard 2.0 ### Step 1: "Manage Palette" ![Screenshot to show where to find the "Manage Palette" option in Node-RED](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-manage-palette.png){dataZoomable=""} Screenshot to show where to find the "Manage Palette" option in Node-RED 1. Click the Node-RED Settings (top-right) 2. Click "Manage Palette" ### Step 2: Search & "Install" ![Screenshot to show where to find the "Install" tab, and how to find @flowfuse/node-red-dashboard](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-search-install.png){dataZoomable=""} Screenshot to show where to find the "Install" tab, and how to find @flowfuse/node-red-dashboard 1. Switch to the "Install" tab 2. Search for *"@flowfuse/node-red-dashboard"* 3. Click "Install" ## Adding your first widgets :cta-image{alt="Wenco deploys new dashboard pages in days with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/wenco-book-demo.png"} With the nodes installed, getting started is as easy as choosing a node from the Palette (the left-hand side list of nodes) in Node-RED, and dropping it onto your canvas. :video{ariaLabel="Screen recording to show how easy it is to deploy your first Dashboard 2.0 application." autoPlay="true" height="460" loop="true" muted="true" playsInline="true" preload="none" width="800"} Screen recording to show how easy it is to deploy your first Dashboard 2.0 application. In this case, we drop in a `ui-button`, click "Deploy" and then can see the button running live in our user interface. Notice too that Dashboard will automatically setup some underlying configurations for you (visible in the right-side menu): - `ui-base`: Each instance of Node-RED that uses Dashboard 2.0 must have a single `ui-base` element (we're hoping to add support for multiple in the future). This element contains all of the global settings for your Dashboard instance. - `ui-page`: A single Dashboard (`ui-base`) can consist of multiple pages, and can be navigated to using the left-side sidebar. Each page is then responsible for displaying a collection of `ui-group` elements. - `ui-group`: Each group contains a collection of widgets, and can be used to organize your Dashboard into logical sections. - `ui-theme`: Each `ui-page` can be assigned a given theme. Your "Themes" provide control over the aesthetic of your Dashboard like color, padding and margins. ## Configuring your layout Dashboard 2.0 adds a dedicated sidebar to Node-RED to provide a centralized view of your pages, groups and widgets. From here you can add new pages and groups, modify existing settings, and re-order content to your liking. ![Screenshot showing the Dashboard 2.0 sidebar in the Node-RED Editor.](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-sidebar.png){dataZoomable=""} Screenshot showing the Dashboard 2.0 sidebar in the Node-RED Editor. When defining your layout options, we break the choice into two sections: - **Page Layout:** Controls how the `ui-groups`'s are presented on a given page in your application. - **Navigation Sidebar:** Defines the left-side navigation style, defined at the `ui-base` level. ![Example of a "Grid" page layout, with "Collapsing" sidebar navigation.](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-layout.png){dataZoomable=""} Example of the "Grid" page layout, with "Collapsing" sidebar navigation. ### Page Layout Currently, we have three different options for page layout: - **Grid:** ([docs](https://dashboard.flowfuse.com/layouts/types/grid.html){rel=""nofollow""}) This is the default layout for a page, and uses a 12-column grid system to layout your `ui-groups`. Widths of groups and widgets define the number of columns they will render in. So, a "width" of 6" would render to 50% of the screen. Grid layouts are entirely responsive, and will adjust to the size of the screen. - **Fixed:** ([docs](https://dashboard.flowfuse.com/layouts/types/fixed.html){rel=""nofollow""}) Each component will render at a *fixed* width, no matter what the screen size is. The "width" property is converted a fixed pixel value (multiples of 48px by default). - **Notebook:** ([docs](https://dashboard.flowfuse.com/layouts/types/notebook.html){rel=""nofollow""}) This layout will stretch to 100% width, up to a maximum width of 1024px, and will centrally align. It's particularly useful for storytelling (e.g. articles/blogs) or analysis type user interfaces (e.g. Jupyter Notebooks), where you want the user to digest content in a particular order through scrolling. ### Navigation Sidebar Dashboard 2.0 offers various options on the appearance of the navigation sidebar: - **Collapsing:** When the sidebar is opened the page content will adjust with the width of the sidebar. - **Fixed:** The full sidebar will always be visible, and the page content will adjust to the width of the sidebar. - **Collapse to Icons:** When minimized, users can still navigate between pages by clicking on the icons representing each page in the sidebar. - **Appear over Content:** When the sidebar is opened, the page is given an overlay, and the sidebar sits on top. - **Always Hide:** The sidebar will never show, and navigation between pages can instead be driven by [`ui-control`](https://dashboard.flowfuse.com/nodes/widgets/ui-control.html){rel=""nofollow""}. ### Define Your Layout In our example, we're going to switch to a "Notebook" layout, with a "Collapse to Icons" sidebar: ![Example of the "Notebook" layout and "Collapse to icons" sidebar](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-example.png){dataZoomable=""} Example of the "Notebook" layout, with "Collapse to Icons" sidebar navigation. ## Adding More Widgets Now, we're going to build a quick example to demonstrate how we can wire nodes together, and visualize the output from a `ui-slider` onto a `ui-chart`. ### Adding a Group In the Node-RED Editor's Dashboard 2.0 sidebar, we're going to then do the following things: 1. Edit "My Group" and rename it to "Controls" 2. Create a new "Group" in your existing page called "Data Visualization" You'll now see the two groups listed under "Page 1". "Controls" with a single `ui-button` and "Data Visualization" with no widgets. ![Screenshot of the modified and newly added groups](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-new-group.png){dataZoomable=""} Screenshot of the modified and newly added groups ### Connecting New Nodes Then, we're going to add two new widgets: - UI Chart - UI Slider Which we can do by dropping them from the left-side Palette and onto our canvas. We'll need to double-click each new node and confirm which "Group" we want to add this node to. In this case, we'll add the `ui-slider` to the "Controls" group, and the `ui-chart` to the "Data Visualization" group. We're also going to connect the output from both the `ui-slider` and `ui-button` to the input of the `ui-chart`: ![Screenshot of the Node-RED Editor, showing the ui-slider and ui-button connected to our ui-chart](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-flow.png){dataZoomable=""} Screenshot of the Node-RED Editor, showing the ui-slider and ui-button connected to our ui-chart Now, when we view our Dashboard, we can see the `ui-slider` output is def straight into our `ui-chart`: ![Screenshot of the Dashboard with all three widgets rendered](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-final.png){dataZoomable=""} Screenshot of the Dashboard with all three widgets rendered The final step we're going to make is to modify our `ui-button`. We're going to rename it to "Clear", and configure it's "Payload" option to send a JSON payload of `[]`, which, when sent to the `ui-chart` will clear the chart of all data. ![The ui-button configuration after setting it's payload and label](https://flowfuse.com/blog/2024/03/images/dashboard-getting-started-btn-config.png){dataZoomable=""} The ui-button configuration after setting it's payload and label With all of this together, we have the following functional Dashboard: :video{ariaLabel="Short animation showing the final functional dashboard" autoPlay="true" height="450" loop="true" muted="true" playsInline="true" preload="none" width="800"} Short animation showing the final functional dashboard. ## Next Steps Whilst this is just a simple introduction of Node-RED Dashboard 2.0, we do have many other articles and documentation that can help you get started with more advanced features. - [FlowFuse Dashboard Articles](https://flowfuse.com/blog/dashboard/) - Collection of examples and guides written by FlowFuse. - [Node-RED Dashboard 2.0 Documentation](https://dashboard.flowfuse.com){rel=""nofollow""} - Detailed information for each of the nodes available in Dashboard 2.0, as well as useful guides on building custom nodes and widgets of your own. - [Node-RED Forums - Dashboard 2.0](https://discourse.nodered.org/tag/dashboard-2){rel=""nofollow""} - The Node-RED forums is a great place to ask questions, share your projects and get help from the community. - [Beginner Guide to a Professional Node-RED](https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/) - A free guide to an enterprise-ready Node-RED. Learn all about Node-RED history, securing your flows and dashboard data visualization. - [FlowFuse - Book a Demo](https://flowfuse.com/contact-us) - FlowFuse provides a complete platform to scale your production Node-RED applications, increase developer velocity, and enhance security in order to accelerate innovation. ## Follow our Progress New features and improvements are coming to Node-RED Dashboard 2.0 every week, if you're interested in what we have lined up, or want to contribute yourself, then you can track the work we have lined up on our GitHub Projects: - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} - [Dashboard 1.0 Feature Parity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/5){rel=""nofollow""} If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. # FlowFuse and Gallarus Announce Strategic Partnership to Accelerate Industry 4.0 Adoption FlowFuse, a leading provider of the low-code end-to-end development platform for industrial applications, and Gallarus Industry Solutions Limited, the leading industry 4.0 integrator in Europe dedicated to digital transformation through the deployment of the Unified Namespace (UNS) digital architecture, today announced an exciting strategic partnership. This collaboration aims to empower businesses with advanced solutions for Industry 4.0 transformation and optimized operational efficiency. Powered by Node-RED, FlowFuse enables teams of all sizes to harness low-code capabilities to build robust applications with unparalleled scalability, and security. FlowFuse’s collaborative environment and scalable architecture ensure that teams can build securely and together for continuous development and operational success. Gallarus, with its extensive experience in digital transformation projects and industrial technology integration, will leverage its expertise in high-quality project implementation, support, and maintenance. Together, they offer a comprehensive solution for businesses seeking to digitally transform their operations. “We are pleased to announce this strategic alliance with Gallarus,” said Zeger-Jan van de Weg, CEO at FlowFuse. “This partnership signifies a significant step forward in providing businesses with the tools they need to excel in the current digital landscape. By combining our expertise, we are confident in delivering exceptional value to our customers.” “This partnership with FlowFuse perfectly aligns with our mission to empower businesses with transformative industry 4.0 solutions,” said Patrick Mc Carthy, COO at Gallarus Industry Solutions Limited. “FlowFuse's low-code development platform, combined with our expertise in UNS architecture and integration, will provide a powerful solution for organizations of all sizes to move away from an Industry 3.0 mentality and embrace Industry 4.0, streamlining operations to unlock new levels of efficiency not seen before.” This combined expertise will address the complexities of digital transformation for businesses by: - Facilitating data access, transformation, and visualization across any protocol. - Breakdown data silos enabling organization-wide data availability via the UNS. - Enable citizen developers to build extremely useful industrial applications. For more information about FlowFuse and Gallarus Industry Solutions Limited, please visit their respective websites at [flowfuse.com](https://flowfuse.com) and [gis.ie.](http://gis.ie){rel=""nofollow""} # FlowFuse Open Source Tier Resource Limits Today, with the latest FlowFuse release, an important change was made that updates the resource limits for our open-source, self-managed FlowFuse server tier. This change does not affect FlowFuse Cloud users in any way. ## New Open Source Tier Resource Limits The new limit for the number of Node-RED runtimes on the Open Source tier is 5. These 5 instances can be distributed across a maximum of 5 teams. This revised structure allows for the development of your initial FlowFuse and Node-RED solutions, providing a clear understanding of how FlowFuse can enhance your organization's workflows. Upgrading to a Team or Enterprise tier license unlocks the full potential of FlowFuse and grants access to our dedicated support team. ## Why the Change? Here at FlowFuse, we're committed to providing a great experience for all our users, including those utilizing our free, open-source tier. We initially designed this tier to offer a platform for small organizations and larger teams to explore FlowFuse's capabilities without needing our direct involvement. However, we've observed instances where the Open Source tier's free limits were being used to run very large deployments and even entire sites. While support is a key benefit we offer to paying customers, we believe everyone deserves a positive FlowFuse experience. As such, we're adjusting the resource limits to better align with the Open Source tier's intended purpose and allow the company to continue to invest in the Open Source tier. ## Transitioning Users If you're currently impacted by these resource limit changes, we want to ensure a smooth transition. We're offering a complimentary 1-year Enterprise license to affected users. To claim your license, simply email with the IP address your server has used to send telemetry data (if available) and a screenshot of your admin panel so we can match the currently usage with the new license. We appreciate your understanding and continued support. If you have any questions regarding this update, please don't hesitate to reach out to our team at . # Securing HTTP Traffic for Node-RED with FlowFuse Citizen development empowers employees to create digital solutions. However, it requires guardrails to ensure data security, operational stability, and compliance. These guardrails are what FlowFuse provides to the Node-RED community to level up their deployments. FlowFuse offers many different security measures for authentication and authorization, which all apply to different scenarios. In this post we’ll take a look at most of them, specifically for HTTP traffic. We’ll discuss the trade-offs for auditabliltiy, convenience to use as either machine or human, among other factors. ## HTTP Basic Authentication: A Simple Approach HTTP Basic Authentication is widely supported and straightforward to implement, making it a popular choice for securing APIs. It requires users to provide a username and password before accessing the Node-RED instance. While this method is easy to use, it's important to note that the username and password are shared and transmitted in plain text, making it vulnerable to interception if the connection doesn't leverage SSL/TLS. FlowFuse by default ensures SSL/TLS is deployed. ## Personal access tokens: Knowing who accessed the Node-RED Personal access tokens (PATs) are an essential component of FlowFuse, allowing users to securely access their accounts without sharing their passwords. These tokens are generated by the user and can be used to authenticate to the SaaS product's API or other services. PATs provide a more secure alternative to traditional username/password authentication, as they can be revoked or regenerated at any time, limiting the potential impact of a compromised token. ## FlowFuse Authentication: Seamless Integration FlowFuse authentication offers a seamless and secure way for users to access dashboards and other resources that are typically accessed through a browser. It leverages single sign-on (SSO) and SAML 2.0, reducing the management burden for organizations. For users this is convenient as they can access multiple applications and resources using a single set of credentials, eliminating the need to remember and manage multiple passwords. For organizations, SSO enhances security by centralizing authentication and authorization, reducing the risk of unauthorized access. By leveraging SSO and SAML 2.0, FlowFuse takes care of user management, freeing up customers from the administrative burden of managing user accounts and passwords. FlowFuse authentication adheres to industry-standard security protocols, ensuring compliance with regulatory requirements. This method of authentication is however impractical for API access by other services, to programmatically transfer data between them. ## Bearer Authentication: A Token-Based Approach Bearer Authentication offers a more secure and flexible alternative to traditional username/password authentication. Users will generate the token through the FlowFuse platform with each instance generating its own token. These tokens can be designed to have limited lifespans, reducing the risk if compromised. In the case the token then becomes compromised only the instance in which the token is generated can become subject to malicious behaviors. In the case that this does occur, simply deleting the token will elevate any unwanted access. Compared to FlowFuse Authentication, this method is very well suited for API access and programmatic access to FlowFuse. ## Choosing the Right Authentication Strategy FlowFuse provides multiple authentication mechanisms to cater to various aspects of security and user experience. When designing your Node-RED applications, consider the specific requirements of your project and the patterns of user interaction to select the most appropriate authentication strategy. By doing so, you can ensure both a high level of security and an optimal user experience for your web development projects with API calls through Node-RED. In conclusion, understanding and utilizing these different types of authentication in FlowFuse empowers citizen developers like you to create more secure and efficient applications for diverse use cases. # Installing and operating Node-RED behind a firewall Practitioners using Node-RED often find themselves in a situation where a firewall is deployed in their organization. This network configuration is a fact of life and is generally not controlled by the same people using Node-RED. Given security reigns supreme in Industrial IoT (IIoT), and a firewall offers a lot of benefits, we anticipate it will be deployed more often in the future, and as such it’s good to understand how you can get the most out of Node-RED when deployed behind a firewall. ## Node-RED installation with a firewall Generally, the standard install procedure for Node-RED requires a connection to the NPM servers that host the package. Due to NPM’s unaudited nature, IT is unlikely to agree to a permanent exception to the firewall to allow access to it. However, there are a couple of actions one can take to install Node-RED anyway. First, ask for a temporary exception. Node-RED is installed in a few minutes, so if there’s a set time schedule an exception can be made there’s a regular method available again through collaboration with the network administrator. The second option is leveraging vendor specific package managers. As these are vetted repositories, it’s not uncommon that these gates in the firewall have been created and opened to you. Some vendors supply repositories for major package managers like `apt-get` on Debian/Ubuntu-based systems, or there’s a marketplace approach to install Node-RED like for example the [Rexroth CtrlX with Node-RED in it](https://developer.community.boschrexroth.com/t5/Store-and-How-to/FlowFuse-Node-RED/ba-p/82135){rel=""nofollow""}. Lastly, you could consider downloading the NPM package beforehand and transferring it to your machine within the network. NPM allows the installation of local packages, which in turn allows you to create applications with Node-RED. This is generally a shadow IT action, and not recommended unless it’s approved. ## FlowFuse and your firewall As FlowFuse can be installed in a VPN behind a firewall, there’s no requirement to open up a ‘gate’ in your firewall to FlowFuse servers. The safe perimeter provided remains to the outside world. The security aspect remains, though as a Node-RED developer, there are still everyday tasks you’ll need access to the outside world. Consider installing third party nodes to connect your Node-RED instance to virtually any protocol or digital service. There are over 5000 of these nodes available, and as an organization it’s challenging to keep on top of. Your firewall provides one layer of security so that the data you’re accessing remains safe. FlowFuse provides a second layer of protection; we’ve introduced a [“Certified Nodes” catalog](https://flowfuse.com/integrations/?certified=1). These nodes have gone through automated and manual inspection to prevent malicious code from making it onto your production systems. Installing these packages would typically still require you to obtain files from NPM. With FlowFuse however, a cache can be built with only vetted nodes – All other nodes remain unavailable. Once Node-RED is installed and the initial development has been completed, FlowFuse aims to reduce the maintenance burden on both IT and OT teams too. In the same package cache aforementioned, Node-RED versions can be added. Updating Node-RED to the latest version becomes a job of just a few clicks. Updating your Node-RED instances, even behind a firewall, is imperative, as virtually all breaches are the result of daisy-chaining multiple security vulnerabilities into a high to critical event. ## Wrap up FlowFuse founding engineers have decades of experience running Node-RED wherever it is valuable. Our product is the culmination of that, and we’re excited to help you become successful in your digitalization efforts – even when a firewall is in play. # Looking towards Node-RED 4.0 and beyond With Node-RED 4.0 coming soon, I wanted to take a look at what users can expect to see with the new release, as well as some of the new features we're working on. The Node-RED project [schedules its releases](https://nodered.org/about/releases/){rel=""nofollow""} around a yearly major release that coincides with when a Node.js version reaches its end-of-support. This lets us drop support for that node.js version and update the default version of node used in the docker containers we publish. We treat this as a major change because it might require actions on the end-users part to update any additional modules they have installed. With Node-RED 4.0, we will be dropping support for anything earlier than Node 18 - with Node 20 the currently recommended version to use. That will give users almost 2 full years before needing to consider another Node.js upgrade. Now, talking about Node.js versions is not that exciting. What is more exciting is to look at what new features are coming to Node-RED. There are a few things already merged and ready to be released in the first beta release, and more landing each week. ### More auto-complete Node-RED already has simple auto-complete on `msg` fields in the editor. We've now extended that to also work with `flow`/`global` context inputs as well as the `env` type for accessing environment variables. ![Node-RED editor autocompleting properties](https://flowfuse.com/blog/2024/03/images/nr4-auto-complete.png "Node-RED editor autocompleting properties") This makes it so much easier to work with these types of properties - being sure you're using something that exists rather than having to switch between different views in the editor to get the names right. In the case of env vars, it also shows you where the value was set - useful when you have nested groups and subflows which might be overriding a particular value. The `msg` auto-complete is still based on a built-in list of common message properties used by the core nodes. There is interest in enabling this to pull completions from 'live' messages seen by the node in question - but that's not currently in the plan for 4.0. ### Timestamp formatting The Inject node has provided the ability to inject a timestamp since the very early days of Node-RED. The value it actually sets is the number of milliseconds since epoch (aka January 1st, 1970). If you're used to working with JavaScript, then this is a perfectly normal way to pass times around. However, it isn't always what is needed and flows end up using a Function node to reformat it in some way. With 4.0 we've added options to pick what format the timestamp is generated in at the start. Now, formatting times and dates can be a big can of worms of options. So, for this initial release, we've kept it simple by offering three options: !["Format options for Node-RED timestamp"](https://flowfuse.com/blog/2024/03/images/nr4-timestamp-formatting.png "Format options for Node-RED timestamp") - *milliseconds since epoch* - the existing option, just more explicitly labelled for what it is - *YYYY-MM-DDTHH\:mm\:ss.sssZ* - also known as ISO 8601 - *JavaScript Date Object* - the standard Date object There is scope to allow custom format strings to be set in the node - but we'll see what the feedback is on these new options first. ### A better CSV node The CSV node has had a big overhaul to make it more standards compliant. It turns out CSV has a whole bunch of tricky edge cases that most users don't hit - but if you did hit them you would be stuck. The new node follows the [RFC4180](https://www.ietf.org/rfc/rfc4180.txt){rel=""nofollow""} standard and is also faster - wins all around. For those flows that rely on some of the non-standard edge case behaviour of the existing node, we've kept a legacy mode in place to keep those flows working. ### Customising config nodes in Subflows This one needs a bit of explaining. Subflows are a way Node-RED lets you create a flow and add multiple reusable instances of it within your flows. For example, a subflow may connect to an MQTT broker and do some standard processing on the messages it received before sending them on. The Subflow can then expose a set of properties that can be customised for each instance. In our example, that could be the topic the MQTT node subscribes to. However, in that example, the MQTT node's broker configuration would be locked to the same broker config node in every instance - and that's something we're solving in Node-RED 4.0. We're making it possible to expose the choice of a configuration node in the Subflow properties - so each instance can be customised even further. Another common use for this will be with Node-RED Dashboard - which uses config nodes to set the location of a widget. With Node-RED today, you cannot really use dashboard nodes inside subflows as you end up with multiple copies of the widgets all packed into the same group. With this update, you'll be able to configure the subflow instance with exactly what dashboard group to place its contents into. ### Updated JSONata The JSONata library is used to provide the `expression` types in Node-RED - a really powerful way of working with JSON objects. With this release we've updated to the new major release of JSONata that comes with a bunch of performance improvements. ### And many more minor changes I'll hold off listing them all out here, but there are plenty of other smaller changes scattered through the editor and the core nodes. Be sure to check the beta release notes when it arrives to see what else has been done. ## Looking further ahead Whilst all of these are great incremental improvements to Node-RED, there are some bigger items we're looking at that will really improve the overall Node-RED experience. I wrote recently about improving how users can [test their flows](https://flowfuse.com/blog/2024/02/software-development-in-node-red/#testing). This remains something I think we really help make Node-RED stand apart from other low-code solutions. It won't be in the imminent 4.0 release, but it is definitely still on the roadmap for a future release. ### Concurrent editing Another area we want to improve is the collaboration experience within Node-RED. Working on flows as a team is a key feature of FlowFuse, and we want to make it even easier to do. One of the common complaints is how Node-RED currently handles multiple users editing flows at the same time. Whilst it's better than it used to be, it still makes for a very jarring experience when you have to keep merging other users' changes into your own. Our goal is to make collaboration as simple and natural as possible. There are a wide range of approaches we could take here. For example, a small improvement would be to merge other users' changes in the background without interrupting what you're doing. But I think we do better than that. What if the editing experience was more like Google Docs - knowing that other users have the editor open, and being able to see their changes in real time. This would make for a truely collaborative editing experience. There are some difficult problems to solve before we can get there, but I think this will be one of the more transformational changes to Node-RED we've had for some time. ## Beta releases The [release plan](https://nodered.org/about/releases/){rel=""nofollow""} has Node-RED 4.0 coming around the end of April. As mentioned, we'll be doing a series of beta releases between now and then to start getting early feedback from the community. Keep an eye on the [community forum](https://discourse.nodered.org/c/news/9){rel=""nofollow""} for release announcements as they come. # Why Low-Code is Better There are two common reasons why new languages come about. They provide a feature missing in the existing programming languages, or it is a tool that is easier to learn and use. The latter often functions like a swiss army knife with each iteration including more and more tools. The journey of low-code is like a swiss army knife, the perfect tool for the Citizen Developer. ## A Typical Coding Evolution Every decade seems to introduce something that simplifies coding. For me, in college, it was Python. A classmate was excited about this new programming language that was all about simplicity and readability, especially with its indentation-based syntax. It seemed too simple at first. Yet, over time, Python became a staple in my programming toolbox. During this time in our education, our coursework was filled with languages like C++, which felt distant from the future of programming we imagined. We joked about "outdated" languages, not yet realizing the breadth of what programming could encompass. After finishing college in 2010, I started working during the tail end of the housing crisis in the U.S. My first role was as a Controls System Integrator at Logical System Inc., where I was introduced to programming PLCs with Ladder Logic. Despite my initial reservations, viewing it as barely a programming language, this experience was my first step towards appreciating the diversity and utility of programming languages beyond the conventional. Later on in my career, I worked as a Corporate Automation and Controls Engineer where I worked on and enforced standards for partner System Integrators and OEMs. One of these important standards was making sure applications written in the control process were written in Ladder Logic within the PLCs. There were exceptions, of course, but the rule was to apply a visually appealing coding language, Ladder Logic, over a text-like language often called Structured Text. ## Programming Languages as Specialized Tools Programming languages are typically optimized for certain tasks. For instance, Matlab and R excel in complex numerical computations thanks to their extensive libraries designed specifically for mathematical operations. While Python might not replace Matlab or R for their core functionalities, it can broaden the applicability of numerical analyses into various other contexts. In another example, VB.net is the go-to for standalone applications in Windows environments. However, for applications that need to run across different operating systems (excluding web applications), Java might be a better choice due to its platform independence. Each programming language has its niche, along with inherent complexities and constraints. There are situations, however, where the specific strengths of a programming language become less critical. In cases where the application is straightforward, the choice of language might simply come down to personal preference or familiarity. But an important consideration arises when thinking about the future of the project: "Will someone else need to edit or view this code later?" If the answer is yes, and especially if the project aims to involve citizen developers, opting for a low-code solution becomes highly advantageous. Low-code platforms are designed with accessibility in mind, making them ideal for projects that benefit from collaboration and ease of maintenance. ## The Purpose of Citizen Development The idea behind Citizen Development is simple, make programming accessible to more people. This is what low-code platforms aim to do. They lower the entry barrier, making programming more inclusive. Insisting on complex, traditional programming languages when there are simpler, equally powerful alternatives seems counterproductive. We should be looking towards making programming more accessible to everyone. If we want to drive meaningful change within our organizations, embracing tools that broaden participation is key. The aim of adopting new standards and tools is to simplify, not complicate. It's about finding better, more accessible ways to work that can accommodate a wider range of skill sets. ## Simplification This discussion isn't about the mechanics of coding, it's about opening up the field to more diverse contributions. Low-code platforms represent a step towards a more inclusive, collaborative future in technology. By lowering barriers to entry, we're not just simplifying coding, we're inviting a broader community to engage, innovate, and drive progress. I look forward to seeing how we can all contribute to this evolving landscape. ## How FlowFuse Helps Our goal here at FlowFuse is to keep expanding on the Swiss army knife, Node-RED. We strive to elevate Node-RED for professionals by providing the tools needed to deploy Node-RED in a safe and secure way. For example by default, the editor for Node-RED is protected using your FlowFuse user credentials. You can also use SSO to further protect your user accounts and give access to Node-RED to your team members. All traffic to FlowFuse and your Node-RED instances is protected by HTTPS. FlowFuse has set up the domain name and manages the certificates so you can spend time on your flows rather than configuring security. We believe that Low-Code is the future and strive to make Citizen Development a reality. To learn more [schedule a call with one of our experts.](https://flowfuse.com/free-consultation/) # Scaling Node-RED with FlowFuse: Differences between a FlowFuse Instance and a Device Instance FlowFuse is a Software as a Service (SaaS) platform designed to enhance the experience and capabilities of Node-RED for its users. By focusing on scalability, security, and Dev Ops, FlowFuse aims to remove some of the technical barriers associated with using Node-RED, making it easier for citizen developers to automate tasks, process data, and create applications. In this blog post, we will discuss the differences between a FlowFuse instance and a FlowFuse device instance while highlighting how FlowFuse addresses scalability challenges in Node-RED deployments. ## Scalability Challenges with Traditional Node-RED Deployments While deploying Node-RED is quite simple, managing multiple instances across different environments can become complex and time-consuming. As the number of devices and use cases grow, users face difficulties in scaling their Node-RED applications efficiently to handle increased load without compromising performance or security. This is where FlowFuse comes into play. ## The Role of FlowFuse as an Orchestration Tool FlowFuse functions as an orchestration tool that allows the deployment and management of all your Node-RED instances at scale, addressing scalability challenges head-on. By leveraging its platform, users can quickly deploy and manage multiple Node-RED instances while ensuring optimal performance and security. This enables them to connect with a wide range of devices, from PLCs and sensors to legacy software, without worrying about the complexities of managing their Node-RED deployment. ## Deploying Node-RED Next to Devices One common issue in IoT deployments is that device instances of Node-RED often communicate with unsecure devices or networks. To mitigate security risks and ensure data protection, it's common to deploy Node-RED in close proximity to these devices. The FlowFuse platform uses [device agents](https://flowfuse.com/platform/device-agent/) that communicate back to the platform via a reverse tunnel over port 443. This setup requires only one firewall rule: allowing outbound connections from the [device agent](https://flowfuse.com/platform/device-agent/) running Node-RED to the FlowFuse platform, significantly minimizing security risks while enabling remote monitoring, flow editing, and configuration deployment at scale. ## Deploying Node-RED Instances Within the FlowFuse Platform Not all instances of Node-RED need to be deployed at the edge and can be deployed anywhere. FlowFuse offers this flexibility in cases where users prefer or require deploying their Node-RED instances within the platform itself. This capability allows users to focus on developing and managing their applications without worrying about the underlying infrastructure. ## Conclusion FlowFuse addresses scalability challenges in Node-RED deployments by providing an easy-to-use platform that enables users to manage multiple instances at scale while maintaining security and performance. By understanding the differences between a FlowFuse instance and a device instance, you can make informed decisions about your deployment strategy and leverage the full potential of Node-RED for your applications. Stay tuned for our upcoming blogs where we will dive deeper into the areas of security, dev ops, and backup solutions provided by FlowFuse. [Walter took this exact path, growing from a single Node-RED instance to more than 130 across its global production network without adding headcount to its IT team](https://flowfuse.com/customer-stories/scaling-industrial-iot-operations-while-maintaining-competitive-edge/), using FlowFuse device agents to manage that scale. # How Kafka is applied in manufacturing Have you ever wondered how manufacturing and automotive industries can effectively manage the vast amount of real-time data generated by sensors and systems throughout the production process? A few years back, these industries faced major obstacles in handling the large volume of real-time data produced by sensors placed across the production line. Even today many industries continue to grapple with similar challenges. Traditional data management systems struggle to process and analyze this data in real-time, leading to inefficiencies in operational activities and decision-making. To address these challenges, various manufacturing and automobile plants have embraced technologies like Apache Kafka. Kafka provides a distributed streaming platform that enables the efficient handling of real-time data streams. By leveraging Kafka, we can aggregate, process, and analyze data in real-time seamlessly. This guide provides a high-level overview of Kafka, covering its definition, components, functionality, applications, and limitations. ## What is Kafka? Apache Kafka is a platform for distributed data streaming that allows for the publishing, subscribing, storing, and processing of streams of records in real-time. It is intended to handle data streams from multiple sources and to deliver them to multiple consumers. In essence, it can move large quantities of data in real-time from any source to any destination, simultaneously. Kafka is also a very good [broker for UNS architecture](https://flowfuse.com/blog/2024/01/unified-namespace-what-broker/). ## Understanding Kafka's Architecture Kafka architecture is designed to provide a scalable and fault-tolerant platform for handling real-time data streams. The architecture consists of several key components, each component serves a specific purpose in the data processing pipeline. In this section, we will take an overview of Kafka's architecture and its key components. !["Architecture of Kafka"](https://flowfuse.com/blog/2024/03/images/using-kafka-in-manufacturing-kafka-architecture.png "Architecture of Kafka"){dataZoomable=""} **1. Topics and Partitions** - Topics: Imagine topics as folders for organizing data – they act as distinct categories. Kafka arranges information into these topics for systematic storage. - Partitions: Think of partitions as subdivisions within topics. They enable parallel processing across multiple servers, enhancing fault tolerance and throughput. **2. Producers:** - Producers: Producers are like architects of data flow. They decide where to send records within a topic. This decision can be balanced using a round-robin or directed by a record key for specific purposes, such as maintaining order. **3. Brokers:** - Definition: Brokers are the backbone servers in a Kafka cluster. - Tasks: Brokers store data, handle requests from both producers and consumers, and maintain the integrity and persistence of data. They also manage the critical task of tracking offsets, which determine the position of consumers within partitions. **4. Consumers and Consumer Groups:** - Consumers: These entities read data from brokers. They subscribe to one or more topics and pull data from the specific partitions they are interested in. - Consumer Groups: Consumers collaborate in groups to scale data processing. Kafka dynamically assigns each consumer in a group a set of partitions from the subscribed topics, ensuring that each partition is processed by only one consumer within the group. **5. Offsets** - Definition: Offsets act as unique identifiers for records within a partition. They denote the position of a consumer in the partition. - Function: As consumers read records, they increment their offset. This allows them to resume processing from where they left off, which is crucial for handling failures or restarts. Kafka stores offset information in a specialized topic for easy recovery. **6. Replication** - Mechanism: Kafka ensures data durability by replicating partitions across multiple brokers. - Replication Factor: This configurable setting determines the number of copies of a partition in the cluster. If one broker fails, another can seamlessly take over, guaranteeing high availability. ## Features of Kafka Now that we've gained a foundational understanding of Kafka, let's explore the key features that make it a preferred choice for many organizations. These features highlight why Kafka transcends being just another data processing tool and why it merits consideration for various use cases. - **High Throughput and Scalability:** Kafka can handle thousands of messages per second and can scale horizontally and vertically to meet growing data demands without compromising performance. - **Fault Tolerance and Reliability:** Built to ensure reliability, Kafka guarantees fault tolerance through replication, safeguarding data against loss in the event of a broker failure. Data redundancy ensures data safety even during hardware failures. - **Real-Time Processing and Low Latency:** Kafka's real-time processing ensures low latency for instant data analysis, which is critical for real-time decision-making. ## Applications of Kafka As we explore the capabilities of Kafka, we realize that it goes beyond being just a regular data processing tool. Kafka is a strategic powerhouse that influences decision-making, operational efficiency, and overall effectiveness in various industries. In this section, we will discuss specific, practical applications of Kafka in different industries, demonstrating how its adaptability can solve unique challenges. **1. Manufacturing Operations Optimization:** - Real-time Production Monitoring: Kafka is used in manufacturing for continuous monitoring of production lines, equipment status, and inventory levels. This real-time visibility aids in optimizing production efficiency, reducing downtime, and enhancing overall supply chain management. - Quality Assurance and Yield Management: Companies utilize Kafka to monitor quality control metrics in real-time, enabling proactive measures to maintain product quality standards, minimize defects, and optimize production yield. **2. Predictive Maintenance:** Organizations use Kafka to collect and analyze sensor data from machinery and equipment to predict potential failures. This helps them optimize scheduled maintenance tasks to prevent costly downtime and disruptions **3. Supply Chain Management:** Kafka provides real-time visibility into supply chain operations. This enables companies to track shipments, monitor inventory levels, and coordinate with suppliers and distributors for efficient supply chain management. **4. Logistics and Transportation:** Companies use Kafka to track vehicle and shipment locations in real-time, optimizing routes through the processing of streams of GPS data. **5. Telecommunications:** Telecom operators utilize Kafka to monitor network performance metrics in real-time. This allows swift responses to outages or service degradations, ensuring a seamless communication network. **6. Financial Services:** Banks leverage Kafka to process transactions in real-time, enabling immediate fraud detection by analyzing patterns in transaction data as they occur. This enhances overall security and compliance in financial operations. ## Challenges and Considerations As beneficial as Kafka is in various industries, it also presents certain limitations and challenges that must be considered before deciding to use Kafka for your applications. 1. Performance: Kafka both receives and transmits data. When the flow of data is compressed or decompressed, the performance is affected. For example, if the data is decompressed it will eventually drain the node memory. As a result, it affects both throughput and performance. 2. Complexity: As we all know Kafka is an excellent platform for streamlining messages. However, in the case of migration projects that transform data, Apache Kafka gets more complex. Hence, to interact with both data producers and consumers you need to create data pipelines. 3. Tool Support: There is always a concern for startup companies to use Kafka over other options. Especially, if it remains in the long run. This is because a full set of management and monitoring tools are absent in Kafka. 4. Message Tweaking: Kafka uses system calls before delivering a message. Therefore, the messages are sensitive to modifications. Tweaking messages reduces the performance of Kafka to a greater extent. The performance is not impacted only under the condition of not changing the message. 5. Data Storage: Apache Kafka is not a recommended option for storing large sets of data. If the data is stored for a long period, the redundant copies of it are also stored. When this happens, the app must be ready to compromise its performance. For this reason, only use Kafka if there is a need to store data for a short period. Additionally, if you are interested in learning more about Kafka and its practical implementation, refer to our guide on Using [Kafka with Node-RED](https://flowfuse.com/blog/2024/03/using-kafka-with-node-red/). ## Conclusion This guide provides a high-level overview of Apache Kafka, including its definition, architecture, features, and applications in various industries and the challenges or limitations of using Kafka. Kafka's versatility in real-time data processing, decision-making, and operational efficiency is highlighted, with applications ranging from manufacturing to finance. The overview aims to provide a clear understanding of Kafka's role in handling data challenges and fostering innovation across sectors. # Using Kafka with Node-RED Kafka is one of the most powerful technologies enabling seamless data communication. Many individuals are utilizing it alongside Node-RED for real-time data exchange in their IoT and IIoT applications. However, some users are encountering difficulties in obtaining assistance with Kafka-related queries. During my recent visit to the Node-RED Forum, I noticed that while some Kafka-related queries have been answered nicely, others remain unanswered or have not been satisfactorily addressed, leaving users feeling stuck. To address this issue, we've created a comprehensive Kafka guide covering everything you need to know about Kafka, from installation and connection to data transmission. For newcomers to Kafka, we recommend reading our previous blog on [how Kafka is applied in manufacturing](https://flowfuse.com/blog/2024/03/using-kafka-in-manufacturing/), where we've covered the basics and practical applications extensively. ## Discussing problem and potential solution Let's start by discussing a problem: imagine a temperature sensor network across a city. We need to centralize and analyze this data in real time for effective monitoring and visualization. To resolve this problem we will use Kafka, Temperature sensors will feed data into Kafka through the Kafka producer. To retrieve real-time data for visualization and monitoring, we’ll be using Kafka Consumer. We will organize the data by region. The temperature data for each region will be managed in a specific Kafka topic partition. While in this guide, we will generate simulated data using random number expression and run both producers and consumers on the same system, practical scenarios often involve distributed setups across different devices or systems. ## Installing and running Kafka locally In this part, we’ll be installing Kafka locally using Docker to simplify the installation process, so make sure you’ve got Docker installed before you dive in. 1. Pull the zookeeper image if it is not already, and run the zookeeper container. ```text docker run -p 2181:2181 zookeeper ``` 2. Pull the Kafka image if it is not already, and run Kafka Container, expose PORT 9092. ```text docker run -p 9092:9092 ` -e KAFKA_ZOOKEEPER_CONNECT=.1:2181 ` -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://:9092 ` -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 ` confluentinc/cp-kafka ``` :cta-image{alt="Power Workplace relies on FlowFuse for scalability, reliability and security audits - book a demo" cta="demo" src="https://flowfuse.com/images/cta/power-workplace-book-demo.png"} ## Running Kafka on the cloud To run Kafka on the cloud, you can consider utilizing any cloud service according to your preferences. For a guide on running Kafka on a cloud platform, the procedures may differ. You can refer to the documentation provided by your preferred cloud service for detailed instructions. During the writing of this tutorial, I utilized [Aiven’s cloud data platform](https://aiven.io/kafka-connect){rel=""nofollow""} which offers the option to use Kafka in the free trial. However, you are free to choose any cloud service that suits your requirements and preferences. ## Installing Dashboard 2.0 We will be installing Dashboard 2.0 to display real-time temperature data of various regions on a chart. If you are new to Dashboard 2.0, we recommend referring to [Getting started with Dashboard 2.0](https://flowfuse.com/blog/2024/03/dashboard-getting-started/), which covers everything from basic concepts to installation and creating your first dashboard seamlessly. ## Installing and configuring Kafka custom node 1. Install `node-red-kafka-manager` by the palette manager. 2. Before connecting to Kafka, ensure you have the following information ready and environment variables set up as discussed below in the Adding environment variable section. - Host: The IP address or hostname of your Kafka broker server. - Port: Kafka typically uses port 9092 by default. Ensure this aligns with your Kafka broker's configuration. - SSL Configuration (if applicable): CA Certificate: The Certificate Authority (CA) certificate for validating the SSL connection. - SASL (Simple Authentication and Security Layer) Mechanism: Most of the Kafka broker servers use SASL for authentication such as 'PLAIN', 'SCRAM-SHA-256,' or 'SCRAM-SHA-512.' - Username: SASL username of Kafka broker server for authentication. - Password: SASL password of Kafka broker server for authentication. 3. Drag the Kafka Producer node onto the Canvas, click on that node, and click on the edit icon next to the broker input field to configure it. !["Screenshot showing configuration of kafka"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-kafka-configuration.png "Screenshot showing configuration of kafka") 4. Enable the TLS option if your Kafka broker server is using it for secure communication. 5. After enabling the TLS option click on the edit icon next to `add new tls-config` and upload the CA Certificate in PEM format. !["Screenshot showing TLS configuration for kafka"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-tls-configuration.png "Configuring tls for kafka") ## Adding Environment variables In this section, we will be setting up environment variables for Kafka configuration. If you have read our previous blog post, you may already know why we highly suggest using environment variables for every configuration. If not, please refer to our blog post on [Using Environment Variables in Node-RED](https://flowfuse.com/blog/2023/01/environment-variables-in-node-red/) for more information. !["Screenshot showing adding environment variable in Node-RED"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-setting-environment-variables.png "Screenshot showing adding environment variable in Node-RED") 1. Navigate to the instance's setting and then go to the environment section. 2. Click on the `add variable` button and add variables ( host, port, username, and password) for the configuration data that we discussed in the above section. To leverage the ease of configuration provided by the Kafka custom node that we are utilizing, ensure to set only one variable for both host and port in the following format: ```text [{"host":,"port":}] ``` 3. Click on the save button and restart the instance by clicking on the top right Action button and selecting the restart option. ## Creating a new Kafka topic In this section, we'll guide you through the process of creating a Kafka topic to handle temperature data from different city regions. To ensure the segregation of data for different zones within the city, we will configure the topic to create three partitions. 1. Drag an inject node onto Canvas. 2. Set `msg.topic` to `createTopics` string. 3. Set `msg.payload` to `[{"topic": "Temperature","partitions": 3,"replicationFactor":1}]`, you can create as many topics as you want at a time. 4. Drag the Kafka admin node onto Canvas. 5. Connect the inject node’s output to the Kafka admin node’s input. 6. Deploy the flow by clicking on the top-right red deploy button. 7. After the Deploy, click on the inject button to create a topic. ![Screenshot showing set payload in inject node for creating new Kafka topic"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-inject-node.png "Screenshot showing set payload in inject node for creating new Kafka topic") !["Screenshot showing Node-RED flow for creating new kafka topic"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-creating-topic.png "Screenshot showing Node-RED flow for creating new kafka topic") ## Sending Data to Kafka topic In this part, we’ll be setting up a producer to send simulated city region temperature data to Kafka. The producer will populate one of the available partitions in our temperature topic. We’ve already set up the temperature topic with 3 partitions. We’ll use these partitions to send data from each city region across the city (downtown region, suburban region, and industrial region). The partitioning process keeps the data separated, making it easier and faster to work with and analyze. 1. Drag a Kafka producer node onto Canvas. 2. Selected added Kafka configuration. 3. Click on that node and add the topic that we have created, set the key as `downtown`, and set the partition number as 0. (The partition index starts from zero) 4. Drag an inject node onto Canvas. 5. Set `msg.payload` to `$floor($random() * 100)` as a JSON expression and set the inject node to send the payload automatically after a specific interval of time. !["Screenshot showing kafka producer configuration"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-kafka-producer.png "Screenshot showing kafka producer configuration") ## Receiving data from Kafka topic In this section, we will be creating consumers who will subscribe to listen to downtown region temperature data. 1. Drag the Kafka consumer node onto Canvas. 2. Selected added Kafka configuration. 3. Click on that node, add the topic that we have created, and enter partition `0` from which it will read temperature data. 4. Add the Change node onto the Canvas and set `msg.topic` to `msg._kafka.key` and `msg.payload` to `$number(msg.payload)` because the Kafka custom node we are using is converting number data into a string. (consumer returns a Kafka object containing information related topic’s partition from data received, a key which we have set in the producer section to recognize data, and other information) 5. Add the ui-chart node on to Canvas and select the created group in which the chart will render. 6. Connect the Kafka consumer node’s output to change the node’s input and change the node’s output to the chart node’s input. !["Screenshot showing kafka consumer configuration"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-kafka-consumer.png "Screenshot showing kafka consumer configuration") !["Changing payload received from Kafka consumer"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-change-node.png "Changing payload received from Kafka consumer") Repeat the same steps to create producer and consumer for the rest of the two regions suburban and industrial, ensuring to set partitions 1 and 2 for, as we have already assigned partition 0 to the first producer created. ## Deploying the flow Our temperature monitoring system is now complete and ready for deployment. To initiate the deployment process, locate the red 'Deploy' button positioned in the top right corner and navigate to `https://.flowfuse.cloud/dashboard` !["Screenshot showing Node-RED flow of Real-time temperature monitoring system"](https://flowfuse.com/blog/2024/03/images/using-kafka-with-node-red-temperature-monitoring-system-flow.png "Screenshot showing Node-RED flow of Real-time temperature monitoring system") :video{ariaLabel="Video showing Dashboard 2.0 view of Real-time temperature monitoring system" autoPlay="true" height="338" loop="true" muted="true" playsInline="true" preload="none" width="600"} ## Conclusion In this guide, we’ve gone over everything you need to know about how to get started with Kafka and Node-RED. additionally, in this article, we’re going to focus on solving a problem where the sensor data across the city need to be centrally stored for efficient monitoring and visualization. By solving this problem step-by-step, you’ll understand how to integrate Kafka into your Node-RED applications. # How to Build an Admin Dashboard with Node-RED Dashboard 2.0 (2026) Managing and analyzing increasing amounts of data becomes crucial for organizations. Dashboard 2.0 and Node-RED help organizations access the data, normalize it, and visualize it. But what about controlling who can access what data? That's where an admin-only page comes in. Now With Node-RED Dashboard 2.0, we can also create robust and secure admin-only pages easily. In this guide, we'll provide you with step-by-step instructions to Build an Admin-only page with Node-RED Dashboard 2.0. If you're new to Dashboard 2.0, refer to our blog post [Getting Started with Dashboard 2.0](https://flowfuse.com/blog/2024/03/dashboard-getting-started/) to install and get things started. ## Enabling FlowFuse User Authentication Before proceeding further, let’s enable FlowFuse user authentication. This step adds an extra layer of protection to our dashboard by adding a login page that restricts access exclusively to registered FlowFuse users. Additionally, it further simplifies the process for the FlowFuse Multiuser addon to track and access logged-in user's data on the dashboard. For more information, refer to the [documentation](https://flowfuse.com/docs/user/instance-settings/#flowfuse-user-authentication) and ensure that it is enabled. !["Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security. "](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-flowfuse-instance-setting.png "Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security. "){dataZoomable=""} ## Exploring FlowFuse Multiuser Addon The FlowFuse Multiuser Addon is a plugin developed for Dashboard 2.0 to access logged-in user data on the dashboard. To install and understand how the FlowFuse Multiuser Addon works, refer to [Exploring the FlowFuse User Addon](https://flowfuse.com/blog/2024/04/displaying-logged-in-users-on-dashboard/#enabling-flowfuse-user-authentication) ## Storing a list of Admin users Before we start building the admin-only page We need to store a list of admin users somewhere so that we can later display the admin-only page to those users only, For this guide we will store the admin list in the global context. 1. Drag an inject node onto the canvas. 2. Drag the 'change' node onto the canvas and set `global.admins` to a JSON array containing the usernames of admin users. This will store the created admin list in our Node-RED global context. !["Screenshot displaying the change node which which stores list of admins username in global context"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-change-node-for-storing-adminlist-to-global-contenxt.png "Screenshot displaying the change node which which stores list of admins username in global context"){dataZoomable=""} 3. Connect the inject node’s output to the change node’s input. 4. To store the list in a global context, click the inject node’s button once you've deployed the flow. ## Building an Admin-only page Now, let's proceed with the practical steps to implement the admin-only page: 1. Create a new page in Dashboard 2.0, where we will display sensitive data that we want to hide from regular users, this page will be our admin page. 2. Drag an event node on the canvas, then click on it, and select the UI base that contains your all pages including the admin page 3. Drag a switch node on the canvas, and add two conditions, one to check whether the user’s username is contained in the admin list or a second for otherwise, see the below image. !["Screenshot displaying the switch node which checks whether the logged-in user's username is contained in the admin list or not"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-switch-node-checking-page-viewer-isadmin.png "Screenshot displaying the switch node which checks whether the logged-in user's username is contained in the admin list or not"){dataZoomable=""} 4. Drag two change nodes onto the canvas, Configure the first change node to show the admin page by setting `msg.payload` as `{"pages":{"show":["Admin View"]}}`, and the second change node to hide the admin page by setting the payload as: `{"pages":{"hide":["Admin View"]}}`. !["Screenshot displaying the change node which contains payload to show admin page"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-change-node-for-showing-page.png "Screenshot displaying the change node which contains payload to show admin page"){dataZoomable=""} !["Screenshot displaying the change node which contains payload to hide admin page"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-change-node-for-hidding-page.png "Screenshot displaying the change node which contains payload to display admin page"){dataZoomable=""} 5. Connect the first change node's input to the switch node's first output and the second change node's input to the switch node's second output. 6. Drag a ui-control widget onto the canvas, then click on it and select ui-base which includes all your pages including the admin page. 7. Finally, connect both change node’s outputs to the ui-control’s input. ## Hidding Admin only page by default To hide an admin-only page by default to ensure regular users don't accidentally land on the admin-only page the following steps are needed. 1. Go to the Dashboard 2.0 sidebar, and select the layout tab. 2. Locate the admin-only page and click on the edit icon next to it. 3. Set visibility as "hidden". !["Screenshot displaying admin-only page configuration"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-admin-only-page-configuration.png "Screenshot displaying admin-only page configuration"){dataZoomable=""} ## Deploying the flow !["Screenshot displaying the FlowFuse Editor with flow of admin-only page"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-flowfuse-editior.png "Screenshot displaying the FlowFuse Editor with flow of admin-only page"){dataZoomable=""} 1. With your flow updated to include the above, click the "Deploy" button in the top-right of the Node-RED Editor. 2. Navigate to `https://.flowfuse.cloud/dashboard`. 3. When you visit the page for the first time, you'll need to log in with your FlowFuse username and password or through Single-Sign on. Now, if your username is added to the list of admin usernames stored in the global context, you will be able to see the admin-only page. !["Screenshot displaying the Dashboard view of normal users"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-dashboard-view-for-normal-users.png "Screenshot displaying the Dashboard view of normal users"){dataZoomable=""} !["Screenshot displaying the Dashboard view of admin users"](https://flowfuse.com/blog/2024/04/images/building-admin-panel-node-red-dashboard-2-dashboard-view-for-admin-users.png "Screenshot displaying the Dashboard view of admin users"){dataZoomable=""} ## Next step If you want to learn more about FlowFuse multiuser addon and personalize the multiuser dashboard. we do have many other resources, please refer to them to learn more. - [Webinar](https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/) - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it. - [Personalised Multi-user Dashboards with Node-RED Dashboard 2.0](https://flowfuse.com/blog/2024/01/dashboard-2-multi-user/) - This article explores the process of building multi-user Dashboards secured with FlowFuse Cloud. - [Displaying logged-in users on Dashboard 2.0](https://flowfuse.com/blog/2024/04/displaying-logged-in-users-on-dashboard/) - This detailed guide demonstrates how to display logged-in users on Dashboard 2.0 which using the FlowFuse Multiuser addon and FlowFuse. - [Multi-User Dashboard for Ticket/Task Management](https://flowfuse.com/blueprints/flowfuse-dashboard/multi-user-dashboard/) blueprint, which allows you to utilize templates to develop Personalize multi-user dashboard quickly. # Dashboard 2.0: Milestones, PWA and New Components With a new release of Node-RED Dashboard 2.0 we have plenty of new fixes and improvements being added to the project. In this post, we'll deep dive into community contributions, PWA support, new Vuetify components, and the rest of the great work published in this latest release. ## Community Contributions We firstly wanted to take this opportunity to point out a big milestone that we're very proud to see in Node-RED Dashboard 2.0. This release marks the first time we've had more contributions in a single release from the community, than from FlowFuse employees. I think this is a testament to the community, and a big milestone in validating the success, and popularity, of Dashboard 2.0 in the wider Node-RED community. So thank you very much [@BartButenaers](https://github.com/bartbutenaers){rel=""nofollow""}, [@Ek1nox](https://github.com/Ek1nox){rel=""nofollow""}, [@fullmetal-fred](https://github.com/fullmetal-fred){rel=""nofollow""} and [@cgjgh](https://github.com/cgjgh){rel=""nofollow""} for for great efforts and initiative in improving Node-RED Dashboard 2.0. For anyone else that's interested in contributing to the project, please do reach out, and we'll be happy to help you get started. We also have a [Contributing Guide](https://dashboard.flowfuse.com/contributing/){rel=""nofollow""} if you want to dive straight in. ## Progressive Web App (PWA) Support The biggest community contribution we saw in this release was the addition of Progressive Web App (PWA) support. This feature was added by [@cgjgh](https://github.com/cgjgh){rel=""nofollow""}, and allows you to install your Node-RED Dashboard 2.0 applications directly onto your platform, including Windows, iOS and Android. This work will give your Dashboard's a much more native/natural feel when running on your own machines, and mean you no longer need to go via your browser to access your applications. ## New Vuetify (Preview) Components Available Vuetify is the component library on which most of our Dashboard 2.0 components are built. Our core widgets implement the more fundamental UI elements, but that doesn't stop you from building out fully customized interfaces yourself using our [ui-template node](https://dashboard.flowfuse.com/nodes/widgets/ui-template.html){rel=""nofollow""} with the vast collection of Vuetify components. Within the `ui-template` node, we natively support any of the [core Vuetify components](https://vuetifyjs.com/en/components/all/#containment){rel=""nofollow""}, but Vuetify itself is always evolving and often they release components into their [Vuetify Labs](https://vuetifyjs.com/en/labs/introduction/#what-is-labs){rel=""nofollow""}. In their latest releases, a few of the new components have caught our eye as we've seen them regularly requested in Dashboard 2.0. As such, we've now made available the following Vuetify components inside a `ui-template` node: #### Number Input ([docs](https://vuetifyjs.com/en/components/number-inputs/#installation){rel=""nofollow""}) ![Number Input](https://flowfuse.com/blog/2024/04/images/vuetify-numeric.png)*An example v-number-input from Vuetify's component library* We do have [plans](https://github.com/FlowFuse/node-red-dashboard/issues/41){rel=""nofollow""} for this to become a core widget, and will likely introduce this sooner, rather than later, however, in the mean time, you can now use the `v-number-input` component in a `ui-template` node to create your own number inputs instead. ```html ``` #### Sparkline ([docs](https://vuetifyjs.com/en/components/sparklines/#installation){rel=""nofollow""}) ![Sparkline](https://flowfuse.com/blog/2024/04/images/vuetify-sparkline.png)*An example v-sparkline rendering the output from a ui-slider* Sparklines are a great way to visualize data trends in a small space, and we've seen them requested a few times in the past. Now you can use the `v-sparkline` component in a `ui-template` node to create your own sparklines. This will also likely become a standalone node at some point too, possibly as a third-party widget, but for now implementing into a `ui-template` is very straight forward. In the following example `ui-template`, we append any incoming `msg.payload` to a `value` array and render the sparkline accordingly. ```html ``` There is no limitation on *where* you can use the sparkline either, we could, for example, add it to a `v-data-table` to show a sparkline of a particular feature for each row in the table: ![Data Table with Sparkline](https://flowfuse.com/blog/2024/04/images/vuetify-data-table-sparkline.png)*An example v-data-table that renders a v-sparkline on each row* Here we see the corresponding template for the above `v-data-table` example: ```html ``` #### Treeview ([docs](https://vuetifyjs.com/en/components/treeview/#installation){rel=""nofollow""}) ![Treeview](https://flowfuse.com/blog/2024/04/images/vuetify-treeview.png)*An example v-treeview from Vuetify Lab's component library* The `v-treeview` component is a great way to visualize hierarchical data in a tree-like structure. We've seen this requested a few times in the past, and now you can use the `v-treeview` component in a `ui-template` node to create your own treeviews. There is still scope for this to, one day, become a core or third party widget, but in the mean time, it's very easy to get this up and running in a `ui-template` node. The Treeview example, and other examples above are available in this sample flow: :iframe{allow="clipboard-read; clipboard-write" height="340px" src="https://flows.nodered.org/flow/0ac4d82aaf97409cb0dce9812cfa214c/share?height=300" style="border: none;" width="100%"} ## Other Highlights Whilst the above are the main highlights of this release, we've also had a number of other smaller improvements and fixes that have been added to the project. These include: - UI Radio Group - Dynamic radio options in [#765](https://github.com/FlowFuse/node-red-dashboard/pull/765){rel=""nofollow""} - UI Notification - Notification output & output msg when button clicked in [#766](https://github.com/FlowFuse/node-red-dashboard/pull/766){rel=""nofollow""} - UI Dropdown - Clear dropdown selection in [#775](https://github.com/FlowFuse/node-red-dashboard/pull/775){rel=""nofollow""} - UI Button - Add "Emulate Click" option in [#783](https://github.com/FlowFuse/node-red-dashboard/pull/783){rel=""nofollow""} You can see the full list of changes in the [1.8.0 Release Notes](https://github.com/FlowFuse/node-red-dashboard/releases/tag/v1.8.0){rel=""nofollow""}. ## Follow our Progress New features and improvements are coming to Node-RED Dashboard 2.0 every week, if you're interested in what we have lined up, or want to contribute yourself, then you can track the work we have lined up on our GitHub Projects: - [Dashboard 2.0 Activity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/1){rel=""nofollow""} - [Dashboard 2.0 Planning Board](https://github.com/orgs/FlowFuse/projects/15/views/4){rel=""nofollow""} - [Dashboard 1.0 Feature Parity Tracker](https://github.com/orgs/FlowFuse/projects/15/views/5){rel=""nofollow""} If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant [GitHub repository](https://github.com/FlowFuse/node-red-dashboard){rel=""nofollow""}. # Displaying logged in user on Node-RED Dashboard 2.0 (2026) About a month ago, a powerful solution became available to the Node-RED community to deal with users and allow multiple to interact with the same dashboard in a personalized manner. It's called the Multli user Dashboard for Node-RED. In this guide, we will provide a step-by-step guide to show you how to secure your dashboard and access and display logged in user information on Dashboard 2.0. If you're new to Dashboard 2.0, refer to our blog post [Getting Started with Dashboard 2.0](https://flowfuse.com/blog/2024/03/dashboard-getting-started/) ## Enabling FlowFuse User Authentication Before we display logged-in user data on the dashboard, first we need to set up a login mechanism with FlowFuse for the dashboard. This simplifies securing Node-RED Dashboards and provides contextual user data within the Dashboard itself for who is logged in. 1. Navigate to the Instance "settings". 2. Select the "Security" tab. 3. Enable “FlowFuse User Authentication” Now, the first time you visit the dashboard, you'll need to log in with your registered FlowFuse username and password !["Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security."](https://flowfuse.com/blog/2024/04/images/displaying-logged-in-user-flowfuse-instance-setting.png "Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security."){dataZoomable=""} ## Exploring the FlowFuse User Addon The FlowFuse User Addon is a plugin developed for Dashboard 2.0, leveraging the FlowFuse API to retrieve information about logged in user. ### Installing Flowfuse user addon :cta-image{alt="Wenco deploys new dashboard pages in days with FlowFuse - book a demo" cta="demo" src="https://flowfuse.com/images/cta/wenco-book-demo.png"} 1. Click the Node-RED Settings (top-right) 2. Click "Manage Palette" 3. Switch to the "Install" tab 4. Search for `@flowfuse/node-red-dashboard-2-user-addon` 5. Click "Install" ### How it Works In this addon, user information is attached to the `msg` emitted by Dashboard 2.0 nodes. This user information object is attached as `msg._client.user`. Below is an example of how that object looks: ```text { "userId": "", // unique identifier for the user "username": "", // FlowFuse Username "email": "", // E-Mail Address connected to their FlowFuse account "name": "", // Full Name "image": "" // User Avatar from FlowFuse } ``` Behind the scenes, the user addon is appending the user object to the `msg`, via the SocketIO auth option. We make the socketio object available via a computed [setup](https://dashboard.flowfuse.com/contributing/guides/state-management.html#setup-store){rel=""nofollow""} object, this means that we can also access user data in any ui-template widget with `{{ setup.socketio.auth.user }}`, in the `