Monitor a UPS attached to a Windows machine

Recently I found myself in the unfortunate situation of needing to monitor a UPS that was attached to a Windows machine from HA. I’ll just preface this by saying if there is any way you can connect it to a Linux machine instead I would recommend that. But if you run Blue Iris or some other Windows only service and you really want/need the UPS connected there then this is the guide for you.

Pre-requisites

I choose to use Network UPS Tools to do the monitoring. I did this because I’ve had great success with it for years with several different UPS’s connected to my HA machine, including the one now sitting next to my Windows machine. It has a windows service so I figured it would be easy (spoiler: not so much). So before you begin, check that your ups is listed here.

Also I am running on Windows 10 Pro. I have no idea if this will work on any other version of Windows. It’s not a simple install process as you’ll see in a moment so YMMV

Installation

After a lot of failures, searching and cursing I stumbled across this guide and it saved the day. I needed to tweak it a bit so here’s the steps I did:

  1. Install the windows service
  2. Go to NUT’s directory, C:\Program Files (x86)\NUT for me
  3. Copy libgcc_s_dw2-1.dll from bin to sbin. it is required for upsmon to run.
  4. NUT requires libeay32.dll and ssleay32.dll which are provided by OpenSSL. It does not package them in the installer. Go here and click the one labeled “Pre-compiled Win32/64 libraries without external dependencies to the Microsoft Visual Studio Runtime DLLs” (link at the time was Index of /SSL). From there I downloaded the zip for v1.0.2u win32 (or newer win32 version if there is one). Extract it and move the two dlls I mentioned into NUT’s sbin folder.
  5. NUT also requires libusb. Download and install that. Don’t have to move dlls around this time.

Driver

Next up, NUT needs to install a driver for your particular UPS. This is where I deviated the most from the JTK guide I linked above. Fortunately, it was the easy part.

  1. Connect your UPS to the machine via USB if you haven’t yet
  2. Run cmd as administrator
  3. Navigate to NUT’s directory again
  4. Run others/wdi-simple.exe
  5. Follow any prompts

For me, that was it. If that fails then you might have to give inf_wizard and nut-scanner a try as the author of that guide did. I couldn’t get those to run so I’m not sure what’s involved.

Configuration

The files you need to configure are in the etc folder within NUT’s folder. There’s a sample of each so you need to save as each one to that folder and remove .sample. I should also note that for this part I copied from Frenck’s wonderful NUT addon as much as possible since that addon has worked well for me for years.

nut.conf

Just set mode=netserver at the bottom

upsd.users

Add something like this:

[upsmonmaster]
  password = <INSERT PASSWORD> (alphanumerics only)
  upsmon master

I actually don’t know if only alphanumerics is required. Some characters are definitely restricted, I didn’t feel like battling it more to find out what.

You can also make a user with specific privileges for HA or others if you want, see here for how.

ups.conf

This one you’ll have to do yourself as it varies per UPS. Here’s the minimum required (you’ll need to provide your own values though):

[myups]
  driver = mydriver
  port = myport

First thing you’ll need to do is figure out your driver. Assuming you looked up your ups here then choose the driver it lists (mine was usbhid-ups).

port is the tricky bit, you need to provide the com port. You can see these by going to device manager, clicking “view” at the top and then “Show hidden devices”. You should now see a section named “Ports (COM & LPT)”. What I put here was simply COM1 since I only had one. If you have more then one then you’ll have to figure out which one is in use. Sorry I don’t really know how to do this, if someone with more windows knowledge then me comments a way then I’ll update this with it.

After that there’s many optional fields you can see here. Most likely this is something you’ll return to if you find you are having issues. There’s a lot of topics on this forum about settings people have had to apply to get their UPS working better in this section. If you see people adding things to devices.config in the NUT addon in posts, that’s the kind of stuff that goes here.

upsd.conf

Basically just one thing to configure here, where to listen. Probably add something like this:

LISTEN <IP address> 3493
LISTEN 127.0.0.1 3493

Or just put LISTEN 0.0.0.0 3493 and tell it to respond to any requests if you don’t feel like getting specific.

There’s also one other important field in this file, MAXAGE. This is another field you should not set unless you have issues. But if you have issues it may help. When you search on the forum if you see people recommending setting upsd_maxage in the addon for similar sounding issues, that is this field.

upssched.conf

I’m pretty sure this isn’t used. I added CMDSCRIPT "C:\\Program Files (x86)\\NUT\\bin\\upssched-cmd" simply because that was the Windows equivalent of what the addon had here.

upsmon.conf

Bunch of things to do here

  1. Add MONITOR [email protected] 1 upsmonmaster PASSWORD master. NOTE: adjust this! myups is whatever you called your ups in ups.conf. upsmonmaster and PASSWORD is the creds you added in upsd.users. master is the role you gave it.
  2. If you want the UPS to shutdown the machine before it dies, add SHUTDOWNCMD "C:\\WINDOWS\\system32\\shutdown.exe -s -t 0"
  3. Add POWERDOWNFLAG C:\\NUT\\killpower. This file can actually be wherever you want. It doesn’t exist, NUT makes it before shutting down. It’s a flag to check in scripts and such.
  4. Customize DEADTIME if you need to. This is another knob people turn when they have issues. If you see people on the forum with the addon recommending changing upsmon_deadtime for similar issues, that’s this field.
  5. Everything else besides NOTIFYCMD and NOTIFYFLAG (more on these in a second) I copied from here

Signaling HA

At this point you can start the NUT service and add the NUT integration in HA to connect to it. Just find Network UPS Tools in your services list and start it. Make sure to set it to Automatic so it runs on startup. I would also recommend going to the recovery tab and telling it to restart on crash just in case.

However the NUT addon fires nut.ups_event events when the UPS changes status. If you want those for your automations like I did then let’s add them. First add the following to upsmon.conf

NOTIFYCMD C:\\NUT\\bin\\notify.bat
NOTIFYFLAG ONLINE	EXEC
NOTIFYFLAG ONBATT	EXEC
NOTIFYFLAG LOWBATT	EXEC
NOTIFYFLAG FSD		EXEC
NOTIFYFLAG COMMOK	EXEC
NOTIFYFLAG COMMBAD	EXEC
NOTIFYFLAG SHUTDOWN	EXEC
NOTIFYFLAG REPLBATT	EXEC
NOTIFYFLAG NOCOMM	EXEC
NOTIFYFLAG NOPARENT	EXEC

This makes NUT call a script any time the UPS status changes. The script can go wherever you want, doesn’t have to be C:\NUT\bin\notify.bat just adjust NOTIFYCMD accordingly.

Wherever you put it, this is the script:

@echo off
SET TOKEN=HA_LL_ACCESS_TOKEN
SET HAHOST=HA_DOMAIN_OR_IP
SET EVENTTYPE=nut.ups_event

curl -sS -X "POST" ^
    -H "Authorization: Bearer %TOKEN%" ^
    --data-binary "{\"ups_name\":\"%UPSNAME%\",\"notify_type\":\"%NOTIFYTYPE%\",\"notify_msg\":"%1%"}" ^
    "http://%HAHOST%/api/events/%EVENTTYPE%"

You need to generate a long-lived access token and replace HA_LL_ACCESS_TOKEN with it. Also replace HA_DOMAIN_OR_IP with your HA’s domain or IP address (however you reach it).

Storing the token encrypted

If you expanded this then I assume it also bothered you that you had to store a long-lived access token plaintext in a file. You don’t but there’s a bunch more steps so you’ll have to decide if its worth it.

To do this I found this stackoverflow post. Which is great, exactly what I wanted! Except the NUT service needs to run as the system user so it can do all the things it needs to do which means it can’t decrypt a credential file made by my user.

But then I found this wonderful guide showing how to not only start a windows task from an event but actually pass data from the event into it. You can define the user to use as part of the task so this works. But man is it complicated, here we go:

  1. From powershell run Get-Credential | Export-CliXml cred.xml. A credentials prompt will appear. Enter anything in username (doesn’t matter) and put your access token in password. Put this file wherever you want, I put it in C:\Users\<username\ha_token.xml just to remind myself that I needed to be that user.
  2. Remove NOTIFYCMD from upsmon.conf and replace the NOTIFYFLAG parts with this:
NOTIFYFLAG ONLINE	SYSLOG
NOTIFYFLAG ONBATT	SYSLOG
NOTIFYFLAG LOWBATT	SYSLOG
NOTIFYFLAG FSD		SYSLOG
NOTIFYFLAG COMMOK	SYSLOG
NOTIFYFLAG COMMBAD	SYSLOG
NOTIFYFLAG SHUTDOWN	SYSLOG
NOTIFYFLAG REPLBATT	SYSLOG
NOTIFYFLAG NOCOMM	SYSLOG
NOTIFYFLAG NOPARENT	SYSLOG
  1. Add this to upsmon.conf
NOTIFYMSG ONLINE	"|ONLINE|UPS %s on line power"
NOTIFYMSG ONBATT	"|ONBATT|UPS %s on battery"
NOTIFYMSG LOWBATT	"|LOWBATT|UPS %s battery is low"
NOTIFYMSG FSD		"|FSD|UPS %s: forced shutdown in progress"
NOTIFYMSG COMMOK	"|COMMOK|Communications with UPS %s established"
NOTIFYMSG COMMBAD	"|COMMBAD|Communications with UPS %s lost"
NOTIFYMSG SHUTDOWN	"|SHUTDOWN|Auto logout and shutdown proceeding"
NOTIFYMSG REPLBATT	"|RELBATT|UPS %s battery needs to be replaced"
NOTIFYMSG NOCOMM	"|NOCOMM|UPS %s is unavailable"
NOTIFYMSG NOPARENT	"|NOPARENT|upsmon parent process died - shutdown impossible"
  1. Restart the service and unplug your ups then plug it back in (we’re generating a windows event)
  2. Go to event viewer and find the event. Select to create a task from it
  3. Follow the prompts (you can name it whatever and the pre-selected event filter is correct). When it asks what to do say “run a program” and put in this:
    • Program/script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
    • Arguments: -Command C:\Users\<username>\nut_event_to_ha.ps1 '$(EventData)'
      • We’re going to make C:\Users\<username>\nut_event_to_ha.ps1 you can put it wherever you want
  4. Go to Task Scheduler → Task Scheduler Library → Event Viewer Tasks. Right click your new task and export it then open it in a text editor
  5. There’s a section of the XML that looks like this:
<Triggers>
    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Application"&gt;&lt;Select Path="Application"&gt;*[System[Provider[@Name='Network UPS Tools'] and EventID=1]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
    </EventTrigger>
  </Triggers>

We’re going to add a piece called ValueQueries which can’t be set in the UI by changing it to this:

<Triggers>
    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Application"&gt;&lt;Select Path="Application"&gt;*[System[Provider[@Name='Network UPS Tools'] and EventID=1]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
	  <ValueQueries>
        <Value name="EventData">Event/EventData/Data</Value>
      </ValueQueries>
    </EventTrigger>
  </Triggers>
  1. Delete your original task and import the one you modified
  2. Set the task to run whether the user is logged in or not, everything else should be fine
  3. Create C:\Users\<username>\nut_event_to_ha.ps1 (or wherever you put it) and add this to it:
param(
	[string]$event_data = "upsmon - |NOCOMM|UPS [email protected] is unavailable"
)
$TOKEN = (( Import-CliXml C:\Users\<username>\ha_token.xml ).GetNetworkCredential().Password)
$HAHOST=HA_DOMAIN_OR_IP
$EVENTTYPE="nut.ups_event"
$UPSNAME="[email protected]"

$NUT_EVENT = ($event_data.Split("|"))
$notify_type = ($NUT_EVENT[1])
$notify_msg = ($NUT_EVENT[2])

curl.exe -sS -X "POST" `
  -H "Authorization: Bearer $TOKEN" `
  --data-binary "{\`"ups_name\`":\`"$UPSNAME\`", \`"notify_type\`":\`"$notify_type\`", \`"notify_msg\`":\`"$notify_msg\`"}" `
  http://$HAHOST/api/events/$EVENTTYPE

In the script, replace C:\Users\<username>\ha_token.xml with wherever you put the credential file and replace HA_DOMAIN_OR_IP with the domain or ip address you reach HA at.

Also replace [email protected] with whatever you called your UPS. You’ll notice that’s hard-coded in here and it wasn’t in the first script. That’s because I got lazy since I only had one UPS. If you have multiple UPS’s hooked up then I can help you pass that value through as well. It’s just a bit annoying because of how NOTIFYMSG works.

And that’s it. No more unencrypted token. Pretty neat trick too with the Windows event → HA event, I used it to help monitor a few other things as well.

Shutdown for HA

For me I wanted HA to shutdown when the UPS says shutdown even though HA isn’t directly connected to it. That’s because my HA machine is POE powered by my switch which is on the same UPS, so if it says shutdown then HA needs to do that too. If this interests you then that can be handled with this automation:

- id: ha_ups_shutdown
  name: Shutdown HA on NUT event
  trigger:
  - platform: event
    event_type: nut.ups_event
    event_data:
      notify_type: SHUTDOWN
  action:
  - service: hassio.host_shutdown

Note that this assumes you followed my steps above in “Signaling HA” and didn’t stop at setting up the NUT integration. If you want to do this, set up the events. UPS’s don’t give a lot of time here when they reach this point so you want to start shutting down as soon as possible. The NUT integration uses polling so if you wait for the status sensor to say shutdown you’ll lose some time.

Update warning

I would be very surprised if this didn’t break on an update to the NUT service given all the moving around of dlls and dependencies. I kind of hope it does and they clean up the install process tbh. Regardless wanted to give a heads up to be cautious with updates to this service as those steps seem quite fragile.

3 Likes

Awesome guide!

Do you have a Linux equivalent of the notify script for someone who doesn’t use the add-on (I run NUT directly on the Debian OS of HA)?

I have everything working there except for the events above.

The nut addon does, here’s the one the addon uses as its NOTIFYCMD script:

Just replace SUPERVISOR_TOKEN with a long-lived access token. And supervisor/core in the url with your HA hostname or ip.

1 Like

Thanks! I didn’t see that.