[Nut-upsuser] A proper way to modify battery.charge.low persistently
Frantisek Rysanek
Frantisek.Rysanek at post.cz
Fri Nov 10 16:08:50 GMT 2023
Dear fellow NUT-UPS users,
I'm writing this message mostly "for the record" - for others who
will follow in my footsteps.
Apparently, I'm not the only one, trying to find a way to "make my
UPS to initiate the graceful shutdown process earlier".
The topic is kind of documented in various different places, and
there are a number of howto's and forum posts in the interwebs that
don't seem to carry all the nuance in one place :-)
My scenario: I have a relatively cheap and low-end UPS, called the
Ellipse ECO 650, nowadays under the Eaton brand (MGE no more).
I did some research ahead of my purchase and picked a UPS with
support in "nut" (and not having a noisy transformer or a fan).
In Debian 11, I have installed the nut-client and the nut-server and
pretty much went headlong into the config files in /etc/nut, where
the config isn't very complicated.
- you need to load the driver for your UPS
(config in ups.conf, mine uses the usbhid-ups)
- in my case nut.conf only says mode=standalone
- upsd.conf can in fact stay empty
- I have two user entries in upsd.users, one of which is for upsmon,
the other for a human admin (me)
- and upsmon.conf contains several relevant rows, first and foremost
a pointer to the UPS driver instance on the local host.
Everything went pretty much smoothly, except for one thing:
in its default config, my UPS waits until the charge percentage is
depleted down to 20%, before finally initiating a shutdown.
I prefer the UPS to initiate the shutdown earlier, because my major
concern is that the small server must shut down gracefully.
It is NOT my point to keep the server alive for as long as possible,
during a power outage.
If you follow the docs to NUT, you will soon stumble over upsctl /
upsc / upsrw.
And, upsc will tell you about a UPS variable called
battery.charge.low
This is an internal, "device-side" variable kept by the UPS firmware,
and in my case it comes at 20% ex works.
It took a few tries (and checking a manpage) to get upsrw to change
this to 90%, and run a practical test.
That worked well.
I then restarted the server and tried again... hmm. No response to
pulling the mains cord, the battery was already several % below 90%
and the system kept running! What ho?
Try upsc... ahh yes, battery.charge.low is back at 20%.
So... the variable is not stored in my UPS, and the configured
threshold is ephemeral... not sure if to the UPS or to the driver
running on the host PC. Makes no difference. The point is that, to
put this UPS variable to practical use, we need to configure it
permanently / persistently.
And, this is where the documentation feels a little sketchy.
The setting command in the shell goes like
upsrw -s battery.charge.low=90 -u my_user -p my_passwd eaton1
But... is there perhaps some systemic way where to configure a UPS
variable? So that it gets configured first thing after a reboot?
Turns out that there isn't.
You need to enter this into a script somewhere.
I was wondering about Systemd's ExecStartPost, but as the nut-server
service unit specifies "type=forking", there is a risk that the
ExecStartPost will run earlier than the nut-server has managed to
start listening... which can be solved by some minimal further
scripting, ...but I ended up just tucking that single command into
/etc/rc.local :-) And I had to prepend a few seconds of delay even
there, otherwise the variable sometimes would not get configured on
startup.
_Now_ the variable is persistent.
I said that there wasn't a systemic place to put the variable alone,
in order to have NUT do the setting on startup for you.
Which may sound perplexing to you, if you have noticed config options
such as
override.battery.charge.low
in ups.conf.
Note that this is a bit of a false friend.
This only applies, if at the same time you configure
ignorelb
Which is an option telling NUT, *not* to act upon the Low Battery
status flag, as reported by the UPS.
In that case, NUT *itself* follows the instantaneous battery.charge
(as reported by the UPS) and NUT *itself* evaluates that the
override.battery.charge.low has been trespassed.
Without ignorelb, NUT merely watches for the LB status flag from the
UPS, and it is the UPS (or its driver?) watching the charge level and
flagging LB when the conditions are met. And, the
override.battery.charge.low option in ups.conf does not apply =
does not get posted to the UPS driver+firmware.
See also the ups.status variable in the output of "upsc eaton1":
OL = on line
OB = on battery
LB = low battery
So, effectively you have a choice:
A) put upsrw in /etc/rc.local or some such, to tell the UPS when to
flag "low battery"
B) tell NUT to ignore LB signaling from the UPS, and instead evaluate
the threshold in software, based on an override option
Another thing that took me a while was to realize, how the whole
graceful shutdown process is going. The process isn't nearly as
straightforward as it may seem at a first glance: "Battery low, SNAP,
lights out". Nonono.
The sequence is described in quite some detail in official
documentation (link to "configuration notes" follows below).
In rough outlines, in a standalone setup, the process goes like:
1) the power goes out, which the UPS signals to the server.
You get a message on the console and in the log, but so far nothing
else happens.
2) the UPS reaches the "battery low threshold", and raises the LB
flag
3) NUTS on the host machine (master monitor instance) notices this
and initiates a shutdown = tells the "init" daemon to orchestrate a
graceful shutdown.
4) at the very end of the "init" daemon's shutdown dance, there is a
stage where all meaningful services have been shut down, all volumes
have been unmounted, and there is a "space" where pieces of software
or config can have a last wish, before the power switch gets flipped.
In systemd parlance, you are free to place your scripts in
/lib/systemd/system-shutdown/
Or you could probably write some unit files to achieve a similar
effect including some dependencies... but just dropping a script in
this directory does work.
And, this is where the nut-monitor package in Debian places its
script, called "nutshutdown".
This script conditionally calls "upsdrvctl shutdown", if a killpower
file is found. You don't want to shut down your UPS upon any regular
shutdown, right? So that's the purpose of
POWERDOWNFLAG /etc/killpower
in /etc/upsmon.conf .
Within the systemd "system-shutdown" section, on my Debian system,
the only other script is mdadm.shutdown = disassemble any MD RAID
arrays for a good measure. (Not really needed IMO, as by now, all
data has been synced to spinning rust as part of umount.)
So in item 4), the UPS gets commanded to disconnect power on its
output.
5) but wait, that is not all yet. The UPS waits a few more seconds,
before actually cutting power to the load. See the UPS variable
ups.delay.shutdown
in my case == 20 seconds.
This means that anything within the systemd system-shutdown directory
should have a chance to finish.
Actually my server manages to perform an ATX powerdown (transition
into ACPI S5) - and then it waits in this state for about a dozen
seconds, before the external UPS actually flips the switch.
If it comes to this end, the server is ready to auto-start upon the
mains coming back up.
Now... what happens if mains power at the wall is restored within
that last ~20s grace period? (I mean ups.delay.shutdown.)
That's an interesting question, and to me the jury is out.
May have to do with the configuration/arrangement of "wakeup by USB".
I've tried poking around in
cat /proc/acpi/wakeup
grep . /sys/bus/usb/devices/*/power/wakeup
cat /sys/bus/usb/devices/1-4/product
echo enabled > /sys/bus/usb/devices/1-4/power/wakeup
...but that didn't seem to have any effect.
During the 20s grace period just before "UPS load off", I don't get
the PC woken up by the UPS.
Upon closer inspection, it actually seems that:
ups.delay.shutdown
ups.delay.start
are not respected by the UPS.
It waits about 15 seconds with the shutdown, and about 7 seconds with
load startup.
Also, if I plug the cord back within its hardwired 15second "grace
period upon the shutdown command received via USB", and I am patient,
the UPS still turns the load off. For those roughly 7 seconds. So the
wakeup via USB is not actually needed. Except that, within those
approx 7 seconds, the StandBy consumption of my PC doesn't manage to
deplete the PSU's primary capacitor, i.e. the server doesn't register
a cold power outage, therefore does not even try to start up, when
the UPS turns the mains back on.
The UPS is relatively dumb. You get what you pay for. The UPS
Companion software from Eaton, when used with this UPS model, has
relatively little to offer in the way of configuration. There are
some options for host-based operation if the "UPS Companion software"
itself, and probably no configurable knobs for the UPS.
I hope this helps someone...
Frank
References:
https://networkupstools.org/docs/user-manual.chunked/Configuration_not
es.html
https://networkupstools.org/docs/user-manual.chunked/ar01s06.html#UPS_
shutdown
https://www.mail-archive.com/nut-upsuser@alioth-lists.debian.net/msg01
334.html
https://forum.openwrt.org/t/nut-ups-does-not-trigger-lowbattery-shutdo
wn-please-help/91252
https://forums.debian.net/viewtopic.php?t=156046
https://devicetests.com/wakeup-linux-wireless-usb-keyboard-mouse
More information about the Nut-upsuser
mailing list