Early beta (0.1.0), not yet for critical processes. See the roadmap
PNeX logo

Predictive maintenance

Anomaly detection and threshold-breach forecasting on any measurement, inside a flow

Two flow nodes turn any numeric measurement into early warnings, with no model to train and no data science stack:

  • Anomaly detection flags values that do not look like the recent behaviour of the series — a spike, a drop, a change of regime — without a fixed threshold.
  • Forecast extrapolates the series and tells when it will cross a threshold: bearing temperature, filter pressure drop, battery voltage, tank level.

Both run in-process in the flow runtime (pure Rust), keep one history per series in Valkey (it survives redeploys), and expose a boolean output made to drive a Notification.

This page builds the complete example below on simulated data, so it works without any hardware: a pump whose vibration spikes every 90 s and whose bearing slowly heats towards 80 °C.

Predictive maintenance flow: simulated pump, anomaly detection, forecast, ntfy alerts and metrics

1. Simulate the measurements

No sensor at hand? A function can generate realistic series. An Inject node without payload emits the current timestamp every second; the function turns it into two measurements, one per output port:

// @output vibration number "Pump vibration (mm/s): noise, with a spike every 90 s"
// @output bearing_temp number "Bearing temperature (°C): slow drift from 55 to 85 °C over 30 min"
function handle(inputs, msg) {
  // Simulated sensors driven by the inject timestamp (ms).
  const t = Math.floor(Number(msg.payload) / 1000);
  const spike = t % 90 < 2 ? 9 : 0;
  const vibration = 2 + (Math.random() - 0.5) * 0.6 + spike;
  // 30-minute cycle: the bearing heats up, then is "replaced".
  const bearingTemp = 55 + (t % 1800) / 60 + (Math.random() - 0.5) * 0.4;
  return {
    vibration: Math.round(vibration * 100) / 100,
    bearing_temp: Math.round(bearingTemp * 100) / 100,
  };
}

The simulated pump function with its two output ports

With a real device, replace the inject + function by a Device (read) node on the pin, or any node producing a number.

2. Detect anomalies

Wire vibration into Anomaly detection. The node warms up (30 samples by default), then scores every value against the window of its series.

Anomaly detection settings

MethodDetectsWhen to use it
Outlier (robust z-score)Isolated spikes and dropsThe safe default: no model, not fooled by the outliers it looks for
Out of forecast bandValues outside the predicted bandSeries with a trend or a daily cycle (set the season)
Regime changeA lasting change of level or variabilityAfter a failure, an intervention, a setting change — fires once per change

Outputs: detail (value, score, expected band) and anomaly (true / false). In the example, each vibration spike scores around 38 for a threshold of 3.5; the noise never crosses it.

3. Forecast the breach

Wire bearing_temp into Forecast, pick the model and set the threshold.

Forecast settings: linear trend, 120-sample window, 1000-step horizon, threshold 80

SettingExampleWhy
ModelLinear trendSlow drift and wear. Exponential smoothing suits series with seasonality
Window120 samplesRecent history used for the fit — shorter adapts faster after a maintenance
Horizon1000 stepsHow far ahead to look; one step = the usual interval between samples
Threshold / Breach when80, aboveThe level to watch

Outputs: detail (forecast points with confidence bands, breach time), breach (true when the threshold is predicted to be crossed within the horizon) and eta (s) (seconds before the predicted crossing).

In the example, at 66.7 °C and +1 °C per minute the node predicts the breach in 791 s — the exact answer is 800 s — and the ETA then counts down with the clock.

The horizon decides how early you are warned. The default (48 steps) only looks 48 s ahead on a series sampled every second: raise it for slow phenomena, or sample less often (a Forecast fed once a minute with a 1000-step horizon looks 16 hours ahead).

4. Alert on the phone

Wire the boolean outputs to the trigger of a Notification node, and the values to the template variables:

  • anomaly → trigger of Vibration spike alert, vibration → its vibration variable;
  • breach → trigger of Bearing breach alert, eta (s) → a small function that rounds it to minutes → the minutes variable of the template Bearing temperature forecast to reach 80 °C in about {{ minutes }} min.

Notification node: ntfy channel, template, anti-spam 1 message per 10 min

Set the anti-spam (here 1 message per 10 minutes): a forecast stays in breach for as long as the trend holds.

Screenshot coming soon

public/screenshots/v0.1/v0.1/en/phone/ntfy-predict.webp — capture scenario: scripts/capture/run.mjs

The ntfy push on the phone

5. Watch it on a dashboard

Add Metric nodes on the measurements and on the ETA: they appear as sources flow_<id> in the dashboards — gauge for the temperature, value for the predicted breach, mini charts for the drift and the spikes.

Dashboard: bearing temperature, predicted breach, vibration and their history

Good to know

  • One series per message topic: a single node can watch several measurements if their messages carry different topics.
  • History survives redeploys (Valkey, kept 30 days).
  • Numbers only: booleans count as 1/0; an object payload is read through the Value key setting. Anything else is rejected, never guessed.
  • The node badge and the Debug node show the latest result; a long horizon makes the detail large (one point per step), so the Debug node shows a truncated preview.

On this page