Monitoring the UPS APC SMT-750I

Using RJ45, Prometheus, Grafana, SNMP

Posted by Giulio Magnifico on Sunday, June 8, 2025

I decided to write this post because something that seemed simple and easy, turned into a deep rabbit hole of UPS units, USB updates, serial cables, network cards, and other mess…

Introduction

It all starts when I decided to change the UPS that keeps the server/cabinet running. I had found an offer on Amazon for the same UPS as my actual one but the new model, which has the option to be monitored via apcupsd exporter, this is the model: APC Back-UPS BX500MI (which was replacing this: APC Back-UPS BX500CI)

When it arrived and I installed it, I immediately noticed that the entire server was consuming 8/10W more than before. Additionally, this new UPS was heating up a lot! I put my hand on it and felt it quite hot, while the previous one consumed less and, most importantly, didn’t heat up at all.

Anyway, with the new BX500MI’s USB cable connected to my Prometheus/Grafana server using apcupsd, I built a useful dashboard displaying extensive info (UPS load, temperature, voltages, etc.).

…but the consumption and heat were too high, so I decided to return it to Amazon. I was disappointed to lose the dashboards by switching back to the old UPS, so I opted to use an old spare UPS I was using for the Intel iMac 27", the excellent APC SMT750I - Smart-UPS. This medium/high-end UPS features a pure sine wave, display, USB and serial ports, network card, etc. Although I initially thought it was “wasteful” to use it for a small server, it has the advantage of consuming very little power online—less than the first APC BX500CI UPS and less than half of the new BX500MI (just a dozen watts)..

Instead of doing nothing, I chose to use the APC SMT750I because its USB port allowed me to monitor it in Grafana.

And that’s where the rabbit hole started:

Installation

When I decided to replace the old one, I measured the cabinet and saw it fit perfectly inside. However, during installation, I encountered an issue I hadn’t considered: the cables and ports on the back =[

Due to the IEC power sockets and USB cable, the UPS stuck out of the cabinet by 8/10 cm.

I didn’t give up, I used a drill to widen the back of the furniture I use as a cabinet.

cabinet-inside

After spending a few hours taking out all the hardware, expanding the hole and reassembling, everything worked and the UPS was perfectly flush (it was even better because I had widened the hole on the back of the cabinet for the cables, and they passed through more easily):

cabinet-rear server cabinet server cabinet

But I immediately found a problem:

Issues with metrics and USB

This UPS exposed very few data via USB!

Basically only whether the UPS was running on battery or electricity, and the voltage. That’s it. Much less than the other one, which also exposed temperature, load, input/output voltages, number of power outages, etc…

Investigating, I noticed that the firmware was very old, from 2010.

So I decided to…

Update the firmware…

…to see if more information could be obtained via USB on a 15-years-newer firmware (as it was supposed to be).

The update process started with Windows because there is no update tool for macOS or Linux, so I connected the USB cable to a miniPC and tried updating using “APC Firmware Upgrade,” but it never found any UPS connected to the PC.

Hmm, I started investigating and discovered that the old versions of this firmware did not allow updating via USB but only via serial! That’s why the APC Firmware Upgrade utility couldn’t even read/find the UPS.

What the [insert insults]…

I decided to look for its serial cable at home but couldn’t find it (after all, it was 15 years old), so I decided to get one via Aliexpress.

⚠️🤬
Just so you know, these damn APC UPS units use a proprietary serial cable: RJ50 –> USB with a custom pinout (and if you use the wrong cable, you might even fry the serial port)

The cable I bought for about 10€ is this one: FTDI USB RS232 to RJ50 RJ45 10P Modular Plug for APC Smart UPS

serial-cable.

…to see if more information could be obtained via USB on a 15 years newer firmware (as it was supposed to be).

Well, after ~10/12 days the cable arrived, and I went quickly to update the firmware, and everything was quite simple.
But when I restarted the apcupsd_exporter , I noticed that the information/metrics it provided was always the same!

Damn, all that work for nothing! The only real improvement is that the new firmware lets you turn off or dim the display light… well, at least there’s that!

But I didn’t give up this time either =)

Monitoring via network card

I remembered at work having monitored a UPS like this one (the APC’s SMT serie) using the network card, and that it provided very advanced metrics and details.

So I decided to learn about the network card for this UPS and discovered it can use the old AP9630 / AP9631 cards or the newer AP9640 / AP9641 models.

The price of the new cards was outrageous: over 3/400€! But on German eBay, I found the AP9631 model (the final “1” indicates the more advanced version with environmental monitoring of temperature and humidity) for 35€ + 15€ shipping. Nice, I decided to buy it, and in just 3 days it arrived at my house.

AP9631 Smart Network Card 2

ap9631 card

Wow, it’s almost new, cool purchase.

I installed it in the UPS

ap9631 card installed

And then I connected it to the network, giving it the IP 192.168.1.242 (.240 and .241 are the two switches).

As soon as I logged into the interface, I noticed that it really has a lot more features than I expected — even a firewall. Nice!

ap9631 WebUI

I went on SNMP and enabled the metrics on the IP of my Grafana/Prometheus server 192.168.1.6.

Then I went to the Grafana/Prometheus server and had to start the fundamental software stack to be able to read the correct OIDs.

On the Grafana/Prometheus server there was already running the snmp_exporter because I use it to monitor the Synology NAS (link to the NAS post).

So I added a scrape point on Prometheus for the UPS:

 job_name: "apc_smt750i"
  metrics_path: /snmp
  params:
    module: [ups_apc]
    auth: [public_v1]
  static_configs:
    - targets: ["192.168.1.242"]
      labels:
        name: "APC SMT750I"
  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - source_labels: [__param_target]
      target_label: instance
    - target_label: __address__
      replacement: "192.168.1.6:9116"

And then I had to implement all the correct OIDs for the exporter’s snmp.yml file to be able to read the data. So I spent some hours and in the end the APC module in the snmp.yml file is this:

modules:
  UPS_APC:
    walk:
      - 1.3.6.1.2.1.33.1.2
      - 1.3.6.1.2.1.33.1.3
      - 1.3.6.1.2.1.33.1.4
      - 1.3.6.1.2.1.33.1.6
      - 1.3.6.1.2.1.33.1.7
      - 1.3.6.1.2.1.33.1.8
      - 1.3.6.1.4.1.318.1.1.1
      - 1.3.6.1.4.1.318.1.1.2
      - 1.3.6.1.4.1.318.1.1.3
      - 1.3.6.1.2.1.1
      - 1.3.6.1.2.1.33.1.3.2.0

    metrics:
      - name: UPS_powernet_output_frequency_hz
        oid: 1.3.6.1.4.1.318.1.1.1.4.3.2.0
        type: gauge
        help: Output frequency in tenths of Hz from PowerNet MIB (divide by 10)

      - name: UPS_battery_test_capacity_mah
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.5
        type: gauge
        help: Battery capacity as determined by the last test (mAh)

      - name: UPS_input_line_bads
        oid: 1.3.6.1.2.1.33.1.3.2.0
        type: counter
        help: Number of input line faults or dropouts detected

      - name: UPS_battery_status
        oid: 1.3.6.1.2.1.33.1.2.1.0
        type: gauge
        help: Battery status of the UPS (1=unknown, 2=batteryNormal, 3=batteryLow, 4=batteryDepleted)

      - name: UPS_battery_charge_remaining
        oid: 1.3.6.1.2.1.33.1.2.4.0
        type: gauge
        help: Battery charge remaining (percent)

      - name: UPS_battery_time_remaining
        oid: 1.3.6.1.2.1.33.1.2.3.0
        type: gauge
        help: Estimated runtime remaining in seconds

      - name: UPS_input_voltage
        oid: 1.3.6.1.2.1.33.1.3.3.1.3.1
        type: gauge
        help: UPS input voltage (Volts)

      - name: UPS_input_frequency
        oid: 1.3.6.1.2.1.33.1.3.3.1.4.1
        type: gauge
        help: UPS input frequency (Hz)

      - name: UPS_input_status
        oid: 1.3.6.1.2.1.33.1.3.1.0
        type: gauge
        help: UPS input status

      - name: UPS_output_voltage
        oid: 1.3.6.1.2.1.33.1.4.4.1.2.1
        type: gauge
        help: UPS output voltage (Volts)

      - name: UPS_output_current
        oid: 1.3.6.1.2.1.33.1.4.4.1.3.1
        type: gauge
        help: UPS output current (Amperes)

      - name: UPS_output_load_percent
        oid: 1.3.6.1.2.1.33.1.4.4.1.5.1
        type: gauge
        help: UPS output load (percent)

      - name: UPS_output_status
        oid: 1.3.6.1.2.1.33.1.4.1.0
        type: gauge
        help: UPS output status (other(1), none(2), normal(3), bypass(4), battery(5), booster(6), reducer(7))

      - name: UPS_bypass_status
        oid: 1.3.6.1.2.1.33.1.7.1.0
        type: gauge
        help: Bypass status

      - name: UPS_test_result
        oid: 1.3.6.1.2.1.33.1.8.2.0
        type: gauge
        help: Result of last battery test

      - name: UPS_temperature
        oid: 1.3.6.1.2.1.33.1.2.7.0
        type: gauge
        help: General UPS temperature in Celsius
        
      - name: UPS_seconds_on_battery
        oid: 1.3.6.1.2.1.33.1.2.2.0
        type: gauge
        help: Seconds elapsed since the UPS switched to battery power (0 = on mains)
    
      - name: UPS_APC_runtime_status
        oid: 1.3.6.1.4.1.318.1.1.3.1.2.1.1.0
        type: gauge
        help: APC UPS runtime status (integer)

      - name: UPS_APC_transfer_count
        oid: 1.3.6.1.4.1.318.1.1.3.1.2.2.1.0
        type: counter
        help: Number of transfers to battery power

      - name: UPS_APC_total_runtime
        oid: 1.3.6.1.4.1.318.1.1.3.1.2.1
        type: counter
        help: APC UPS total runtime in seconds

      - name: UPS_powernet_UPS_status
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.1.0
        type: gauge
        help: UPS overall status from PowerNet MIB

      - name: UPS_powernet_input_voltage
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.2.0
        type: gauge
        help: PowerNet input voltage (divide in dashboard if needed)

      - name: UPS_powernet_input_frequency
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.3.0
        type: gauge
        help: PowerNet input frequency (divide if > 1000)

      - name: UPS_powernet_output_voltage
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.4.0
        type: gauge
        help: PowerNet output voltage (divide if needed)

      - name: UPS_powernet_output_frequency
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.5.0
        type: gauge
        help: PowerNet output frequency (divide if needed)

      - name: UPS_powernet_output_current
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.6.0
        type: gauge
        help: Output current in tenths of Amperes

      - name: UPS_powernet_output_power
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.7.0
        type: gauge
        help: Output power in Watts

      - name: UPS_powernet_battery_voltage
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.8.0
        type: gauge
        help: Battery voltage (Volts)

      - name: UPS_powernet_battery_current
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.9.0
        type: gauge
        help: Battery current in Amperes

      - name: UPS_powernet_UPS_runtime_seconds
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.10.0
        type: counter
        help: Cumulative runtime in seconds (PowerNet)

      - name: sys_uptime_seconds
        oid: 1.3.6.1.2.1.1.3.0
        type: gauge
        help: System uptime in seconds
        scale: 0.01

      - name: UPS_battery_voltage_RAW
        oid: 1.3.6.1.2.1.33.1.2.5.0
        type: gauge
        help: RAW battery voltage from standard UPS MIB

      - name: UPS_output_source
        oid: 1.3.6.1.2.1.33.1.4.4.1.4.1
        type: gauge
        help: Source of UPS output (0=unknown, 1=other, 2=none, 3=normal, 4=bypass, 5=battery, 6=booster, 7=reducer)

      - name: UPS_battery_needs_replacement
        oid: 1.3.6.1.4.1.318.1.1.1.7.2.6.0
        type: gauge
        help: Indicates if battery needs replacement (1=yes, 2=no)
      
      - name: UPS_alarm_count
        oid: 1.3.6.1.2.1.33.1.6.3.0
        type: gauge
        help: Number of active UPS alarms

      - name: UPS_test_elapsed_time
        oid: 1.3.6.1.2.1.33.1.8.3.0
        type: gauge
        help: Seconds elapsed since last battery test

      - name: UPS_test_interval
        oid: 1.3.6.1.2.1.33.1.8.4.0
        type: gauge
        help: Self-test interval configuration in seconds (-1 = UPS Startup or complex mode)
      
      - name: UPS_battery_replacement_date
        oid: 1.3.6.1.4.1.318.1.1.1.7.2.4.0
        type: DisplayString
        help: Date the UPS battery was last replaced
      
      - name: UPS_battery_model
        oid: 1.3.6.1.4.1.318.1.1.1.2.2.19.0
        type: DisplayString
        help: UPS battery model (e.g., RBC48)
        
      - name: UPS_sys_descr
        oid: 1.3.6.1.2.1.1.1.0
        type: DisplayString
        help: System description, typically includes UPS model and firmware
        
      - name: UPS_sys_name
        oid: 1.3.6.1.2.1.1.5.0
        type: DisplayString
        help: System name

      - name: UPS_sys_location
        oid: 1.3.6.1.2.1.1.6.0
        type: DisplayString
        help: Physical UPS location

      - name: UPS_sys_contact
        oid: 1.3.6.1.2.1.1.4.0
        type: DisplayString
        help: Contact responsible for this UPS
      
      - name: UPS_firmware_version
        oid: 1.3.6.1.4.1.318.1.1.1.1.2.1.0
        type: DisplayString
        help: UPS firmware version string
          

With these OIDs, I can read these metrics:

# HELP snmp_scrape_duration_seconds Total SNMP time scrape took (walk and processing).
# TYPE snmp_scrape_duration_seconds gauge
snmp_scrape_duration_seconds{module="ups_apc"} 6.522366084
# HELP snmp_scrape_packets_retried Packets retried for get, bulkget, and walk.
# TYPE snmp_scrape_packets_retried gauge
snmp_scrape_packets_retried{module="ups_apc"} 0
# HELP snmp_scrape_packets_sent Packets sent for get, bulkget, and walk; including retries.
# TYPE snmp_scrape_packets_sent gauge
snmp_scrape_packets_sent{module="ups_apc"} 271
# HELP snmp_scrape_pdus_returned PDUs returned from get, bulkget, and walk.
# TYPE snmp_scrape_pdus_returned gauge
snmp_scrape_pdus_returned{module="ups_apc"} 259
# HELP snmp_scrape_walk_duration_seconds Time SNMP walk/bulkwalk took.
# TYPE snmp_scrape_walk_duration_seconds gauge
snmp_scrape_walk_duration_seconds{module="ups_apc"} 6.521636888
# HELP sys_uptime_seconds System uptime in seconds
# TYPE sys_uptime_seconds gauge
sys_uptime_seconds 159155.5
# HELP ups_apc_total_runtime APC UPS total runtime in seconds
# TYPE ups_apc_total_runtime counter
ups_apc_total_runtime 0
# HELP ups_apc_transfer_count Number of transfers to battery power
# TYPE ups_apc_transfer_count counter
ups_apc_transfer_count 1
# HELP ups_battery_charge_remaining Battery charge remaining (percent)
# TYPE ups_battery_charge_remaining gauge
ups_battery_charge_remaining 100
# HELP ups_battery_model UPS battery model (e.g., RBC48)
# TYPE ups_battery_model gauge
ups_battery_model{ups_battery_model="RBC48"} 1
# HELP ups_battery_needs_replacement Indicates if battery needs replacement (1=yes, 2=no)
# TYPE ups_battery_needs_replacement gauge
ups_battery_needs_replacement 2
# HELP ups_battery_replacement_date Date the UPS battery was last replaced
# TYPE ups_battery_replacement_date gauge
ups_battery_replacement_date{ups_battery_replacement_date="06/07/2025"} 1
# HELP ups_battery_status Battery status of the UPS (1=unknown, 2=batteryNormal, 3=batteryLow, 4=batteryDepleted)
# TYPE ups_battery_status gauge
ups_battery_status 2
# HELP ups_battery_time_remaining Estimated runtime remaining in seconds
# TYPE ups_battery_time_remaining gauge
ups_battery_time_remaining 72
# HELP ups_battery_voltage_raw Raw battery voltage from standard UPS MIB
# TYPE ups_battery_voltage_raw gauge
ups_battery_voltage_raw 268
# HELP ups_bypass_status Bypass status
# TYPE ups_bypass_status gauge
ups_bypass_status 0
# HELP ups_firmware_version UPS firmware version string
# TYPE ups_firmware_version gauge
ups_firmware_version{ups_firmware_version="UPS 07.1 (ID17) "} 1
# HELP ups_input_frequency UPS input frequency (Hz)
# TYPE ups_input_frequency gauge
ups_input_frequency 0
# HELP ups_input_line_bads Number of input line faults or dropouts detected
# TYPE ups_input_line_bads counter
ups_input_line_bads 1
# HELP ups_input_voltage UPS input voltage (Volts)
# TYPE ups_input_voltage gauge
ups_input_voltage 230
# HELP ups_output_current UPS output current (Amperes)
# TYPE ups_output_current gauge
ups_output_current 5
# HELP ups_output_load_percent UPS output load (percent)
# TYPE ups_output_load_percent gauge
ups_output_load_percent 17
# HELP ups_output_source Source of UPS output (0=unknown, 1=other, 2=none, 3=normal, 4=bypass, 5=battery, 6=booster, 7=reducer)
# TYPE ups_output_source gauge
ups_output_source 0
# HELP ups_output_status UPS output status (other(1), none(2), normal(3), bypass(4), battery(5), booster(6), reducer(7))
# TYPE ups_output_status gauge
ups_output_status 3
# HELP ups_output_voltage UPS output voltage (Volts)
# TYPE ups_output_voltage gauge
ups_output_voltage 230
# HELP ups_powernet_battery_voltage Battery voltage (Volts)
# TYPE ups_powernet_battery_voltage gauge
ups_powernet_battery_voltage 27
# HELP ups_powernet_input_frequency PowerNet input frequency (divide if > 1000)
# TYPE ups_powernet_input_frequency gauge
ups_powernet_input_frequency 432000
# HELP ups_powernet_input_voltage PowerNet input voltage (divide in dashboard if needed)
# TYPE ups_powernet_input_voltage gauge
ups_powernet_input_voltage 41
# HELP ups_powernet_output_frequency_hz Output frequency in tenths of Hz from PowerNet MIB (divide by 10)
# TYPE ups_powernet_output_frequency_hz gauge
ups_powernet_output_frequency_hz 500
# HELP ups_powernet_output_voltage PowerNet output voltage (divide if needed)
# TYPE ups_powernet_output_voltage gauge
ups_powernet_output_voltage 1
# HELP ups_powernet_ups_status UPS overall status from PowerNet MIB
# TYPE ups_powernet_ups_status gauge
ups_powernet_ups_status 100
# HELP ups_seconds_on_battery Seconds elapsed since the UPS switched to battery power (0 = on mains)
# TYPE ups_seconds_on_battery gauge
ups_seconds_on_battery 0
# HELP ups_sys_contact Contact responsible for this UPS
# TYPE ups_sys_contact gauge
ups_sys_contact{ups_sys_contact="giuliomagnifico@gmail.com"} 1
# HELP ups_sys_descr System description, typically includes UPS model and firmware
# TYPE ups_sys_descr gauge
ups_sys_descr{ups_sys_descr="APC Web/SNMP Management Card (MB:v4.1.0 PF:v6.4.6 PN:apc_hw05_aos_646.bin AF1:v6.4.6 AN1:apc_hw05_sumx_646.bin MN:AP9631 HR:05 SN: ZA1424005599 MD:06/12/2014) (Embedded PowerNet SNMP Agent SW v2.2 compatible)"} 1
# HELP ups_sys_location Physical UPS location
# TYPE ups_sys_location gauge
ups_sys_location{ups_sys_location="Casa"} 1
# HELP ups_sys_name System name
# TYPE ups_sys_name gauge
ups_sys_name{ups_sys_name="SMT750I"} 1
# HELP ups_temperature General UPS temperature in Celsius
# TYPE UPS_temperature gauge
UPS_temperature 41
# HELP UPS_test_elapsed_time Seconds elapsed since last battery test
# TYPE UPS_test_elapsed_time gauge
UPS_test_elapsed_time -1
# HELP UPS_test_interval Self-test interval configuration in seconds (-1 = UPS Startup or complex mode)
# TYPE UPS_test_interval gauge
UPS_test_interval -1
# HELP UPS_test_result Result of last battery test
# TYPE UPS_test_result gauge
UPS_test_result -1

That allow me to built this useful Grafana dashboard:

grafana-dashboard full

Using these queries I also added many useful alerts in Grafana for power outages, excessive load, high temperature, and more. Example:

grafana-push-alert-example

Conclusions

It’s curious how something as simple as changing a UPS turned into a rabbit hole of USB updates, serial cables, network management cards, etc… that consumed over a month of my time! But it’s nice this way: I learned and discovered new things.

PS: My “summary” is getting too long… new updates coming soon =)

grafana-full