an adventure to activate energy management control in apcupsd

I have an Back-UPS ES 700

A moment in 2018 with the ups out of use because it needed a new battery gives an opportuntiy to see what features are missing

Have a machine with libvirt and kvm, get a guest and licence a proprietary operating system

Using it virtualised is permitted inside a trusted hypervisor like kvm is permitted and adviseable.

Install powerchute for windows and notice that it can toggle energy management and change settings, that which neither apcupsd and nut do not seem to support. Update, latest nut in git ff860090 - ff860097

Energy management means that the controlled outlets will be on only if the master outlet draws more than a set number of watts.

Thus run powerchute in a windows guest under kvm and attach wireshark in usb debug mode to get the instructions issued to the ups...

Tracing sets with wireshark

Wireshark reveals ups gets sent urb control messages with a REPORT ID and some data, it is quickly noticed that the report id is present as the first octet of the "value"

We can compare apcupsd and powerchute

descriptionreport idvalue
with powerchute alarm on0x03780x7802
with powerchute alarm off0x03780x7801
with apcupsd alarm on0x03180x1802
with apcupsd alarm off0x03180x1801
line sensitivity high0x03350x3502
line sensitivity med0x03350x3501
line sensitivity low0x03350x3500
energy management enable first sets0x38f8f01
energy management enable second sets0x38d8d19
energy management disable first sets0x38f8f00
energy management disable then sets0x38d8d19
energy management set 10watts then sets0x38d8d0a
energy management set 25watts then sets0x38d8d19
energy management set 60watts then sets0x38d8d3c
energy management set custom min is 10watts then sets0x38d8d0a
energy management set custom max is 80watts then sets0x38d8d50
for 0x1337 sent in decimal seconds as ontime0x03910x913713
for 0x55aa sent in decimal seconds as offtime0x03920x92aa55
turn power on now sends (presume sets ontime zero)0x03910x910000
turn power on now then sends (presume sets ontime back to gui setting of 0x13370x03910x913713

The powerchute gui lets user pick watts of between 10 and 80 inclusive and times of between 1 and 65535 seconds

Rewriting reportid into usb hid usages

Then found, when trying to set apctest to do energy management, "report id" is not hardcoded into apcupsd, instead we have to find a value called "usage":

The resolution was found by replugging the UPS usb cable, the ups dumps a mapping table of "report id" to "usage"

I firstly tried usbhid-dump --model=051d:0002 just a big block of hex

Then success, save a wireshark pcap of the device being plugged in, tshark -x -r plugin.pcap -T json usbhid | grep "usbhid.item.local.usage_raw\|usbhid.item.global.report_id_raw"

To correlate, the "usages" and reportids of features supported by both powerchute and apcupsd were compared.

When reading this, notice that the usage, that is the last octet in FF8600XX is preceeded by the corresponding report id, which is the operation code octet sent to the ups over usb hid protocol.

This is in addition to 008400XX and 008500XX with USB-IF defined values usage tables for power devices

                
"usbhid.item.global.report_id_raw": "8c",
"usbhid.item.local.usage_raw": "91",
"usbhid.item.global.report_id_raw": "8d",
"usbhid.item.local.usage_raw": "92",
"usbhid.item.global.report_id_raw": "8e",
"usbhid.item.local.usage_raw": "93",
"usbhid.item.global.report_id_raw": "8f",
"usbhid.item.local.usage_raw": "94",
"usbhid.item.global.report_id_raw": "90",
"usbhid.item.local.usage_raw": "95",
"usbhid.item.global.report_id_raw": "91",
"usbhid.item.local.usage_raw": "96",
"usbhid.item.global.report_id_raw": "92",
"usbhid.item.local.usage_raw": "97",

We have at last some lines for src/drivers/usb/usb.c they are inserted into _known_info[]

The CI_ items are completely made up, they are internal to apcupsd, add them to the table in include/defines.h

I know CI_energy_master_enable is volatile as there is a button on the ups to enable or not energy management, also the threshold can be set by holding the button in until the ups beeps thrice.

  1. {CI_ENERGY_THRESHOLD, 0xFF860092, P_ANY, P_ANY, T_NONE, true},
  2. {CI_ENERGY_MASTER_ENABLE, 0xFF860094, P_ANY, P_ANY, T_NONE, true},
  3. {CI_ENERGY_ONDELAY, 0xFF860096, P_ANY, P_ANY, T_NONE, true},
  4. {CI_ENERGY_OFFDELAY, 0xFF860097, P_ANY, P_ANY, T_NONE, true},

At this point I copy the part of apctest.c that does the ups alarm and have it control the energy management.

It works, can theoretically let do everything that powerchute lets us, in powerchute seconds defaults to 4 and watts to 25.

Being adventurous, I notice that usages 90,91,93 and 95 are defined, though powerchute does not call them. 90 turns out to be unreadable, 91 and 95 return 1, and 93 return 0

I plug in an 18w rated soldering iron into the master port and notice that usage 93 returns 18. Great, this means 93 can be used to measure power draw on tha master outlet.

Doing the hold button until beep thrice reveals the ups adds 10 watts to usage 0xFF860093 and stores it in usage 0xFF860092

  1. {CI_ENERGY_91, 0xFF860091, P_ANY, P_ANY, T_NONE, true},
  2. {CI_ENERGY_DRAW_MASTER, 0xFF860093, P_ANY, P_ANY, T_NONE, true},
  3. {CI_ENERGY_95, 0xFF860095, P_ANY, P_ANY, T_NONE, true},

This led to ACPI virtual battery for QEMU KVM