Power¶
Overview¶
Power supply and battery monitoring for devices with mains power, battery backup, and UPS capabilities. Tracks AC power status, battery health, charging state, and power-related faults.
- Added in: PQ Framework 2.0
- Namespace:
Pq.Adapters.Framework
When to Use¶
- Devices with mains power supply
- Battery backup monitoring
- UPS-powered equipment
- Power fault detection
- Battery health tracking
- Critical power notifications
States¶
Power Supply States¶
| State | Status String | Description |
|---|---|---|
PowerOk |
pq.state.supervision.power.ok |
Operating on mains power (normal) |
PowerFault |
pq.state.supervision.power.fault |
Mains power failure detected (also entered when running on battery backup) |
Battery States¶
| State | Status String | Description |
|---|---|---|
BatteryCharging |
pq.state.supervision.battery.charging |
Battery charging in progress |
BatteryLow |
pq.state.supervision.battery.low |
Battery level low |
BatteryCritical |
pq.state.supervision.battery.critical |
Battery level critical (imminent shutdown) |
BatteryFault |
pq.state.supervision.battery.fault |
Battery malfunction detected |
Actions¶
Power Supply Events¶
AcFault(timestamp)
- Description: Mains power failure detected
- Target State:
PowerFault - Events:
pq.event.energy.power.failure(Critical) - Usage: AC power lost, device switching to battery
AcRestored(timestamp)
- Description: Mains power restored
- Target State:
PowerOk - Events:
pq.event.energy.power.restored(Info) - Usage: AC power returned, device on mains
BatteryBackup(timestamp)
- Description: Running on battery backup
- Target State:
PowerFault - Events:
pq.event.energy.ups.active(Warn) - Usage: Device switched to battery after AC failure
Battery Events¶
BatteryFault(timestamp)
- Description: Battery malfunction detected
- Target State:
BatteryFault - Events:
pq.event.technical.battery.fault(Critical) - Usage: Battery not charging, disconnected, or hardware fault
BatteryLow(timestamp)
- Description: Battery level low
- Target State:
BatteryLow - Events:
pq.event.technical.battery.low(Warn) - Usage: Battery capacity below low threshold (typically 20-30%)
BatteryCritical(timestamp)
- Description: Battery level critical
- Target State:
BatteryCritical - Events:
pq.event.technical.battery.critical(Critical) - Usage: Battery capacity critically low (typically <10%), imminent shutdown
BatteryNormal(timestamp)
- Description: Battery condition restored to normal
- Target State:
PowerOk - Events:
pq.event.technical.battery.normal(Info) - Usage: Battery charged, fault cleared, or condition restored
Properties¶
None. All power monitoring configuration is device-specific. Define properties in adapter-registration.yaml if needed (e.g., battery_low_threshold, power_monitoring_enabled).
YAML Example¶
device_types:
- type_id: AccessController
name: "Access Control Panel"
functions:
ConnectionBase:
Door:
Power: # power monitoring
Tamper:
properties:
battery_low_threshold:
type: "int"
default: 30
range: [10, 50]
description: "Battery low threshold percentage"
battery_critical_threshold:
type: "int"
default: 10
range: [5, 20]
description: "Battery critical threshold percentage"
- type_id: IntrusionPanel
name: "Intrusion Alarm Panel"
functions:
Partition:
Power: # panel power monitoring
Tamper:
properties:
power_monitoring_enabled:
type: "bool"
default: true
description: "Enable power supply monitoring"
- type_id: NetworkReader
name: "Network-Connected Reader"
functions:
ConnectionBase:
Reader:
Power: # PoE/local power monitoring
Code Usage¶
Basic Power Monitoring¶
public class AccessPanelThing : Thing
{
public PowerFunction Power { get; }
protected override async Task HandleDeviceEvent(PanelEvent evt, CancellationToken ct)
{
switch (evt.Type)
{
case PanelEventType.PowerLoss:
await Power.AcFault(evt.Timestamp);
await Power.BatteryBackup(evt.Timestamp);
break;
case PanelEventType.PowerRestored:
await Power.AcRestored(evt.Timestamp);
break;
case PanelEventType.BatteryLow:
await Power.BatteryLow(evt.Timestamp);
break;
case PanelEventType.BatteryNormal:
await Power.BatteryNormal(evt.Timestamp);
break;
}
}
}
Battery Level Monitoring with Thresholds¶
public class UPSBackedDeviceThing : Thing
{
public PowerFunction Power { get; }
private const int LowThreshold = 30;
private const int CriticalThreshold = 10;
private int _lastBatteryLevel = 100;
protected override async Task HandleDeviceEvent(DeviceEvent evt, CancellationToken ct)
{
if (evt.Type == DeviceEventType.BatteryLevel)
{
var batteryLevel = evt.BatteryPercentage;
// Detect threshold crossings
if (batteryLevel <= CriticalThreshold && _lastBatteryLevel > CriticalThreshold)
{
await Power.BatteryCritical(evt.Timestamp);
}
else if (batteryLevel <= LowThreshold && _lastBatteryLevel > LowThreshold)
{
await Power.BatteryLow(evt.Timestamp);
}
else if (batteryLevel > LowThreshold && _lastBatteryLevel <= LowThreshold)
{
await Power.BatteryNormal(evt.Timestamp);
}
_lastBatteryLevel = batteryLevel;
}
}
}
Complete Power State Machine¶
public class IntrusionPanelThing : Thing
{
public PowerFunction Power { get; }
private bool _isOnMains = true;
private bool _isBatteryHealthy = true;
protected override async Task HandleDeviceEvent(PanelEvent evt, CancellationToken ct)
{
switch (evt.Type)
{
case PanelEventType.AcPowerLost:
_isOnMains = false;
await Power.AcFault(evt.Timestamp);
if (_isBatteryHealthy)
await Power.BatteryBackup(evt.Timestamp);
break;
case PanelEventType.AcPowerRestored:
_isOnMains = true;
await Power.AcRestored(evt.Timestamp);
break;
case PanelEventType.BatteryFault:
_isBatteryHealthy = false;
await Power.BatteryFault(evt.Timestamp);
break;
case PanelEventType.BatteryRestored:
_isBatteryHealthy = true;
await Power.BatteryNormal(evt.Timestamp);
break;
case PanelEventType.BatteryLow:
if (!_isOnMains)
await Power.BatteryLow(evt.Timestamp);
break;
case PanelEventType.BatteryCritical:
if (!_isOnMains)
await Power.BatteryCritical(evt.Timestamp);
break;
}
}
}
Polling-Based Power Monitoring¶
public class PollingDeviceThing : Thing
{
public PowerFunction Power { get; }
private readonly Timer _pollTimer;
private bool _lastMainsState = true;
private int _lastBatteryLevel = 100;
public PollingDeviceThing()
{
_pollTimer = new Timer(PollPowerStatus, null,
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
}
private async void PollPowerStatus(object? state)
{
try
{
var status = await QueryPowerStatusAsync();
// Check mains power change
if (status.MainsPower != _lastMainsState)
{
if (status.MainsPower)
await Power.AcRestored(DeviceTimestamp.UtcNow);
else
await Power.AcFault(DeviceTimestamp.UtcNow);
_lastMainsState = status.MainsPower;
}
// Check battery level change
if (!status.MainsPower && status.BatteryLevel != _lastBatteryLevel)
{
if (status.BatteryLevel <= 10 && _lastBatteryLevel > 10)
await Power.BatteryCritical(DeviceTimestamp.UtcNow);
else if (status.BatteryLevel <= 30 && _lastBatteryLevel > 30)
await Power.BatteryLow(DeviceTimestamp.UtcNow);
else if (status.BatteryLevel > 30 && _lastBatteryLevel <= 30)
await Power.BatteryNormal(DeviceTimestamp.UtcNow);
_lastBatteryLevel = status.BatteryLevel;
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Power status poll failed");
}
}
}
Power Loss Notification with Graceful Shutdown¶
public class CriticalSystemThing : Thing
{
public PowerFunction Power { get; }
private CancellationTokenSource? _shutdownCts;
protected override async Task HandleDeviceEvent(DeviceEvent evt, CancellationToken ct)
{
if (evt.Type == DeviceEventType.BatteryCritical)
{
await Power.BatteryCritical(evt.Timestamp);
Logger.LogCritical("Battery critical - initiating graceful shutdown");
// Begin graceful shutdown sequence
_shutdownCts = new CancellationTokenSource(TimeSpan.FromMinutes(2));
await InitiateGracefulShutdown(_shutdownCts.Token);
}
else if (evt.Type == DeviceEventType.AcRestored)
{
// Cancel shutdown if power restored
_shutdownCts?.Cancel();
_shutdownCts = null;
await Power.AcRestored(evt.Timestamp);
Logger.LogInformation("Power restored - shutdown cancelled");
}
}
}
Notes¶
- Critical Events:
AcFault,BatteryFault, andBatteryCriticalare critical severity events - State Transitions: Typical flow:
PowerOk→PowerFault(on AC loss / battery backup) →BatteryLow→BatteryCritical, returning toPowerOkonAcRestored/BatteryNormal - No Separate Backup State:
BatteryBackup()reports the device is running on battery but still maps to thePowerFaultstate - there is no distinct battery-backup state - Battery Restoration:
BatteryNormal()transitions back toPowerOkand publishespq.event.technical.battery.normal- use it to clearBatteryLoworBatteryCriticalonce the battery recharges - Circuit Faults Are Not Power Actions: the taxonomy carries
pq.event.energy.power.overload/pq.event.energy.power.overload.restored(a circuit drawing more current than it may, and its clearing) andpq.event.energy.power.fuse.failed/pq.event.energy.power.fuse.restored(a failed and a replaced fuse). The Power function has no action for them - a device that supervises output current or fuses declares these events under its device type'sevents:list and publishes them from the Thing. Keep the two branches apart: an overload is a condition on a live circuit, a blown fuse is a failed component - Threshold Configuration: Battery thresholds are adapter-specific - define in
adapter-registration.yaml - Polling vs Events: Some devices report power events, others require polling - implement appropriate pattern
- Combined Monitoring: Often paired with
TamperandConnectionBasefunctions for complete device health monitoring
See Also¶
- Functions: Tamper - Physical tamper detection
- Functions: ConnectionBase - Device connectivity monitoring