Skip to content

Sensor Calibration

Observium can automatically calculate and apply sensor alert thresholds based on observed operating data. Rather than relying on vendor defaults or manual guesswork, calibration analyses historical RRD data for each sensor and sets thresholds that reflect real-world behaviour, with a configurable safety margin.

Calibration can be scheduled automatically for eligible sensors, applied manually from the web UI, or run from the command line.

Subscription Feature

Sensor auto-calibration is included in the Subscription Edition of Observium.


How Thresholds Are Calculated

Calibration analyses the sensor's RRD data over a historical period (default: 7 days). Raw minimum, maximum, average, and standard deviation values are retained for diagnostics, but automatic threshold placement uses robust percentile baselines by default:

  • Lower baseline: 1st percentile (percentile_low)
  • Upper baseline: 99th percentile (percentile_high)

Using percentiles prevents one isolated spike or corrupt sample from permanently widening the learned operating band. The configured margin is then placed outside those baselines:

Threshold Description
High Critical upper_baseline + margin
High Warning upper_baseline + margin/2
Low Warning lower_baseline - margin/2
Low Critical lower_baseline - margin

Warning and critical values are rounded as an ordered pair. If rounding or a physical boundary leaves no room for both values on one side, that side is disabled instead of storing equal, reversed, or impossible thresholds.

Threshold Direction

Calibration can operate in one of three directions:

  • both: set both high and low thresholds
  • high: set only high warning and high critical thresholds
  • low: set only low warning and low critical thresholds

If you do not explicitly choose a direction, Observium uses the default direction for the sensor class. For example, many classes such as temperature, current, power, load, and capacity default to high, while humidity, voltage, and fanspeed default to both, and runtime defaults to low.

This means that some sensors will not receive all four thresholds unless you explicitly override the direction in the UI or CLI.

Adaptive Margin

The margin is calculated per sensor class. For most classes it is the larger of two values:

  • Range margin: a percentage of the observed operating range (max - min)
  • Adaptive minimum: a percentage of the historical sensor magnitude, which prevents vanishingly small margins on very stable sensors

This ensures that a sensor with a 1 C variation over 7 days still gets a meaningful threshold gap, rather than thresholds separated by fractions of a degree.

Some sensor classes such as dbm, snr, attenuation, and sound use fixed absolute minimums instead, because percentage-of-value margins are not useful on logarithmic scales.

Thresholds are rounded before being written so that the stored values match the scale of the sensor.

Physical Bounds

Calibration will not create thresholds outside known physical ranges:

Sensor class Bounds
temperature Low side cannot be below absolute zero (-273.15 C)
fanspeed, frequency Low side cannot be below zero
humidity, capacity, load Thresholds are limited to 0-100

Automatic calibration also rejects historical baselines outside these ranges. A baseline that touches a physical limit and leaves no warning/critical headroom is not applied automatically.


Automatic Calibration Scheduling

Auto-calibration scheduling is controlled by $config['sensors']['auto_calibration']['mode']:

  • none: disable automatic scheduling
  • all: schedule all sensors whose classes are in auto_classes
  • auto_only (default): schedule eligible sensors only when discovery identified their original thresholds as automatic or default

When auto-calibration scheduling is enabled, sensors are scheduled for calibration after a learning period (default: 7 days). The process is:

  1. Sensor is discovered and auto_calibrate_after is set to now + learning_days
  2. Each later discovery run checks whether the learning period has elapsed
  3. Once the date is reached, calibration runs during discovery
  4. On success, thresholds are applied and the sensor is marked as auto-calibrated
  5. Auto-calibrated thresholds are protected from later discovery updates

By default, only newly discovered sensors are enrolled. Set schedule_existing to TRUE to enroll existing unscheduled sensors that still qualify. Jobs that were already scheduled continue normally regardless of this setting.

When upgrading an installation with already-scheduled sensors, discovery records whether each sensor's thresholds were automatic before auto_only permits the pending calibration. Missing provenance fails safe until that discovery pass.

Note

Automatic calibration is skipped for ignored or deleted sensors, sensors with manual thresholds, classes not listed in auto_classes, and classes listed in skip_classes.

Each discovery process attempts at most max_per_discovery due calibrations (default: 5), with no more than max_per_device attempts for one device (default: 1). These bounds prevent a backlog from making discovery unbounded and keep one device from consuming a larger process-wide budget. If calibration fails transiently, Observium retries once per day up to max_retries times before giving up. A log event is recorded when a sensor is abandoned.

Automatic Data-Quality Gates

Before an unattended threshold write, Observium requires:

  • At least min_coverage known RRD data over the learning window (default: 80%)
  • Historical samples spanning at least min_span of that window (default: 80%)
  • A latest sample no older than max_data_age seconds (default: 1800)
  • A sensor that is enabled and currently in the ok state
  • A non-zero usable baseline with valid physical values and threshold headroom
  • A current value that remains inside the proposed threshold band

Failures are recorded as scheduler reasons and retried when transient. Class exclusions, manual thresholds, and auto_only ineligibility are permanent skips.


Manual Calibration via the Web UI

Single Sensor

For users with admin-level access, the current thresholds on sensor tables are clickable. Clicking the thresholds opens the calibration modal, which shows:

  • Historical statistics for the selected period: minimum, maximum, average, and range
  • A graph with current and proposed thresholds overlaid
  • A threshold comparison table showing current, proposed, and delta values
  • The calculated margin and how it was derived

You can preview the proposed thresholds before applying them. The preview displays data-quality warnings and validates threshold ordering. An invalid proposal has no Apply action; warnings that do not invalidate the thresholds remain advisory for manual calibration.

Applying calibration from the modal marks the sensor's thresholds as manual, which prevents future auto-calibration from changing them.

If the sensor already has custom or manual thresholds, the modal still opens and applying calibration replaces the current thresholds with the newly calculated values.

Bulk Calibration

The Calibration view, accessible from the sensor table navbar or the health page navbar, shows all sensors for a device or globally, with checkboxes for bulk selection.

From the calibration view you can:

  • Select individual sensors using row checkboxes, use the table header checkbox for select all or none, or use Select Problems
  • Choose the historical period to use: 7 days, 30 days, or 90 days
  • Choose direction and margin preset for the preview and apply actions
  • Click Preview Selected to open a preview modal showing proposed thresholds for all selected sensors, with per-sensor Apply buttons
  • Click Apply Selected to apply calibration to all selected sensors with a progress indicator

Command Line Tool

The sensor-calibrate.php script provides preview and apply workflows suitable for scripting and scheduled tasks.

Text Only
USAGE:
  sensor-calibrate.php [OPTIONS]

OPTIONS:
  --help                     Show this help
  -h <device>                Calibrate sensors for specific device ID or hostname
  -s <sensor_id>             Calibrate specific sensor ID
  -c <sensor_class>          Calibrate sensors of specific class
  -p <period>                Historical period to analyse (default: 7d)
  -d <direction>             Threshold direction override: both, high, low
  -m <preset>                Margin preset: tight (0.5x), normal (1.0x), loose (2.0x)
  -a                         Apply calculated thresholds (default: preview only)
  -v                         Verbose output
  -n                         No prompts (non-interactive mode)

If -d is omitted, the script uses the default direction for the sensor class. Use -d both if you want to force both high and low thresholds for a class that normally calibrates only one side.

Examples:

Bash
# Preview calibration for all sensors on device 1
php sensor-calibrate.php -h 1

# Preview using hostname
php sensor-calibrate.php -h switch01.example.com

# Apply calibration to a single sensor
php sensor-calibrate.php -s 123 -a

# Preview all temperature sensors using 30 days of data
php sensor-calibrate.php -c temperature -p 30d

# Apply fan speed calibration for a device non-interactively
php sensor-calibrate.php -h 1 -c fanspeed -a -n

# Apply high-only thresholds with loose margins
php sensor-calibrate.php -s 123 -d high -m loose -a

# Force both sides for a class that normally defaults to high-only
php sensor-calibrate.php -c temperature -d both -a

Without -a, the script only previews proposed thresholds without modifying anything. This is safe to run at any time for review.

Applying with -a marks the thresholds as manual, just like applying from the web UI.


Configuration

All auto-calibration settings live under $config['sensors']['auto_calibration'] in config.php.

Global Settings

Option Default Description
mode 'auto_only' Auto-calibration scheduler mode: none, all, auto_only
learning_days 7 Days to observe a new sensor before calibrating
schedule_existing FALSE Enroll existing unscheduled sensors; opt in to avoid an upgrade backlog
auto_classes ['temperature', 'voltage', 'humidity', 'fanspeed', 'frequency', 'load'] Classes eligible for unattended calibration
skip_classes ['state'] Sensor classes to never auto-calibrate
max_retries 30 Maximum retry attempts before giving up on a failed calibration
max_per_discovery 5 Maximum due calibration attempts in one discovery process
max_per_device 1 Maximum due calibration attempts for one device in a discovery process
analysis_width 2500 Maximum RRD graph width used to bound analysis work
percentile_low 1 Percentile used for the robust lower baseline
percentile_high 99 Percentile used for the robust upper baseline
min_coverage 0.80 Minimum fraction of known RRD data required automatically
min_span 0.80 Minimum fraction of the learning window spanned by data
max_data_age 1800 Maximum age in seconds of the latest historical sample

capacity is excluded from the default allowlist because the class represents both increasing usage and remaining battery capacity, which require opposite alarm directions. Add only definitions whose semantics you have verified.

Example: extend learning, opt existing sensors into a staged rollout, and skip humidity

PHP
1
2
3
4
5
6
7
8
9
$config['sensors']['auto_calibration']['mode']          = 'auto_only';
$config['sensors']['auto_calibration']['learning_days'] = 14;
$config['sensors']['auto_calibration']['schedule_existing'] = TRUE;
$config['sensors']['auto_calibration']['auto_classes']  = [
    'temperature', 'voltage', 'humidity', 'fanspeed',
    'frequency', 'load'
];
$config['sensors']['auto_calibration']['max_per_device'] = 1;
$config['sensors']['auto_calibration']['skip_classes'] = ['state', 'humidity'];

Per-Class Threshold Settings

Margin behaviour is configured per sensor class. The following keys are supported:

Key Description
margin Operating range multiplier for the critical threshold margin
adaptive_min_percent Minimum margin as a percentage of current sensor value
max_margin_ratio Cap margin at this multiple of sensor value
fallback_min Absolute minimum margin
min_high Fixed absolute minimum for high-threshold margins
min_low Fixed absolute minimum for low-threshold margins

Default per-class settings:

Class margin adaptive_min_percent Notes
current 20% 5% SI-range adaptive
voltage 20% 5% SI-range adaptive
power / apower / rpower 25% 5% SI-range adaptive
frequency 10% 2% Tight, mains frequency is usually very stable
temperature 50% 5% Wide range margin with 1 degree absolute fallback
fanspeed 30% 3% 100 RPM absolute fallback
humidity / load / capacity 15-20% 3-5% Percentage-based sensors
dbm / snr / attenuation 20% - Fixed absolute minimums
wavelength 10% 1% Very tight, wavelengths are stable
default 20% 5% Applied to unrecognised classes

To override a class, add an entry to config.php:

PHP
1
2
3
4
5
$config['sensors']['auto_calibration']['thresholds']['temperature'] = [
    'margin'               => 0.30,
    'adaptive_min_percent' => 3.0,
    'fallback_min'         => 0.5,
];

Threshold Protection and Recalibration

Calibration respects existing threshold state, but the protection rules are different for automatic and manual workflows:

  • Auto-calibration skips sensors with manual or CLI calibration provenance, and legacy custom limits that have no automatic provenance
  • Thresholds applied via the web UI modal are marked as manual and protected from future auto-calibration
  • Thresholds applied via the CLI tool with -a are also marked as manual
  • Thresholds applied by automatic background calibration are marked as auto-calibrated and protected from later discovery overwrites
  • Each successful calibration records one audit event containing the method and old/new threshold values
  • Manual recalibration from the web UI or CLI can replace existing thresholds, including thresholds that were already custom or manual

If you want a sensor to be eligible for automatic scheduling again, clear its manual protection or calibration state and let discovery schedule it again.


Troubleshooting

Sensor shows no proposed thresholds in the modal

The sensor has insufficient RRD data for the selected period, or its RRD file does not yet exist. Try a shorter period or wait for more polling data to accumulate.

Auto-calibration never fired for a new sensor

Check that $config['sensors']['auto_calibration']['mode'] is not 'none', the sensor class is present in auto_classes and absent from skip_classes, and discovery is running regularly. In auto_only mode, the sensor must also have been scheduled with discovery-provided automatic threshold provenance. Calibration is triggered during discovery, not polling.

Thresholds seem too wide or too narrow

Adjust the margin and adaptive_min_percent values for the relevant sensor class in config.php. Use a longer period such as -p 30d if you want the thresholds to reflect a broader operating history.

Auto-calibration was abandoned

The sensor failed calibration max_retries times. Common causes are a missing RRD file, insufficient coverage or span, stale data, an abnormal current state, invalid physical values, or a current reading outside the proposed band. Investigate with sensor-calibrate.php -s <id> -v.

Manual calibration from the web UI or CLI still works and applies thresholds immediately, but it does not reschedule the abandoned background job. If you want the scheduler to try again later, reset the sensor's calibration state and let discovery schedule it again after the underlying problem is fixed.