Wiring a UPS Into Home Assistant With NUT: Graceful Shutdown for the Whole Homelab Home Automation

Wiring a UPS Into Home Assistant With NUT: Graceful Shutdown for the Whole Homelab

by Joule P. Kraft · June 3, 2026

As an Amazon Associate I earn from qualifying purchases. No affiliate relationship influences my recommendations.

I lost a Home Assistant database to a brownout last fall. The lights flickered for maybe three seconds, the Mini PC I run HA on lost power, and the SQLite database came back corrupt. Two weeks of recorder history, gone. Every automation worked fine after the restore, but the energy graphs looked like someone took an eraser to them.

That was the day I finally bought a UPS for the rack and wired it into Home Assistant. Six months in, it has saved my bacon three more times (two brownouts and one full outage). This is the build log.

What I’m Building

The goal:

  • A UPS sits between the wall and everything in my rack — Home Assistant mini-PC, network switch, Pi running Zigbee2MQTT, the LTE failover modem.
  • Home Assistant talks to the UPS over USB and exposes its battery percentage, load, and estimated runtime as sensors.
  • When the power goes out and the battery drops below a safe threshold, an automation tells every host on the rack to shut down cleanly. Then HA itself shuts down last.
  • When the power comes back, everything boots in the right order and HA confirms via a notification that the rack is healthy.

The whole thing runs on Network UPS Tools (NUT), an old, boring, extremely reliable piece of Linux software that’s been monitoring server-room UPS units since 1998. It is the right tool for this. None of the OEM Windows-only “PowerChute” garbage.

Hardware

  • An APC Back-UPS Pro BR1500MS2. This is my pick. 1500VA / 900W, pure sine wave output, USB-C charging ports on the front (handy), and 10 outlets split between battery+surge and surge-only. About $230. The pure sine wave matters more than you’d think — see the FAQ.
  • A USB A-to-B cable. The BR1500MS2 ships with one but I always have a spare because the OEM cable is short.
  • Your Home Assistant host. I’m running HAOS on a Beelink mini-PC, but a Raspberry Pi 5 works equally well. Whatever host you use needs to be plugged into the UPS and needs a free USB port to talk to it.

If you’re shopping and the BR1500MS2 is out of stock, the rule is: get pure sine wave, 1000–1500VA depending on your rack load, and verify it’s on the NUT compatibility list before you buy.

Step 1: Size the UPS Correctly

I see people on r/homeassistant buying 600VA units and then asking why the runtime estimate is two minutes. The math is simple. Add up the wattage of everything you want to keep alive:

  • Beelink mini-PC running HA: ~12W idle, 18W under load
  • Unmanaged 8-port gigabit switch: ~5W
  • Raspberry Pi 4 running Zigbee2MQTT: ~4W
  • LTE failover router: ~7W
  • A second Pi I use for backups: ~5W

Call it 45W average. The BR1500MS2’s 900W capacity is gross overkill for that load, but that’s the point. Lower load means longer runtime. At 45W I get about 90 minutes of runtime. At 100W (someone left a beefy Plex transcode running) I’d get about 30. That’s the budget I’m working with.

Pro tip: do not plug your modem and your laptop charger into the same UPS as your rack. They will drain the battery faster and skew your runtime estimates. The UPS is for the always-on infrastructure, not for “keeping the WiFi up while I finish this email.”

Step 2: Plug Everything In, Including the USB Cable

Outlet side: rack power into the battery+surge outlets. Anything you don’t care about (printer, lamp) into the surge-only outlets so they aren’t burning battery during an outage.

USB side: the small USB-B port on the back of the BR1500MS2 goes into a USB-A port on your Home Assistant host. This is the bit people skip and then wonder why the integration doesn’t see the UPS. No USB cable, no telemetry. The UPS will still keep your rack up during an outage but HA will be blind.

Step 3: Install the NUT Add-on in Home Assistant OS

If you’re running HAOS or Home Assistant Supervised, this is dead simple. There’s an official Network UPS Tools add-on in the Add-on Store.

  1. Settings → Add-ons → Add-on Store, search “NUT” or “Network UPS Tools,” install.
  2. Before you start it, configure it. Open the Configuration tab. The default config detects most USB UPS units automatically via nut-scanner, but I prefer to be explicit:
users:
  - username: hass
    password: <generate a long random string here>
    instcmds:
      - all
    actions: []
devices:
  - name: rack
    driver: usbhid-ups
    port: auto
    config:
      - desc = "APC BR1500MS2"
mode: netserver
shutdown_host: false

The shutdown_host setting is important — leave it false. We want Home Assistant deciding when to shut down, not the NUT add-on doing it autonomously. (If you forget this and leave it true, the add-on will helpfully halt your HA host the moment battery drops, with no automation logic. Ask me how I know.)

  1. Start the add-on. Check the log for usbhid-ups: UPS HID device found and Startup successful. If you see No matching HID UPS found then either the USB cable isn’t connected or your kernel doesn’t have hidraw permissions — for HAOS that just means restart the host.

Step 4: Wire the NUT Integration to Home Assistant

This is the client side that turns the NUT server’s data into Home Assistant sensors.

  1. Settings → Devices & Services → Add Integration → Network UPS Tools (NUT).
  2. Host: a0d7b954-nut (the add-on hostname inside HA’s Docker network) if you used the add-on. Or localhost if you installed NUT outside the add-on system.
  3. Port: 3493 (default).
  4. Username: hass (whatever you set above). Password: the long random string.
  5. UPS name: rack (matches the name in the NUT config).

It should immediately discover a device with about 20 sensors. The ones I actually use in automations:

  • sensor.rack_battery_charge (percentage 0-100)
  • sensor.rack_battery_runtime (seconds — divide by 60 for minutes)
  • sensor.rack_input_voltage
  • sensor.rack_status (text: OL = on line, OB = on battery, LB = low battery, often combined as OB DISCHRG etc.)
  • sensor.rack_ups_load (percentage of capacity in use)

sensor.rack_status is the one to watch. The “is the power out right now” signal is when status contains OB (on battery). Everything else is for graphing and dashboards.

Step 5: The Shutdown Automation

Here’s the automation that has saved me three times now. Three triggers, escalating in severity.

alias: "UPS — Graceful Rack Shutdown"
description: "Shut down the homelab cleanly when battery runs low"
mode: single
trigger:
  - platform: numeric_state
    entity_id: sensor.rack_battery_runtime
    below: 300  # 5 minutes of runtime remaining
    for: "00:00:30"
  - platform: numeric_state
    entity_id: sensor.rack_battery_charge
    below: 20
    for: "00:00:30"
  - platform: state
    entity_id: sensor.rack_status
    to: "LB"  # UPS-reported low battery, hardware-level
condition:
  - condition: state
    entity_id: sensor.rack_status
    state: "OB DISCHRG"
    # only shut down if we're actually on battery, not just briefly dropped
action:
  - service: notify.mobile_app_jpk_phone
    data:
      title: "Rack shutdown initiated"
      message: "Battery at {{ states('sensor.rack_battery_charge') }}%, runtime {{ (states('sensor.rack_battery_runtime') | int / 60) | round(1) }} min"
  - service: shell_command.shutdown_pi_zigbee
  - service: shell_command.shutdown_pi_backup
  - delay: "00:01:00"  # give them time to halt
  - service: hassio.host_shutdown

The shell_command entries are SSH calls to each Pi:

# configuration.yaml
shell_command:
  shutdown_pi_zigbee: "ssh -i /config/.ssh/id_ed25519 -o StrictHostKeyChecking=no [email protected] sudo shutdown -h now"
  shutdown_pi_backup: "ssh -i /config/.ssh/id_ed25519 -o StrictHostKeyChecking=no [email protected] sudo shutdown -h now"

(You’ll need to generate an SSH key in /config/.ssh/ on Home Assistant, copy the public key to each Pi’s ~/.ssh/authorized_keys, and grant passwordless sudo for the shutdown command via /etc/sudoers.d/. Worth the 15 minutes of setup.)

hassio.host_shutdown does the HA mini-PC last. Order matters because the moment HA goes down, you lose your orchestration. Everything else has to be down first.

Step 6: The “Power Came Back” Notification

The flip side is just as useful. When the UPS reports back on line power, ping me.

alias: "UPS — Power Restored"
mode: single
trigger:
  - platform: state
    entity_id: sensor.rack_status
    to: "OL"
    from: "OB DISCHRG"
    for: "00:01:00"  # debounce — wait a minute to make sure it sticks
action:
  - service: notify.mobile_app_jpk_phone
    data:
      title: "Rack is back on grid"
      message: "Battery: {{ states('sensor.rack_battery_charge') }}%. Outage lasted {{ (as_timestamp(now()) - as_timestamp(states.sensor.rack_status.last_changed)) | round(0) }}s before recovery (estimate)."

The reason I want the notification: I have an LTE failover that keeps my HA cloud connection up during a wired internet outage, so I genuinely don’t know about power outages in real time when I’m out of the house. This is the only signal that tells me my house briefly went dark.

Step 7: Dashboard Card

A small Mushroom card on my main dashboard:

type: custom:mushroom-template-card
primary: "UPS — {{ states('sensor.rack_status') }}"
secondary: >-
  {{ states('sensor.rack_battery_charge') }}% • 
  {{ (states('sensor.rack_battery_runtime') | int / 60) | round(0) }} min • 
  {{ states('sensor.rack_ups_load') }}% load
icon: >-
  {% if states('sensor.rack_status') == 'OL' %}mdi:power-plug
  {% else %}mdi:battery-alert{% endif %}
icon_color: >-
  {% if states('sensor.rack_status') == 'OL' %}green
  {% else %}red{% endif %}

A single glance tells me whether power is fine, what the battery is at, how long I’d survive, and what percentage of the UPS’s capacity I’m using. The last one matters because if I see load creeping above 30%, something’s plugged in that shouldn’t be.

What I Got Wrong the First Time

A few mistakes worth documenting so you can skip them:

  1. I bought a stepped-approximation UPS first. It worked fine with the Pi but my mini-PC’s active PFC power supply refused to run on the stepped output. The PSU would chirp and cut power the moment the UPS engaged. Returning that UPS and buying the BR1500MS2 for the pure sine wave fixed it permanently.

  2. I wired the shutdown automation to trigger on sensor.rack_battery_charge < 20 without the for: 30s delay. The battery reading is noisy under sudden load spikes and would dip to 19% for half a second when a fan kicked on. Got a phantom shutdown at 3 AM. The 30-second debounce eliminated it.

  3. I forgot to put the modem and router on the UPS the first time. Power went out, the rack stayed up, but the internet was down so my LTE failover never had anything to fail over to. Now my modem, router, and access point are all on battery too. The runtime estimate dropped from 120 to 90 minutes, which is the right trade.

  4. I tried to use the APC official “PowerChute Personal” software at first because the UPS box mentioned it. Don’t. It’s Windows-only, it does nothing NUT doesn’t do better, and it makes Home Assistant integration impossible because it monopolizes the USB connection. NUT is the answer.

The Bottom Line

If you run a homelab, you need a UPS. If you run Home Assistant on that homelab, you need to wire the UPS into HA via NUT so it can shut itself down gracefully instead of having its database corrupted by the next brownout.

The APC Back-UPS Pro BR1500MS2 is the right unit for a small rack. Pure sine wave, 900W capacity, plays nicely with NUT. About $230. Cheaper than the SSD you’ll have to replace and the recorder history you’ll lose the next time the power flickers.

The whole NUT-to-Home-Assistant chain takes maybe an hour to wire up properly. Then it sits there silently for years, doing the boring job of telling you when your house is on battery, and doing the slightly less boring job of shutting your rack down cleanly before the lights go out.

That’s the whole pitch. Boring infrastructure that earns its slot.

Frequently Asked Questions

Do I need a pure sine wave UPS for Home Assistant on a Raspberry Pi?+
For a Pi alone, no, a stepped-approximation UPS is fine. The moment you add a mini-PC with active PFC power supply (most modern Intel NUCs, Beelinks, ASUS PNs), you need pure sine wave or the PSU will refuse to switch to battery and cut power instead. That's why I recommend the BR1500MS2 — it's pure sine and only ~$25 more than the stepped models.
Will NUT work with cheap no-name UPS units?+
Sometimes. The NUT project maintains a hardware compatibility list at networkupstools.org/stable-hcl.html. APC, CyberPower, and Eaton are well-supported. Off-brand units often present as generic HID-UPS which works for basic battery percentage but loses estimated runtime and load reporting.
Can I run NUT and the Home Assistant NUT integration on the same Raspberry Pi?+
Yes, that's the most common setup. The HA NUT integration is a client — it needs a NUT server somewhere on the network. The Home Assistant OS add-on bundles both server and client on the same host, which is what this build uses.
How long should the runtime threshold be before I trigger shutdown?+
Set it to comfortably longer than your slowest device's shutdown time. My rack takes about 90 seconds end-to-end to halt cleanly, so I trigger at 5 minutes of estimated runtime remaining. That gives margin for the runtime estimate getting more pessimistic as the battery drains under load.
What happens to the UPS sensors if Home Assistant restarts during a power outage?+
The NUT server (nut-server add-on) keeps running independently of Home Assistant Core, so it continues monitoring the UPS over USB. When HA Core comes back up, the NUT integration reconnects and you see the current state immediately. The risk is missing the automation trigger during the restart window — keep HA's auto-update window outside your local outage-prone hours.