Skip to content

Commit

Permalink
drivers/usbhid-ups.c, docs/man/usbhid-ups.txt: introduce lbrb_log_del…
Browse files Browse the repository at this point in the history
…ay_sec et al [#2347]

Signed-off-by: Jim Klimov <[email protected]>
  • Loading branch information
jimklimov committed Jul 29, 2024
1 parent 1f5b686 commit d15ab31
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 4 deletions.
3 changes: 3 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ https://github.com/networkupstools/nut/milestone/11
- usbhid-ups updates:
* Support of the `onlinedischarge_log_throttle_hovercharge` in the NUT
v2.8.2 release was found to be incomplete. [#2423, follow-up to #2215]
* Added support for `lbrb_log_delay_sec` setting to delay propagation of
`LB` or `LB+RB` state (buggy with APC BXnnnnMI devices circa 2023-2024).
[#2347]
* General suggestion from `possibly_supported()` message method for devices
with VendorID=`0x06da` (Phoenixtec), seen in some models supported by
MGE HID or Liebert HID, updated to suggest trying `nutdrv_qx`. [#334]
Expand Down
14 changes: 14 additions & 0 deletions docs/man/usbhid-ups.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,20 @@ not forcing it to be fully charged all the time. As long as the current value
of `battery.charge` remains at or above this threshold percentage (default 100),
the `OL+DISCHRG` message logging is not triggered by variations of the charge.

*lbrb_log_delay_sec*='num'::
Set to delay status-setting (and log messages) about device entering `LB` or
`LB+RB` state.
+
Some APC BXnnnnMI device models or firmware versions (reportedly 2023-2024)
frequently report low battery, replace battery, and all ok within a couple
of seconds, sometimes but not always preceded by OL+DISCHRG (presumably
calibration). This setting lets the driver ignore short-lived states and
only pay attention if they persist longer than this setting (and the device
power state is `OL`).

*lbrb_log_delay_without_calibrating*::
Set to apply `lbrb_log_delay_sec` even if device is not calibrating.

*disable_fix_report_desc*::
Set to disable fix-ups for broken USB encoding, etc. which we apply by default
on certain models (vendors/products) which were reported as not following the
Expand Down
4 changes: 3 additions & 1 deletion docs/nut.dict
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
personal_ws-1.1 en 3199 utf-8
personal_ws-1.1 en 3201 utf-8
AAC
AAS
ABI
Expand Down Expand Up @@ -100,6 +100,7 @@ BTS
BTV
BUFRD
BUZ
BXnnnnMI
BYP
BZ
BZOFF
Expand Down Expand Up @@ -2144,6 +2145,7 @@ labcd
lan
langid
lasaine
lbrb
ld
ldd
le
Expand Down
129 changes: 126 additions & 3 deletions drivers/usbhid-ups.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,34 @@ static int onlinedischarge_log_throttle_charge = -1;
*/
static int onlinedischarge_log_throttle_hovercharge = 100;

/**
* Per https://github.com/networkupstools/nut/issues/2347 some
* APC BXnnnnMI devices made (flashed?) in 2023-2024 irregularly
* but frequently spew a series of state changes:
* * (maybe OL+DISCHRG),
* * LB,
* * RB,
* * <all ok>
* within a couple of seconds. If this tunable is positive, we
* would only report the device states on the bus if they persist
* that long (or more), only then assuming they reflect a real
* problematic state and not some internal calibration.
*/
static int lbrb_log_delay_sec = 0;
/**
* By default we only act on (lbrb_log_delay_sec>0) when the device
* is in calibration mode of whatever nature (directly reported or
* assumed from other flag combos). With this flag we do not check
* for calibration and only look at LB + RB timestamps.
*/
static int lbrb_log_delay_without_calibrating = 0;
/**
* When did we last enter the situation? (if more than lbrb_log_delay_sec
* ago, then set the device status and emit the message)
*/
static time_t last_lb_start = 0;
static time_t last_rb_start = 0;

/* support functions */
static hid_info_t *find_nut_info(const char *varname);
static hid_info_t *find_hid_info(const HIDData_t *hiddata);
Expand Down Expand Up @@ -1024,6 +1052,12 @@ void upsdrv_makevartable(void)
addvar(VAR_VALUE, "onlinedischarge_log_throttle_hovercharge",
"Set to throttle log messages about discharging while online (only if battery.charge is under this value)");

addvar(VAR_VALUE, "lbrb_log_delay_sec",
"Set to delay status-setting (and log messages) about device in LB or LB+RB state");

addvar(VAR_FLAG, "lbrb_log_delay_without_calibrating",
"Set to apply lbrb_log_delay_sec even if device is not calibrating");

addvar(VAR_FLAG, "disable_fix_report_desc",
"Set to disable fix-ups for broken USB encoding, etc. which we apply by default on certain vendors/products");

Expand Down Expand Up @@ -1376,6 +1410,46 @@ void upsdrv_initups(void)
}
}

val = getval("lbrb_log_delay_sec");
if (val) {
int ipv = atoi(val);
if ((ipv == 0 && strcmp("0", val)) || (ipv < 0)) {
lbrb_log_delay_sec = 3;
upslogx(LOG_WARNING,
"Warning: invalid value for "
"lbrb_log_delay_sec: %s, "
"defaulting to %d",
val, lbrb_log_delay_sec);
} else {
lbrb_log_delay_sec = ipv;
}
} else {
/* Activate APC BXnnnnMI tweaks, for details see
* https://github.com/networkupstools/nut/issues/2347
*/
size_t productLen = hd->Product ? strlen(hd->Product) : 0;

/* FIXME: Consider also ups.mfr.date as 2023 or newer?
* Eventually up to some year this gets fixed?
*/
if (hd->Vendor
&& productLen > 6 /* BXnnnMI at least */
&& (!strcmp(hd->Vendor, "APC") || !strcmp(hd->Vendor, "American Power Conversion"))
&& (strstr(hd->Product, " BX") || strstr(hd->Product, "BX") == hd->Product)
&& (hd->Product[productLen - 2] == 'M' && hd->Product[productLen - 1] == 'I')
) {
lbrb_log_delay_sec = 3;
upslogx(LOG_INFO, "Defaulting lbrb_log_delay_sec=%d "
"for %s model %s",
lbrb_log_delay_sec,
hd->Vendor, hd->Product);
}
}

if (testvar("lbrb_log_delay_without_calibrating")) {
lbrb_log_delay_without_calibrating = 1;
}

if (testvar("disable_fix_report_desc")) {
disable_fix_report_desc = 1;
}
Expand Down Expand Up @@ -2017,6 +2091,8 @@ static void status_set_CAL(void)
status. */
static void ups_status_set(void)
{
int isCalibrating = 0;

if (ups_status & STATUS(VRANGE)) {
dstate_setinfo("input.transfer.reason", "input voltage out of range");
} else if (ups_status & STATUS(FRANGE)) {
Expand Down Expand Up @@ -2199,6 +2275,8 @@ static void ups_status_set(void)
status_set("OL");
}

isCalibrating = status_get("CAL");

if ((ups_status & STATUS(DISCHRG)) &&
!(ups_status & STATUS(DEPLETED))) {
status_set("DISCHRG"); /* discharging */
Expand All @@ -2208,13 +2286,58 @@ static void ups_status_set(void)
status_set("CHRG"); /* charging */
}
if (ups_status & (STATUS(LOWBATT) | STATUS(TIMELIMITEXP) | STATUS(SHUTDOWNIMM))) {
status_set("LB"); /* low battery */
if (lbrb_log_delay_sec < 1
|| (!isCalibrating && !lbrb_log_delay_without_calibrating)
|| !(ups_status & STATUS(ONLINE)) /* assume actual power failure, do not delay */
) {
/* Quick and easy decision */
status_set("LB"); /* low battery */
} else {
time_t now;
time(&now);

if (!last_lb_start) {
last_lb_start = now;
} else {
if (difftime(now, last_lb_start) > lbrb_log_delay_sec) {
/* Patience expired */
status_set("LB"); /* low battery */
} else {
upsdebugx(2, "%s: throttling LB status due to lbrb_log_delay_sec", __func__);
}
}
}
} else {
last_lb_start = 0;
}
if (ups_status & STATUS(OVERLOAD)) {
status_set("OVER"); /* overload */
}
if (ups_status & STATUS(REPLACEBATT)) {
status_set("RB"); /* replace batt */
if (lbrb_log_delay_sec < 1
|| (!isCalibrating && !lbrb_log_delay_without_calibrating)
|| !last_lb_start /* Calibration ended (not LB anymore) */
|| !(ups_status & STATUS(ONLINE)) /* assume actual power failure, do not delay */
) {
/* Quick and easy decision */
status_set("RB"); /* replace batt */
} else {
time_t now;
time(&now);

if (!last_rb_start) {
last_rb_start = now;
} else {
if (difftime(now, last_rb_start) > lbrb_log_delay_sec) {
/* Patience expired */
status_set("RB"); /* replace batt */
} else {
upsdebugx(2, "%s: throttling RB status due to lbrb_log_delay_sec", __func__);
}
}
}
} else {
last_rb_start = 0;
}
if (ups_status & STATUS(TRIM)) {
status_set("TRIM"); /* SmartTrim */
Expand All @@ -2229,7 +2352,7 @@ static void ups_status_set(void)
status_set("OFF"); /* ups is off */
}

if (!status_get("CAL")) {
if (!isCalibrating) {
if (last_calibration_start) {
time(&last_calibration_finish);
upsdebugx(2, "%s: calibration is no longer in place, took %f sec",
Expand Down

0 comments on commit d15ab31

Please sign in to comment.