This document intends to detail the change log for relatively recent work (roughly since the source code was tracked in Git).
This change log section represents git commits in range v2.8.5..HEAD (commits 0e051f9f6..2caa3c875).
drivers/apcmicrolink-maps.c: fix(apcmicrolink): read ups.beeper.status as a bitmask, not an enum ups.beeper.status matched the exact values 193/194 (0xC1/0xC2), taken from round-tripping PowerChute writes on one SCL500RM1UC. Decompiling PowerChute’s own CompositeAudibleAlarm class shows this is actually a bitmask: bit 0 (0x01) means enabled, bit 1 (0x02) means disabled. The 0xC1/0xC2 values only worked because that particular unit always sets two extra bits (0xC0) alongside the enabled/disabled bit. A second device (APC Smart-UPS X1500, SMX1500RM2U, reported in https://github.com/networkupstools/nut/issues/3587) reports 0x09
input.sensitivity is an opaque
string [#3591] Per investigation of current code posted in
https://github.com/networkupstools/nut/pull/3591#issuecomment-5558864820
common values include: auto, low, reduced, medium,
normal, high in various order and with 2 to 4 of these present
in particular drivers; also H, M, L; also Normal,
Reduced, Low (cased).
self.__widgets, but __attempt_reconnect() lives in the
gui_updater class, which has no __widgets attribute
(name-mangled to _gui_updater__widgets). The widgets belong to
the interface class and are reached, as the surrounding lines
already do, through self.__parent_class._interface__widgets. The
branch is taken whenever PyNUT.AuthConf.getAuthConf() returns an
object, which is the common case, so every reconnection attempt
raised: AttributeError: gui_updater object has no attribute
_gui_updaterwidgets In the Qt variants an exception escaping a
slot is fatal: PyQt turns it into qFatal() and the process aborts
(SIGABRT) instead of reconnecting. Reproduced on Arch Linux, PyQt6
6.11 / Qt 6.11.2, by restarting upsd while NUT-Monitor-py3qt6
(master, 59fad7a97) was connected. Same one-line fix applied to
the py3qt5, py3qt6 and py2gtk2 variants. Co-Authored-By: Claude
Fable 5.1 <noreply@anthropic.com>
syslog: huawei-ups2000: instcmd: command
[bypass.start] reg1 is negative huawei-ups2000: instcmd: command
[shutdown.return] reg1 is negative huawei-ups2000: instcmd: command
[shutdown.reboot] reg1 is negative huawei-ups2000: instcmd: command
[shutdown.reboot.graceful] reg1 is negative Because the regression
was introduced shortly after the initial driver was merged into the
upstream, these instant commands do no work in any NUT version
(with the exception of the earliest development build). This
problem was not discovered during pre-merge test for the same
reason: it’s a regression that occurred after the Pull Request was
merged. In huawei-ups2000, all instant commands are driven by a
lookup table to consolidate the logic to a central dispatcher. Some
commands are dispatched to a (*handler_func)(uint16_t reg1)
function, which is used to handle commands that needs additional
processing. Not all *handler_func actually reads "reg1", it’s an
optional argument. Because declaring the function to accept a
variadic or a (void *) would be over-engineering, a simple unsigned
16-bit value is used, *handler_func may or may not use it. During
the project-wide "fightwarn" refactor campaign, it was noticed that
"reg1" is stored in the lookup table as "int16_t" (-1 indicates an
unneeded register), but *handler_func accepts a "uint16_t". In case
that *handler_func doesn’t need the register ID from the lookup
table, "reg1" is implicitly converted from -1 to 65535. This is
harmless, since "reg1" is not actually used in this case.
Unfortunately, in an attempt to make the code "safe" by suppressing
compiler warning, an incorrect range check was added to the
function, which refuses to invoke *handler_func if "reg1" is
negative. As a result, all instant commands that don’t use "reg1"
are broken. These commands include bypass.start, shutdown.return,
shutdown.reboot, and shutdown.reboot.graceful. To fix this
problem, fix the implicit type conversion correctly by change the
"reg1" data type in the lookup table from "int16_t" to "uint16_t",
and remove the "negative reg1" check. As defensive programming. all
*handler_func that actually need "reg1" would check and abort if
"reg1" is 0. Fixes: e9f02e242acf ("drivers/huawei-ups2000.c:
instcmd(): range-check and cast for ups2000_write_register() and
handler_func()")
docs/Makefile.am, docs/apcmicrolink-descriptors.txt, docs/new-drivers.txt: docs(apcmicrolink): document the descriptor format the driver parses Adds docs/apcmicrolink-descriptors.txt, covering the frame/page/descriptor structure this driver parses: frames as [page id][width data bytes][2 byte checksum], the flat-blob assembly from page N at offset N * width, and the descriptor table that maps this wire structure to descriptor paths
qr/.../ to define regexes
[#1953]
model [#3581] At least when included into
tests, this code fails on some static analyzers: In file included
from fixreportdesctest-tripplite-hid.c:60:
../drivers/tripplite-hid.c: In function ‘tripplite_chemistry_fun’:
../drivers/tripplite-hid.c:179:14: error: argument 1 null where
non-null expected [-Werror=nonnull] 179 \| if
(!strcmp(model, "1003")) { | ~~~~~~~~~~ In
file included from fixreportdesctest-tripplite-hid.c:35:
/usr/include/string.h:156:12: note: in a call to function ‘strcmp’
declared ‘nonnull’ 156 \| extern int strcmp (const char *s1, const
char *s2) CC gpiotest-generic_gpio_liblocal.o \|
~~~ In function ‘tripplite_chemistry_fun’, inlined from
‘tripplite_chemistry_fun’ at ../drivers/tripplite-hid.c:171:20:
../drivers/tripplite-hid.c:184:14: error: argument 1 null where
non-null expected [-Werror=nonnull] 184 \| if
(!strcmp(model, "2005")) { | ~~~~~~~~~~
/usr/include/string.h: In function ‘tripplite_chemistry_fun’:
/usr/include/string.h:156:12: note: in a call to function ‘strcmp’
declared ‘nonnull’ 156 \| extern int strcmp (const char *s1, const
char *s2) | ~~\~ cc1: all warnings being treated as
errors
apcmicrolink driver with USB
HID support [#3579]
NEWS.adoc, data/cmdvartab, data/driver.list.in,
docs/man/apcmicrolink.txt, docs/nut-names.txt, docs/nut.dict,
drivers/Makefile.am, drivers/apcmicrolink-usb.c,
drivers/apcmicrolink-usb.h, drivers/apcmicrolink.c:
feat(apcmicrolink): add USB HID transport with HID-PDC fallback
Tunnel the Microlink protocol over USB HID interrupt IN/OUT for
models with no serial port (porttype = usb). Tested against a
single APC Smart-UPS 500 (SCL500RMI1UC); experimental even relative
to the rest of this already-experimental driver. - Session startup
on this hardware is intermittently unreliable and can take 90s\+ to
respond; refusing to start on handshake failure was the top "driver
won’t start" complaint. If the device also exposes standard HID
Power Device usages, start on that fallback data
(ups.status/battery.charge/battery.runtime) instead, and bring up
outlet-group data/commands automatically once the Microlink tunnel
connects, no restart needed. hid_fallback=no opts out. - USB read
timeout raised to 1s (vs 100ms serial) and handshake retries raised
to 10: this device’s interrupt pipe interleaves unrelated Input
reports with Microlink replies, and a 100ms/3 retry budget starves
the handshake before real data arrives. - Add load.cycle and
per-outlet-group instant commands (outlet.group.N.load.,
outlet.group.N.shutdown.), and outlet.group.N.switchable to mark
the unswitched main bank (group 0) as having no switching commands.
make memcheck-NIT{-sandbox,}{-devel,} attempts
voltronic MIB subdriver, bump
snmp-ups version [#3570]
val for all code
paths [#3563]
cypress/cypress_0665_5161_subdriver subdriver fix on
deployments and provided workaround instructions.
cypress/cypress_0665_5161_subdriver subdriver fix on
deployments and provided workaround instructions.
cypress_drain_quirk be
configured via ups.conf [#3563]
*.adoc do
dynacfgPipeline.appliesToChangedFilesRegex_FILES_TXT
docs/Makefile.am goal.
\\.\pipe\upssched, only fall back to it [#3525]
\n [#3525]
\n
[#3525]
reconnect_max_tries in one
place [#3541]
reconnect_max_tries setting for NUT drivers [#3541]
[admin@:12345] being treated
as localhost [#3329]
gui_status_message() call from __check_gui_fields [#3514] It
was causing relevant messages (like mismatches or discovery
results) to disappear instantly.
list <ups>
operation [#2567]
background() method [#3437]
SKIP: can't open existing event log NAMED_PIPE
messages [#3527]
cut * Consider starting AND
ending characters of input hash string for more randomness
make-oriented logs [#1711]
default-all-errors failure messages for
easier search in make-oriented logs [#1711]
scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: NUT-Monitor: Add automatic reconnection mechanism for Qt5 and Qt6 - Add automatic reconnection when connection to NUT server is lost - Implement proper teardown of old PyNUTClient instances - Use specific exceptions (socket.error, ConnectionError, EOFError, OSError, PyNUT.PyNUTError) - Prevent multiple simultaneous reconnection attempts - Add was_online flag to track connection state changes
operator= [#3329]
.github/workflows/01-make-dist-worker.yml, .github/workflows/05-codeql.yml: build(deps): bump actions/cache from 5 to 6 Bumps actions/cache from 5 to 6. - Release notes - Changelog
.github/workflows/01-make-dist-worker.yml, .github/workflows/05-codeql.yml: build(deps): bump actions/checkout from 6 to 7 Bumps actions/checkout from 6 to 7. - Release notes - Changelog
case
WITH_SSL_CLIENT_CERTIDENT ... if WITH_SSL_CLIENT != none [#1711]
certutil or
openssl into prepare_NIT_certs() where we would inevitably use
them [#1711]
cp -prf failed [#1711]
--
--
and arrow with =>
file type parsing fails for text with some but not
other wide characters (so it is "data" and not some sort of
"text"). This test was added for C sources as well, but keep in
mind that the final authority is the actually used compilers -
which are free to complain or not.
unset CCACHE_DIR if provided but empty Notably,
ccache-3.7.12 in FreeBSD 15 breaks CI build with that complaint.
--
[#3447]
--
[#3447]
drivers/libusb1.c: use explicit context; drop per-close libusb_exit() [#598] The original code called libusb_init(NULL) on every nut_libusb_open() and libusb_exit(NULL) on every nut_libusb_close(), bumping and decrementing the default context’s refcount. That violates the documented contract for libusb_exit, which "should be called after closing all open devices and before your application terminates", and on certain firmware it wedges indefinitely: when libusb_exit hits the 1→0 transition it tears the context down, which waits on libusb’s internal sync primitives for outstanding URBs to drain. URBs orphaned by libusb_reset_device or by an unexpected device disconnect never drain, so the wait never returns. The deadlock is reachable from any reconnect path. It was first recognized by 4f84b7f92 ("don’t libusb_exit() when closing a previously opened device"), which dropped the call in nut_libusb_open()'s rematch loop. The fallthrough reconnect path in qx_command and the rest of the driver lifecycle still hit it. With the companion change in nutdrv_qx.c (a4813f868) that escalates persistent LIBUSB_ERROR_OVERFLOW to libusb_reset_device, the deadlock window opens on every escalation and was hit reproducibly on the 0665:5161 Cypress USB-serial bridge family (Salicru SPS, Ippon, ViewPower, Voltronic Power UPSes; see #598, #993, #2453). Switch to an explicit libusb context owned end-to-end: - Initialize once on the first nut_libusb_open() and register a matching one-shot atexit handler. - Remove libusb_exit() from nut_libusb_close(); the close path now only calls libusb_close(), which is non-blocking. - Replace libusb_get_device_list(NULL, …) with the explicit context so the default context is never touched.
usb_resetter --reset-device.
NEWS.adoc, data/driver.list.in, docs/man/Makefile.am, docs/man/ragtech.txt, docs/nut.dict, drivers/Makefile.am, drivers/ragtech.c, include/wincompat.h: ragtech: new driver for Ragtech UPSes (USB CDC-ACM, family 10) Targets Brazilian-built Ragtech "Easy Pro" / NEP / TORO / INNERGIE / OneUP devices that present themselves as USB CDC-ACM (VID 0x04D8, PID 0x000A, Microchip PIC firmware). Validated end-to-end against an Easy 2000 TI (reg 0x9A model id = 16) read out of a working OneUP Nitro 2000. Protocol — three opcodes observed in OEM traffic: 0x01 ADDR_HI ADDR_LO VALUE CKSUM write byte 0x02 ADDR_HI ADDR_LO MASK CKSUM AND mask (atomic bit-clear) 0x04 ADDR_HI ADDR_LO COUNT CKSUM read range CKSUM = (ADDR_HI \+ ADDR_LO + VALUE) & 0xFF The CDC-ACM channel ignores baud at the wire but DTR/RTS are interpreted by some Ragtech families as a remote shutdown signal — the driver forces both low after open and does NOT call ser_set_speed() to avoid the tcsetattr() DTR pulse that some Linux tty drivers perform. The full 30-byte main range (0x80..0x9D) plus V_IOUTCALIB (0xF3) and the oscillator calibration pair (0x202/0x203) are read; output frequency is interpolated per devices.xml formula rather than hard-coded. Twenty models from the family-10 device table populate ups.model, ups.realpower.nominal, output.voltage scaling, output.current scaling and battery voltage scaling. input.voltage.nominal switches between 115V and 220V dynamically based on the measured input. Instcmds implemented: shutdown.stayoff — aa 02 00 80 fe (clear AUTOSTART) \+ aa 01 00 98
scripts/Windows/dllldd.sh helper script
refinements [#3420]
ln to create hard links, and tell 7-zip to
store them as such, not as independent files (subject to 7zip
format du jour)
tr ':' '\n' that can be done once.
dirname calls (spawning those processes takes a few seconds in
MSYS2 on Windows), use one sed process.
strings outputs and findings [#3420]
touch -d "-10 seconds", hide the errors
(such request is known to be not too portable)
case.
openssl defaults and old(er) java keytool.
config.cache file from older runs moved to NUT v2.8.6 [#3108]
make
all-quick for less noise
RunTimeCalibrationStatus_BF is
explained in MPAO-98KJ7F_R1_EN Appendix B. This adds defines for
the bitfield and decodes it into
experimental.ups.calibration.result using a string join
converter.
PowerSystemError_BF is explained in MPAO-98KJ7F_R1_EN
Appendix B. This adds defines for the bitfield and decodes it as
alarms in the driver.
BatterySystemError_BF is explained in MPAO-98KJ7F_R1_EN
Appendix B. We were already using the bitfield for the RB
status, this adds defines for the bitfield and decodes it as alarms
in the driver.
_apc_modbus_handle_error function with a simpler retry
mechanism built into _apc_modbus_read_registers. The new
approach: - Retries register reads on ETIMEDOUT errors up to
modbus_retries times (configurable, default 3) - On non-timeout
errors or after retry exhaustion, closes the connection for
reconnection on the next update cycle - Removes the
platform-specific (WIN32/POSIX) timeout detection and the
flush-based recovery that didn’t work anyway (flush is already done
in _apc_modbus_reopen upon reconnection) Also adds a
modbus_retries driver option to configure the number of read
retry attempts, and improves logging for connection open/close
events. This change was inspired by a patch by @marcan to do the
same and from testing the behaviour of apcupsd, noticing that on
my USB unit it times out on read and only succeeds on the first
retry.
_apc_modbus_string_join did not zero terminate the
result in every case, like when all values are NULL.
certpasswd string.