PHP library for reading data from APC PDU AP8XXX series via SNMP (v1 and v3) or SSH with Network Port Sharing support.
- PHP 8.2+
- One of the following backends:
ext-snmp- PHP SNMP extension (for Native client)net-snmp- System package (for Binary client)freedsx/snmp- Composer package (for FreeDSx client)ext-ssh2- PHP SSH2 extension (for SSH client)
composer require sunfoxcz/apc-pduuse Sunfox\ApcPdu\ApcPduFactory;
use Sunfox\ApcPdu\DeviceMetric;
use Sunfox\ApcPdu\OutletMetric;
$pdu = ApcPduFactory::snmpV1('192.168.1.100', 'public');$pdu = ApcPduFactory::snmpV3(
'192.168.1.100',
'monitor',
'AuthPassphrase',
'PrivPassphrase'
);The library provides multiple SNMP client implementations with automatic discovery:
| Method | Description | Dependencies |
|---|---|---|
snmpV1() / snmpV3() |
Auto-detects best available client | Any of the below |
snmpV1Binary() / snmpV3Binary() |
Uses the snmpget binary via shell |
net-snmp package |
snmpV1FreeDsx() / snmpV3FreeDsx() |
Uses the FreeDSx SNMP library | freedsx/snmp composer package |
snmpV1Native() / snmpV3Native() |
Uses PHP's native SNMP functions | ext-snmp |
ssh() |
Uses SSH/CLI interface | ext-ssh2 |
The snmpV1() and snmpV3() methods automatically detect and use the best
available SNMP client in priority order:
- Binary - Most efficient batch operations (
net-snmppackage) - FreeDSx - Efficient batch operations, pure PHP (
freedsx/snmp) - Native - Fallback, slower batch operations (
ext-snmp)
If no client is available, a NoSnmpClientAvailableException is thrown.
// Auto-detect best available client (recommended)
$pdu = ApcPduFactory::snmpV3($host, $user, $authPass, $privPass);
// Or explicitly choose a specific client:
// Binary - requires net-snmp package (apt install snmp)
$pdu = ApcPduFactory::snmpV3Binary($host, $user, $authPass, $privPass);
// FreeDSx - pure PHP, no extensions required (composer require freedsx/snmp)
$pdu = ApcPduFactory::snmpV3FreeDsx($host, $user, $authPass, $privPass);
// Native - requires ext-snmp PHP extension
$pdu = ApcPduFactory::snmpV3Native($host, $user, $authPass, $privPass);
// SSH - uses CLI commands over SSH (requires ext-ssh2)
$pdu = ApcPduFactory::ssh($host, $sshUser, $sshPass);Benchmark results (SNMPv3, AP8653 PDU):
| Operation | Native | Binary | FreeDSx | SSH |
|---|---|---|---|---|
| Single device metric | 128 ms | 69 ms | 73 ms | 905 ms |
| Device metrics (batch) | 1.26 s | 200 ms | 215 ms | N/A* |
| Single outlet metric | 62 ms | 65 ms | 233 ms | 904 ms |
| Outlet metrics (batch) | 1.09 s | 213 ms | 229 ms | N/A* |
| All 24 outlets | 25.82 s | 7.07 s | 7.45 s | N/A* |
| Full PDU status | 54.74 s | 16.64 s | 13.72 s | N/A* |
*SSH uses an interactive shell which adds latency. It supports only a limited set of metrics (Power, Energy, ApparentPower, PowerFactor, Current, Name) and cannot retrieve full device/outlet status.
Recommendations:
- Native: Good for simple single-metric queries, requires
ext-snmp - Binary: Best batch performance, requires
net-snmpsystem package - FreeDSx: Best for full status dumps, pure PHP with no extension dependencies
- SSH: Alternative when SNMP is unavailable, limited metrics support
Run bin/benchmark to compare performance on your system. Use --ssh flag to
include SSH in the comparison.
// Get individual metrics (PDU 1 is default)
$power = $pdu->getDevice(DeviceMetric::Power); // Returns watts
$peak = $pdu->getDevice(DeviceMetric::PeakPower); // Returns watts
$energy = $pdu->getDevice(DeviceMetric::Energy); // Returns kWh
$name = $pdu->getDevice(DeviceMetric::Name); // Returns string
$loadStatus = $pdu->getDevice(DeviceMetric::LoadStatus); // Returns int (1=Normal, 2=LowLoad, 3=NearOverload, 4=Overload)
$apparentPower = $pdu->getDevice(DeviceMetric::ApparentPower); // Returns VA
$powerFactor = $pdu->getDevice(DeviceMetric::PowerFactor); // Returns ratio (0.0-1.0)
// Get all device metrics at once as DTO
$device = $pdu->getDeviceStatus();
echo $device->powerW; // Current power in watts
echo $device->peakPowerW; // Peak power in watts
echo $device->energyKwh; // Total energy in kWh
echo $device->name; // Device name
echo $device->loadStatus->name; // LoadStatus enum (Normal, LowLoad, NearOverload, Overload)
echo $device->apparentPowerVa; // Apparent power in VA
echo $device->powerFactor; // Power factor (0.0-1.0)
echo $device->outletCount; // Number of outlets
echo $device->phaseCount; // Number of phases// Get individual outlet metrics
$name = $pdu->getOutlet(1, 5, OutletMetric::Name);
$power = $pdu->getOutlet(1, 5, OutletMetric::Power);
$current = $pdu->getOutlet(1, 5, OutletMetric::Current);
$energy = $pdu->getOutlet(1, 5, OutletMetric::Energy);
$state = $pdu->getOutlet(1, 5, OutletMetric::State); // 1=Off, 2=On
// Get all metrics for one outlet as DTO
$outlet = $pdu->getOutletStatus(1, 5);
echo $outlet->name; // Outlet name
echo $outlet->index; // Outlet index
echo $outlet->state->name; // PowerState enum (Off, On)
echo $outlet->currentA; // Current in amps
echo $outlet->powerW; // Power in watts
echo $outlet->peakPowerW; // Peak power in watts
echo $outlet->energyKwh; // Energy in kWh
echo $outlet->outletType; // Outlet type (e.g., "IEC C13")
// Get all outlets for a PDU
$outlets = $pdu->getAllOutlets(1);use Sunfox\ApcPdu\OutletCommand;
// Get an outlet object for control operations
$outlet = $pdu->getOutlet(1, 5); // PDU 1, Outlet 5
// Power control
$outlet->setState(OutletCommand::On); // Turn on
$outlet->setState(OutletCommand::Off); // Turn off
$outlet->setState(OutletCommand::Reboot); // Reboot (off then on)
// Configuration
$outlet->setName('Web Server');
$outlet->setExternalLink('https://example.com/server1');
// Power thresholds (in Watts)
$outlet->setLowLoadThreshold(10);
$outlet->setNearOverloadThreshold(400);
$outlet->setOverloadThreshold(500);// Reset device-level counters
$pdu->resetDevicePeakPower(1); // Reset peak power to current load
$pdu->resetDeviceEnergy(1); // Reset energy meter to zero
// Reset all outlet counters
$pdu->resetOutletsPeakPower(1); // Reset all outlets peak power
$pdu->resetOutletsEnergy(1); // Reset all outlets energy meters// Get complete status for one PDU
$pduInfo = $pdu->getPduInfo(1);
echo $pduInfo->pduIndex;
echo $pduInfo->device->powerW;
foreach ($pduInfo->outlets as $outlet) {
echo $outlet->name . ': ' . $outlet->powerW . 'W';
}
// Get complete dump of all PDUs (stops when PDU not found)
$status = $pdu->getFullStatus();When multiple PDUs are daisy-chained (up to 4 PDUs supported), specify the PDU index as the second parameter:
use Sunfox\ApcPdu\DeviceMetric;
// PDU 1 (Host) - default when no index specified
$hostPower = $pdu->getDevice(DeviceMetric::Power);
$hostPower = $pdu->getDevice(DeviceMetric::Power, 1); // Explicit
// PDU 2 (Guest)
$guest1Power = $pdu->getDevice(DeviceMetric::Power, 2);
// PDU 3 and 4 (additional guests)
$guest2Power = $pdu->getDevice(DeviceMetric::Power, 3);
$guest3Power = $pdu->getDevice(DeviceMetric::Power, 4);
// Get all metrics for specific PDU
$device = $pdu->getDeviceStatus(2); // All metrics for PDU 2if ($pdu->testConnection()) {
echo "PDU is reachable";
}| Metric | Unit | Description |
|---|---|---|
| ModuleIndex | - | Module index |
| PduIndex | - | PDU index |
| Name | string | Device name |
| LoadStatus | LoadStatus | Load status (Normal, LowLoad, NearOverload, Overload) |
| Power | W | Current power consumption |
| PeakPower | W | Peak power since last reset |
| PeakPowerTimestamp | datetime | When peak power occurred |
| PeakPowerStartTime | datetime | When peak power tracking started (last reset) |
| Energy | kWh | Total energy since last reset |
| EnergyStartTime | datetime | When energy tracking started (last reset) |
| ApparentPower | VA | Apparent power |
| PowerFactor | ratio | Power factor (0.0-1.0) |
| OutletCount | - | Number of outlets |
| PhaseCount | - | Number of phases |
| LowLoadThreshold | % | Low load warning threshold |
| NearOverloadThreshold | % | Near overload warning threshold |
| OverloadRestriction | - | Overload restriction setting |
| Metric | Unit | Description |
|---|---|---|
| ModuleIndex | - | Module index |
| PduIndex | - | PDU index |
| Name | string | Outlet name/label |
| Index | int | Outlet index |
| State | PowerState | Power state (Off, On) |
| Current | A | Current draw |
| Power | W | Power consumption |
| PeakPower | W | Peak power |
| PeakPowerTimestamp | datetime | When peak power occurred |
| PeakPowerStartTime | datetime | When peak power tracking started (last reset) |
| Energy | kWh | Total energy |
| EnergyStartTime | datetime | When energy tracking started (last reset)* |
| OutletType | string | Outlet type (e.g., "IEC C13") |
| ExternalLink | string | External link URL |
*Note: EnergyStartTime for outlets is shared across all outlets on a PDU (comes from
device-level rPDU2DeviceStatusOutletsEnergyStartTime OID). When you reset an outlet's
energy, all outlets share the same start time.
use Sunfox\ApcPdu\LoadStatus;
use Sunfox\ApcPdu\PowerState;
use Sunfox\ApcPdu\OutletCommand;
// LoadStatus values
LoadStatus::Normal; // 1
LoadStatus::LowLoad; // 2
LoadStatus::NearOverload; // 3
LoadStatus::Overload; // 4
// PowerState values
PowerState::Off; // 1
PowerState::On; // 2
// OutletCommand values (for write operations)
OutletCommand::On; // 1
OutletCommand::Off; // 2
OutletCommand::Reboot; // 3- APC AP8653 (Metered by Outlet with Switching)
- Rack PDU FW: 7.1.4
- APC OS: 7.1.2
# Create .env file with your PDU credentials
cp .env.example .env
# Edit .env with your settings
# Run unit tests (default)
docker compose run --rm php
# Run integration tests (requires real PDU)
docker compose run --rm integration
# Run PHPStan analysis
docker compose run --rm phpstan
# Run PSR-12 coding style check
docker compose run --rm phpcs
# Auto-fix PSR-12 violations
docker compose run --rm phpcbf
# Run benchmark (requires real PDU)
docker compose run --rm php bin/benchmark --helpcomposer install
composer testMIT License. See LICENSE for details.