Skip to content

tripplitesu: extend device control and improve command handling - #3722

Draft
TomZanna wants to merge 5 commits into
networkupstools:masterfrom
TomZanna:master
Draft

TomZanna wants to merge 5 commits into
networkupstools:masterfrom
TomZanna:master

Conversation

@TomZanna

Copy link
Copy Markdown
Contributor

Note on the current state of this PR:
This is an initial draft, and I’m mainly opening it to get some early feedback on the overall structure and approach before spending more time polishing it.

I used Cline with DeepSeek V4 Flash during the development of this work.
I realize that this may eventually be better split into several more focused/atomic commits or separate PRs. I’m happy to restructure it based on maintainer feedback. For now, I preferred to put the work out for review rather than keep polishing it locally indefinitely and risk never submitting it.

Also, please don’t hesitate to use AI to review or summarize the changes if that makes the initial pass easier. Since this is mainly a first-pass review to get feedback on the direction and identify obvious issues, I don’t want to take up more of your time than necessary. Just mention it in the review/PR if AI assistance was used.

  • Added the offdelay and startdelay settings to ups.conf (in seconds and minutes, respectively, matching the units used by the corresponding device commands). Also added support for an optional delay (in seconds) passed through the value argument of the shutdown.return, shutdown.reboot, and shutdown.reboot.graceful instant commands. This allows the delay to be selected per invocation without restarting the driver or changing its configuration.

  • Added control of individual outlet banks through the outlet.n.load.off and outlet.n.load.on instant commands, along with the corresponding outlet.count, outlet.n.status, and outlet.n.switchable variables.

  • Added the shutdown.stayoff instant command and aligned the shutdown-related commands with their intended return-delay behavior:

    • shutdown.reboot and shutdown.reboot.graceful request the load to return immediately after shutdown. The device will only restart the load when the restart delay is longer than the shutdown delay.
    • shutdown.return and shutdown.stayoff clear the return delay, so the load returns only when mains is (re)applied, or remains off, respectively.
  • Added the following variables and instant commands:

    • Read/write variables: ups.beeper.status, ups.delay.start, ups.start.auto, ups.watchdog.status, outlet.delay.start, and input.eco.switchable
    • Read-only variable: battery.date
    • Instant commands: beeper.mute and clear.fault.record
    • Added validation of input.transfer.low and input.transfer.high against the ranges reported by the device, exposed through input.transfer.low.min/.max and input.transfer.high.min/.max.
  • Instant commands and variable writes now report failures when the device rejects an operation or does not respond, instead of unconditionally reporting success.

  • Added the command_delay setting to ups.conf to enforce a minimum interval, in milliseconds, between commands sent to the UPS. This works around devices that fail to respond when commands are sent too soon after one another. The interval is measured from the previous command, so an idle connection is not penalized. The special value -1 starts the driver without an initial delay and enables one-second command pacing only after a command times out. See [PR drivers/tripplitesu.c: add 1s delay in command_send #3273].

Add the optional 'value' argument to the upscmd man page synopsis
and describe both the 'command' and 'value' parameters.

Signed-off-by: TomZanna <git@tomzanna.com>
Document and implement command_delay automatic timeout handling, plus
offdelay and startdelay parameters, and expose outlet.count and
ups.watchdog.status variables.

Signed-off-by: TomZanna <git@tomzanna.com>
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

A ZIP file with standard source tarball and another tarball with pre-built docs for commit e506a3a is temporarily available: NUT-tarballs-PR-3722.zip.

@AppVeyorBot

Copy link
Copy Markdown

✅ Build nut 2.8.5.5554-master completed (commit eaf8b63f12 by @TomZanna)

…ings

Add read-only input.transfer.reason variable from the device's recorded
bypass code, refreshed each poll and cleared by clear.fault.record. Stop
publishing device settings (outlet.delay.start, ups.start.auto,
ups.watchdog.status, input.eco.switchable, ups.beeper.status) with assumed
defaults; expose them only once the device reports them, retaining last
known values with a warning when the device stops responding.
@AppVeyorBot

Copy link
Copy Markdown

✅ Build nut 2.8.5.5558-master completed (commit 18766b1f95 by @TomZanna)

@AppVeyorBot

Copy link
Copy Markdown

@jimklimov jimklimov added Tripp Lite Incorrect or missing readings On some devices driver-reported values are systemically off (e.g. x10, x0.1, const+Value, etc.) Shutdowns and overrides and battery level triggers Issues and PRs about system shutdown, especially if battery charge/runtime remaining is involved AI For good or bad, machine tools are upon us. Humans are still the responsible ones. labels Oct 2, 2026

@jimklimov jimklimov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks quite solid, thanks! One group of comments I have is about terminology (see concerns about "outlet bank" below), another is about readability of logged messages - many places where I think there should be a space between %s tokens... or maybe I missed something as I read the code. There may also be some de-duplication possible with existing common code.

Comment thread docs/man/tripplitesu.txt Outdated
Comment on lines +148 to +150
A command that is reported as failed had at least one of the operations it
performs rejected by the device (or unanswered), so a partially applied
command is possible and worth re-checking with linkman:upsc[8].

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would upsc help with the re-check? Would the failure be exposed in an alarm/status? (If so, clarify in the doc).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point... upsc only shows the published state, and a rejected write does not change it,
so the old sentence was misleading. Reworded so that is clear that the driver logs which operation was
rejected, and what is published is what the device actually did, as reported on the next
poll. Nothing is surfaced as an alarm or a status variable, and the text no longer implies
otherwise.

Comment thread docs/man/tripplitesu.txt Outdated
with a warning for every failed read.

*outlet.count*::
Total number of controllable outlet banks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, I assume "outlet bank" is same concept as "outlet group" named in existing code/docs? There is a whole section on them in docs/nut-names.txt.

Likewise (also for code) there is already a documented data point name outlet.group.count

That said, there is also precedent (in SNMP mappings mostly) about outlet.group.count, outlet.count and outlet.group.%i.count (IIRC reflecting the amount of groups, total sockets, and sockets in the particular group, if exposed by the device like ePDU). In many cases, a larger UPS may provide a couple or more groups of 4-8 sockets which turn on/off as one bundle, and sometimes address high-power C19 sockets (where a PDU might be connected with a dozen or more devices fed) for stand-alone management.

So please revise which one fits this device/driver best to use them somewhat consistently :)

Comment thread docs/man/upscmd.txt
*upscmd* -l 'ups'

*upscmd* [-A 'authconf'] [-u 'username'] [-p 'password'] [-w] [-t <timeout>] 'ups' 'command'
*upscmd* [-A 'authconf'] [-u 'username'] [-p 'password'] [-w] [-t <timeout>] 'ups' 'command' ['value']

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, we were just two decades late on documenting this change :D

Comment thread drivers/tripplitesu.c Outdated
Comment on lines +315 to +325
/* Milliseconds elapsed since '*start', as a signed value ("now" is expected
* to be later than the reference time). */
static long elapsed_milliseconds(const struct timeval *start)
{
struct timeval now;

gettimeofday(&now, NULL);
return (long)(now.tv_sec - start->tv_sec) * 1000L
+ (long)(now.tv_usec - start->tv_usec) / 1000L;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is neat, but I wonder if common.c already has something of the sort - check elapsed_since_timeval(), IMHO it looks like a stronger alternative?

Comment thread drivers/tripplitesu.c Outdated
Comment on lines +495 to +496
upslogx(LOG_ERR, "device rejected or did not answer [%s%s]",
command, NUT_STRARG(parameters));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this want a third %s in the middle, printed as: ((parameters && *parameters) ? " " : "") to separate from command?.. Or just a space, as NUT_STRARG would print a <null> if applicable anyway?

Comment thread drivers/tripplitesu.c Outdated
do_command(SET, IDENTIFICATION, response, NULL);
if (!send_set_command(IDENTIFICATION, response))
upslogx(LOG_ERR, "%s: device rejected or did not "
"answer [%s%s]", __func__, IDENTIFICATION,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe similar comment as elsewhere, now about spacing between ID and response?

Comment thread drivers/tripplitesu.c Outdated
do_command(SET, TRANSFER_VOLTAGE, response, NULL);
if (!send_set_command(TRANSFER_VOLTAGE, response))
upslogx(LOG_ERR, "%s: device rejected or did not answer "
"[%s%s]", __func__, TRANSFER_VOLTAGE, response);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe similar comment as elsewhere, now about spacing between TV and response?

Comment thread drivers/tripplitesu.c Outdated
do_command(SET, TRANSFER_VOLTAGE, response, NULL);
if (!send_set_command(TRANSFER_VOLTAGE, response))
upslogx(LOG_ERR, "%s: device rejected or did not answer "
"[%s%s]", __func__, TRANSFER_VOLTAGE, response);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe similar comment as elsewhere, now about spacing between TV and response?

Comment thread drivers/tripplitesu.c Outdated
do_command(SET, VOLTAGE_SENSITIVITY, parm, NULL);
if (!send_set_command(VOLTAGE_SENSITIVITY, parm))
upslogx(LOG_ERR, "%s: device rejected or did "
"not answer [%s%s]", __func__,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe similar comment as elsewhere, now about spacing between VS and parm?

Comment thread drivers/tripplitesu.c
Comment on lines +789 to +794
/* Read the watchdog setting and publish ups.watchdog.status. The device
* reports "<timeout in seconds>;<restart alarm flag>", where a timeout of 0
* means that the watchdog is disabled. This is auxiliary information, so a
* missing or unexpected answer must not mark the whole UPS as stale: a
* warning is logged and the previously published value is left in place. */
static int get_watchdog(void) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting - can this use the UPS as an external watchdog (e.g. reboot the computer if it missed a few polls)? Can this then be disabled (by a command and perhaps by graceful driver exit), for example when we know we are bringing the driver down for package upgrade, system shutdown, etc.?

@TomZanna TomZanna Oct 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if polling stops while it's armed the UPS just cuts power to the load.
Right now, you can disarm it manually via upsrw -s ups.watchdog.status=disabled [ups-name]. I'll test if disarming it in upsdrv_cleanup() is doable over the next few days and push a commit.

@jimklimov jimklimov added this to the 2.8.6 milestone Oct 2, 2026
@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

…y in man page

Improve tripplitesu driver error handling to exit upon intermediate
errors rather than continuing. Update the man page to consistently use
"outlet" terminology instead of "outlet bank", and expand documentation
of the watchdog setting and command failure behavior for clarity.

Signed-off-by: TomZanna <git@tomzanna.com>
@AppVeyorBot

Copy link
Copy Markdown

✅ Build nut 2.8.5.5565-master completed (commit cecbe3ba76 by @TomZanna)

@AppVeyorBot

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI For good or bad, machine tools are upon us. Humans are still the responsible ones. Incorrect or missing readings On some devices driver-reported values are systemically off (e.g. x10, x0.1, const+Value, etc.) Shutdowns and overrides and battery level triggers Issues and PRs about system shutdown, especially if battery charge/runtime remaining is involved Tripp Lite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants