Skip to content

Tamper

Overview

Physical tamper detection for device enclosures, modules, and housings. Detects unauthorized physical access to device internals through tamper switches, seals, or sensors.

  • Added in: PQ Framework 2.0
  • Namespace: Pq.Adapters.Framework

When to Use

  • Device enclosure tamper switches
  • Module/board tamper detection
  • Housing seal monitoring
  • Expansion module tamper sensors
  • Environmental housing intrusion detection

States

Tamper States

State Status String Description
Sealed pq.state.intrusion.tamper.sealed Tamper condition normal, enclosure sealed
Tampered pq.state.intrusion.tamper Tamper detected, enclosure opened or sensor triggered

Actions

Tamper Events

TamperDetected(timestamp)

  • Description: Tamper switch activated (enclosure opened or sensor triggered)
  • Target State: Tampered
  • Events: pq.event.intrusion.tamper (Critical)
  • Usage: Device enclosure opened, tamper switch closed, seal broken

TamperCleared(timestamp)

  • Description: Tamper condition restored (enclosure closed)
  • Target State: Sealed
  • Events: pq.event.intrusion.tamper.cleared
  • Usage: Enclosure properly closed, tamper switch opened, seal restored

Properties

None. All tamper configuration is device-specific. Define properties in adapter-registration.yaml if needed (e.g., tamper_latching, tamper_masking_enabled).

YAML Example

device_types:
  - type_id: AccessController
    name: "Access Control Panel"
    functions:
      ConnectionBase:
      Door:
      Tamper:              # panel enclosure tamper
    properties:
      tamper_latching:
        type: "bool"
        default: false
        description: "True = tamper latches until manually cleared"

  - type_id: ExpansionModule
    name: "I/O Expansion Module"
    functions:
      ConnectionBase:
      Input:
      Output:
      Tamper:              # module housing tamper
    properties:
      tamper_enabled:
        type: "bool"
        default: true
        description: "Enable tamper monitoring for this module"

  - type_id: PIRMotionDetector
    name: "PIR Motion Detector"
    functions:
      IntrusionDetector:
      Tamper:              # detector tamper
      Power:

Code Usage

Basic Tamper Detection

public class AccessPanelThing : Thing
{
    public TamperFunction Tamper { get; }

    protected override async Task HandleDeviceEvent(PanelEvent evt, CancellationToken ct)
    {
        switch (evt.Type)
        {
            case PanelEventType.TamperOpened:
                await Tamper.TamperDetected(evt.Timestamp);
                break;

            case PanelEventType.TamperClosed:
                await Tamper.TamperCleared(evt.Timestamp);
                break;
        }
    }
}

Latching Tamper with Manual Clear

public class IntrusionPanelThing : Thing
{
    public TamperFunction Tamper { get; }
    private bool _tamperLatched = false;

    protected override async Task HandleDeviceEvent(PanelEvent evt, CancellationToken ct)
    {
        if (evt.Type == PanelEventType.TamperDetected)
        {
            _tamperLatched = true;
            await Tamper.TamperDetected(evt.Timestamp);
        }
    }

    public async Task HandleTamperClear(Commands.ClearTamper cmd, CancellationToken ct)
    {
        if (!_tamperLatched)
            return;

        // Send clear command to device
        await SendDeviceCommand(new DeviceCommands.ClearTamperLatch(), ct);

        _tamperLatched = false;
        await Tamper.TamperCleared(DeviceTimestamp.UtcNow);
    }
}

Multi-Zone Tamper Handling

public class ExpansionModuleThing : Thing
{
    public TamperFunction Tamper { get; }

    // Track tamper state for multiple zones
    private readonly HashSet<int> _tamperedZones = new();

    protected override async Task HandleDeviceEvent(ModuleEvent evt, CancellationToken ct)
    {
        switch (evt.Type)
        {
            case ModuleEventType.ZoneTamper:
                _tamperedZones.Add(evt.ZoneNumber);

                // Report tamper if first zone
                if (_tamperedZones.Count == 1)
                    await Tamper.TamperDetected(evt.Timestamp);
                break;

            case ModuleEventType.ZoneTamperRestored:
                _tamperedZones.Remove(evt.ZoneNumber);

                // Clear tamper if all zones restored
                if (_tamperedZones.Count == 0)
                    await Tamper.TamperCleared(evt.Timestamp);
                break;
        }
    }
}

Tamper with Supervision

public class SupervisedDeviceThing : Thing
{
    public TamperFunction Tamper { get; }
    private readonly Timer _supervisionTimer;

    public SupervisedDeviceThing()
    {
        // Periodic supervision check
        _supervisionTimer = new Timer(CheckSupervision, null,
            TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30));
    }

    private async void CheckSupervision(object? state)
    {
        try
        {
            var response = await QueryDeviceSupervision();

            if (response.TamperFault)
            {
                await Tamper.TamperDetected(DeviceTimestamp.UtcNow);
            }
            else if (Tamper.State == TamperFunction.States.Tampered)
            {
                await Tamper.TamperCleared(DeviceTimestamp.UtcNow);
            }
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Supervision check failed");
        }
    }
}

Combined with IntrusionDetector

public class PIRDetectorThing : Thing
{
    public IntrusionDetectorFunction Detector { get; }
    public TamperFunction Tamper { get; }

    protected override async Task HandleDeviceEvent(DetectorEvent evt, CancellationToken ct)
    {
        switch (evt.Type)
        {
            // Motion detection
            case DetectorEventType.MotionDetected:
                await Detector.MotionDetected(evt.Timestamp);
                break;

            case DetectorEventType.MotionCleared:
                await Detector.MotionCleared(evt.Timestamp);
                break;

            // Tamper detection
            case DetectorEventType.TamperOpened:
                await Tamper.TamperDetected(evt.Timestamp);
                break;

            case DetectorEventType.TamperClosed:
                await Tamper.TamperCleared(evt.Timestamp);
                break;

            // Can also report via detector
            case DetectorEventType.TamperAlarm:
                await Detector.Tamper(evt.Timestamp);
                break;
        }
    }
}

Notes

  • Critical Security Event: Tamper detection is a critical security event (severity: Critical)
  • IntrusionDetector Alternative: IntrusionDetector function also has Tamper() and TamperRestored() actions. Use Tamper function for device enclosure monitoring, IntrusionDetector.Tamper() for sensor-level tamper events.
  • Latching Behavior: Some devices latch tamper conditions until manually cleared - implement this in adapter logic
  • Supervision: Tamper monitoring may be integrated with device supervision/health checks
  • Multiple Zones: For devices with multiple tamper zones, report tamper when any zone triggers, clear only when all restore
  • Restoration Event: TamperCleared() publishes pq.event.intrusion.tamper.cleared and transitions back to Sealed

See Also