Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise the stance on ECO/... modes in NUT standard, amend implementations #2850

Merged
merged 43 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6c66ef1
ecomode.auto.start added
masterwishx Nov 22, 2024
ecb84d0
ecomode.auto.start added trying to automate enter to ECO Mode NOT FIN…
masterwishx Nov 22, 2024
dda634a
comments added
masterwishx Nov 22, 2024
61ef37d
str fix
masterwishx Nov 22, 2024
4a39175
more work for function
masterwishx Nov 22, 2024
605349f
fix if
masterwishx Nov 22, 2024
9a65c3e
fix static
masterwishx Nov 22, 2024
c1bc750
drop down function for fix
masterwishx Nov 22, 2024
046b3bb
remove static from vars
masterwishx Nov 22, 2024
16ef142
finalise the function
masterwishx Nov 23, 2024
7a42998
typo fixed
masterwishx Nov 23, 2024
162d660
remove unneeded code from function
masterwishx Nov 23, 2024
b4adf69
rename ecomode.auto.start to ecomode.start.auto
masterwishx Nov 23, 2024
95fcd9c
cosmetic fix
masterwishx Nov 23, 2024
71e3651
add info to nut-names,cmdvartab
masterwishx Nov 23, 2024
c7a60aa
set value to unused
masterwishx Nov 23, 2024
b2a23ba
change getinfo for better results
masterwishx Nov 24, 2024
d1fc491
fix strcmp
masterwishx Nov 25, 2024
1a844cd
drivers/mge-hid.c: cosmetic fixes
jimklimov Feb 8, 2025
3f8bf25
docs/nut-names.txt: propose and describe names for `output.inverter.m…
jimklimov Mar 3, 2025
6a43d73
clients/upsmon.c: pollups(), parse_status(), parse_status(), get_var(…
jimklimov Mar 6, 2025
4b157fd
clients/status.h: note that "ECO" in "ups.status" should no longer ha…
jimklimov Mar 6, 2025
648343a
drivers/dstate.{c,h}, docs/new-drivers.txt, docs/nut.dict: introduce …
jimklimov Mar 8, 2025
9337d11
clients/upsmon.c, common/nutconf.cpp, common/nutwriter.cpp: comment a…
jimklimov Mar 16, 2025
8dcb8ac
drivers/usbhid-ups.c: comment ECO and ESS status handling as obsolete…
jimklimov Mar 8, 2025
9d9b50b
drivers/usbhid-ups.c: upsdrv_updateinfo(): add invmode_init/commit() …
jimklimov Mar 16, 2025
1e15a0c
drivers/mge-hid.c: convert to use invmode_set() [#2708]
jimklimov Mar 16, 2025
411e29d
drivers/nutdrv_qx.c: upsdrv_updateinfo(): add invmode_init/commit() i…
jimklimov Mar 16, 2025
4694dae
drivers/nutdrv_qx_voltronic.c: invmode_set() voltronic eco and eco_ad…
jimklimov Mar 16, 2025
8b3bad8
drivers/apc_modbus.c: use invmode_{init,get,set,commit}() methods for…
jimklimov Mar 8, 2025
c9490ba
docs/nut-names.txt: replace `output.inverter.mode` by `ups.mode` and …
jimklimov Mar 21, 2025
68f48b0
drivers/dstate.{c,h}, docs/new-drivers.txt: rename invmode_* methods …
jimklimov Mar 21, 2025
9d211d9
clients/upsmon.c: pollups(), parse_status(), get_var(): consider "(ex…
jimklimov Mar 21, 2025
847e234
drivers/{apc_modbus,usbhid-ups,mge-hid,nutdrv_qx,nutdrv_qx_voltronic}…
jimklimov Mar 21, 2025
f638728
drivers/mge-hid.c: bump (C) for recent committers
jimklimov Mar 24, 2025
de286f6
data/cmdvartab, drivers/mge-hid.c, drivers/usbhid-ups.c, docs/nut-nam…
jimklimov Mar 24, 2025
7206458
docs/nut-names.txt: fix outlet.n.protect.status and input.eco.switcha…
jimklimov Mar 24, 2025
ca27e06
docs/nut-names.txt, drivers/mge-hid.c, data/cmdvartab: revert "experi…
jimklimov Mar 26, 2025
d57fdde
autoecomode addon without last function needed for 9E model but then …
masterwishx Mar 26, 2025
58f7786
experimental.ecomode.stop.auto function added
masterwishx Mar 26, 2025
3b58f5c
comment about 9E stuck in ECO
masterwishx Mar 26, 2025
e905e16
drivers/mge-hid.c: fix whitespace [#2850]
jimklimov Mar 27, 2025
64466e6
Merge pull request #7 from masterwishx/2850-addon
jimklimov Mar 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clients/status.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ static struct {
{ "BOOST", "VOLTAGE BOOST", 1 },
{ "CAL", "CALIBRATION", 1 },
{ "BYPASS", "BYPASS", 2 },
/* NOTE: "ECO" should not be happening as a status
* or alarm anymore... in NUT core code base */
{ "ECO", "ECO", 1 },
{ "ALARM", "ALARM", 2 },
{ NULL, NULL, 0 }
Expand Down
63 changes: 55 additions & 8 deletions clients/upsmon.c
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,20 @@ static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize)
numq = 3;
}
else
if (!strcmp(var, "buzzword")) {
query[0] = "VAR";
query[1] = ups->upsname;
query[2] = "ups.mode.buzzwords";
numq = 3;
}
else
if (!strcmp(var, "X-buzzword")) {
query[0] = "VAR";
query[1] = ups->upsname;
query[2] = "experimental.ups.mode.buzzwords";
numq = 3;
}
else
if (!strcmp(var, "alarm")) {
/* Opaque string */
query[0] = "VAR";
Expand Down Expand Up @@ -2533,10 +2547,11 @@ static int try_connect(utype_t *ups)
}

/* deal with the contents of STATUS or ups.status for this ups */
static void parse_status(utype_t *ups, char *status)
static void parse_status(utype_t *ups, char *status, char *buzzword, char *buzzwordX)
{
char *statword, *ptr, other_stat_words[SMALLBUF];
int handled_stat_words = 0, changed_other_stat_words = 0;
int handled_stat_words = 0, changed_other_stat_words = 0,
is_eco_buzzword = 0;
st_tree_timespec_t st_start;

clear_alarm();
Expand Down Expand Up @@ -2564,11 +2579,34 @@ static void parse_status(utype_t *ups, char *status)
ups_is_notoff(ups);
if (!strstr(status, "BYPASS"))
ups_is_notbypass(ups);
if (!strstr(status, "ECO"))
ups_is_noteco(ups);
if (!strstr(status, "ALARM"))
ups_is_notalarm(ups);

/* NOTE: ECO Should not be happening as a status or alarm anymore
* but we may still notify about changes in the ups.mode.buzzword */
if (buzzword && *buzzword && (
!strstr(buzzword, ":ECO")
|| !strstr(buzzword, ":ESS")
|| !strstr(buzzword, ":HE")
|| !strstr(buzzword, ":SMART")
)) {
is_eco_buzzword = 1;
} else
if (buzzwordX && *buzzwordX && (
!strstr(buzzwordX, ":ECO")
|| !strstr(buzzwordX, ":ESS")
|| !strstr(buzzwordX, ":HE")
|| !strstr(buzzwordX, ":SMART")
)) {
is_eco_buzzword = 1;
}

if (!strstr(status, "ECO") && !is_eco_buzzword) {
ups_is_noteco(ups);
} else if (is_eco_buzzword) {
ups_is_eco(ups);
}

statword = status;
/* Track what status tokens we no longer see */
state_get_timestamp(&st_start);
Expand Down Expand Up @@ -2615,6 +2653,8 @@ static void parse_status(utype_t *ups, char *status)
handled++;
}
else if (!strcasecmp(statword, "ECO")) {
/* NOTE: ECO Should not be happening
* as a status or alarm anymore */
ups_is_eco(ups);
handled++;
}
Expand Down Expand Up @@ -2748,9 +2788,9 @@ static void parse_status(utype_t *ups, char *status)
/* see what the status of the UPS is and handle any changes */
static void pollups(utype_t *ups)
{
char status[SMALLBUF];
char status[SMALLBUF], buzzmode[SMALLBUF], buzzmodeX[SMALLBUF];
int pollfail_log = 0; /* if we throttle, only upsdebugx() but not upslogx() the failures */
int upserror;
int upserror, got_status, got_buzzmode, got_buzzmodeX;

/* try a reconnect here */
if (!flag_isset(ups->status, ST_CLICONNECTED)) {
Expand All @@ -2766,7 +2806,14 @@ static void pollups(utype_t *ups)

set_alarm();

if (get_var(ups, "status", status, sizeof(status)) == 0) {
if ((got_status = get_var(ups, "status", status, sizeof(status))))
status[0] = '\0';
if ((got_buzzmode = get_var(ups, "buzzword", buzzmode, sizeof(buzzmode))))
buzzmode[0] = '\0';
if ((got_buzzmodeX = get_var(ups, "X-buzzword", buzzmodeX, sizeof(buzzmodeX))))
buzzmodeX[0] = '\0';

if (got_status == 0 || got_buzzmode == 0 || got_buzzmodeX == 0) {
clear_alarm();

/* reset pollfail log throttling */
Expand All @@ -2789,7 +2836,7 @@ static void pollups(utype_t *ups)
ups->pollfail_log_throttle_state = upserror;
ups->pollfail_log_throttle_count = -1;

parse_status(ups, status);
parse_status(ups, status, buzzmode, buzzmodeX);
return;
}

Expand Down
4 changes: 2 additions & 2 deletions common/nutconf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1329,9 +1329,9 @@ UpsmonConfiguration::NotifyType UpsmonConfiguration::NotifyTypeFromString(const
return NOTIFY_BYPASS;
else if(str=="NOTBYPASS")
return NOTIFY_NOTBYPASS;
else if(str=="ECO")
else if(str=="ECO") /* inverter mode, not ups state, for notifications */
return NOTIFY_ECO;
else if(str=="NOTECO")
else if(str=="NOTECO") /* inverter mode, not ups state, for notifications */
return NOTIFY_NOTECO;
else if(str=="ALARM")
return NOTIFY_ALARM;
Expand Down
2 changes: 1 addition & 1 deletion common/nutwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ const NotifyFlagsStrings::TypeStrings NotifyFlagsStrings::type_str = {
"NOTOFF", // NOTIFY_NOTOFF
"BYPASS", // NOTIFY_BYPASS
"NOTBYPASS", // NOTIFY_NOTBYPASS
"ECO\t", // NOTIFY_ECO (including padding)
"ECO\t", // NOTIFY_ECO (including padding); NOTE: inverter mode, not ups state, for notifications
"NOTECO", // NOTIFY_NOTECO
"ALARM", // NOTIFY_ALARM
"NOTALARM", // NOTIFY_NOTALARM
Expand Down
10 changes: 6 additions & 4 deletions data/cmdvartab
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,12 @@ CMDDESC calibrate.start "Start run time calibration"
CMDDESC calibrate.stop "Stop run time calibration"
CMDDESC bypass.start "Put the UPS in Bypass mode"
CMDDESC bypass.stop "Take the UPS out of Bypass mode"
CMDDESC ecomode.enable "Put UPS in High Efficiency (aka ECO) mode"
CMDDESC ecomode.disable "Take the UPS out of High Efficiency (aka ECO) mode"
CMDDESC essmode.enable "Put UPS in Energy Saver System (aka ESS) mode"
CMDDESC essmode.disable "Take the UPS out of Energy Saver System (aka ESS) mode"
CMDDESC experimental.ecomode.enable "Put UPS in High Efficiency (aka ECO) mode"
CMDDESC experimental.ecomode.disable "Take the UPS out of High Efficiency (aka ECO) mode"
CMDDESC experimental.ecomode.start.auto "Put UPS in Bypass mode then High Efficiency (aka ECO) mode"
CMDDESC experimental.ecomode.stop.auto "Take the UPS out of High Efficiency (aka ECO) mode after exiting Bypass mode"
CMDDESC experimental.essmode.enable "Put UPS in Energy Saver System (aka ESS) mode"
CMDDESC experimental.essmode.disable "Take the UPS out of Energy Saver System (aka ESS) mode"
CMDDESC reset.input.minmax "Reset minimum and maximum input voltage status"
CMDDESC reset.watchdog "Reset watchdog timer"
CMDDESC beeper.on "Obsolete (use beeper.enable)"
Expand Down
15 changes: 15 additions & 0 deletions docs/new-drivers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,21 @@ met. Otherwise, setting status to `OB LB` should be preferred.
variable naming scheme,nut-names] for more information.
==============================================================================

Similar functionality can be supported for `experimental.ups.mode.buzzwords`,
where it is tracked dynamically (e.g. due to ECO/ESS/HE/Smart or similar
marketing buzzword modes supported by the device), using the following
methods in the processing loop:

buzzmode_init() -- before doing anything else (clear internal buffers,
etc.)

buzzmode_get(val) -- optionally check if an UPS mode buzzword had been
set since the most-recent buzzmode_init()

buzzmode_set(val) -- add an UPS mode buzzword (vendor:eaton:ECO, etc.)

buzzmode_commit() -- push out the update

UPS alarms
----------

Expand Down
112 changes: 106 additions & 6 deletions docs/nut-names.txt
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ ups: General unit information
| ups.beeper.status | UPS beeper status
(enabled, disabled or muted) | enabled
| ups.type | UPS type (*** opaque) | offline
| ups.mode | Current UPS mode (see the
note below) | line-interactive
| experimental.ups.mode.buzzwords
| UPS mode details, not
classified (opaque string) | vendor:eaton:ECO
| ups.watchdog.status | UPS watchdog status
(enabled or disabled) | disabled
| ups.start.auto | UPS starts when mains is
Expand All @@ -274,6 +279,58 @@ will set `ups.start.auto` to the right value before issuing the command.
That is, `shutdown.stayoff` will first set `ups.start.auto` to `no`,
while `shutdown.return` will set it to `yes`.

[NOTE]
======
When present, the value of `ups.mode` specifies the currently enabled
mode of operation of the inverter and other components in the UPS.
Some devices are wired to only have one mode, others may support several
modes and usually have a way to select which one you want to be currently
active, either via protocol commands or by a physical switch.

There are many marketing keywords of different vendors and device generations
that sometimes correspond to same or very similar concepts, other times overlap
with wildly different meanings.

For example, some devices may be "online" (doing double-conversion and
feeding the load from battery even when wall power is available) when
charging or compensating for poor quality of input power, but become
"line-interactive" or even power off some of their electronics when the
input is deemed reliable.

The `ups.mode` can have one of the following standard values, possibly
changing over time or never changing (for devices with one known mode):

- `online`: battery is always charging on one side, and always feeds the
inverter and so the load on the other (pros: instant protection from
outages; cons: higher overheads of the UPS itself, possibly faster wear
of the battery);
- `line-interactive`: battery is charged and then kept at rest, load is
fed from input, but in case of outage or other troubles the inverter
or other compensation mechanism (trim/buck) would be fired up (after
a small but non-trivial delay);
- `bypass`: inverter and battery are bypassed (e.g. for maintenance),
so input directly feeds the output, until it suddenly does not.

The `experimental.ups.mode.buzzwords` can have one or more values, separated
by spaces, to provide information we know from the device but have not yet
agreed how to reflect it in a well-structured fashion (hence `experimental`
namespace is used):

- `vendor:VENDORNAME:MODENAME`: we know that the "vendor's marketing
buzzword" mode is activated. Users may read vendor documentation for
their device model, and know better than the driver what this actually
means for them. It is recommended to keep vendor names lower-cased
and mode names upper-cased, for more deterministic matching and sorting
in NUT clients.
* A number of `MODENAME` values/patterns may be interpreted by
linkman:upsmon[8] to produce notifications about entering or
exiting the "ECO" mode state.

Other values may be introduced later.

See also `output.inverter.latency`.
======

NOTE: When possible, time-stamps and dates should be expressed as detailed
above in the Time and Date format chapter.

Expand Down Expand Up @@ -309,7 +366,7 @@ input: Incoming line/power information
| input.transfer.high.max | greatest settable high
voltage transfer point (V) | 136
| input.eco.switchable | Input High Efficiency (aka ECO)
mode switch (0-2) | normal
mode switch (opaque string) | normal
| input.sensitivity | Input power sensitivity | H (high)
| input.quality | Input power quality (***
opaque) | FF
Expand Down Expand Up @@ -422,8 +479,27 @@ output: Outgoing power/inverter information
| output.frequency.nominal | Nominal output frequency (Hz) | 60
| output.current | Output current (A) | 4.25
| output.current.nominal | Nominal output current (A) | 5.0
| output.inverter.latency | Delay of inverter activation
when switching to battery
(seconds, floating-point) | 0.01
|===============================================================================

[NOTE]
======
One practical aspect that the users may be actually interested in is the
`output.inverter.latency`, representing the time gap when an outage begins,
after the mains power has disappeared and before the inverter begins to feed
the load from battery. Typical values are 0 for double-conversion devices,
which always feed the load from battery, and 10msec (0.01 seconds in standard
units) for "line-interactive" devices which monitor input status and take time
to react to an outage or breach of thresholds. While common computer power
sources include elements that allow them to slide over such a short outage,
other protected devices (laser printers, Hi-Fi audio) might not, and would
restart.

See also `ups.mode` and `experimental.ups.mode.buzzwords`.
======

Three-phase additions
~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -763,7 +839,7 @@ of the user manual.
| outlet.n.status | Outlet switch status
(on/off) | on
| outlet.n.protect.status | Outlet protection status
(0-2) | protected
(opaque string) | protected
| outlet.n.alarm | Alarms for outlets and PDU,
published in ups.alarm | outlet 1 low
voltage warning
Expand Down Expand Up @@ -944,10 +1020,6 @@ Instant commands
| calibrate.stop | Stop runtime calibration
| bypass.start | Put the UPS in Bypass mode
| bypass.stop | Take the UPS out of Bypass mode
| ecomode.enable | Put UPS in High Efficiency (aka ECO) mode
| ecomode.disable | Take the UPS out of High Efficiency (aka ECO) mode
| essmode.enable | Put UPS in Energy Saver System (aka ESS) mode
| essmode.disable | Take the UPS out of Energy Saver System (aka ESS) mode
| reset.input.minmax | Reset minimum and maximum input voltage status
| reset.watchdog | Reset watchdog timer (forced reboot of load)
| beeper.enable | Enable UPS beeper/buzzer
Expand All @@ -961,3 +1033,31 @@ Instant commands
| outlet.n.load.cycle | Power cycle the outlet immediately
| outlet.n.shutdown.return | Turn off the outlet and return when power is back
|========================================================================

Experimental instant commands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following commands were added to test feature support, but are not expected
to last as part of NUT standard protocol in the long run.

.Vendor-dependent "ECO" modes
[options="header"]
|========================================================================
| Name | Driver/Devices | Description
| experimental.ecomode.enable | usbhid-ups => mge-hid (Eaton/MGE)
| Put UPS in High Efficiency (aka ECO) mode
| experimental.ecomode.disable | usbhid-ups => mge-hid (Eaton/MGE)
| Take the UPS out of High Efficiency (aka ECO) mode
| experimental.ecomode.start.auto | usbhid-ups => mge-hid (Eaton/MGE)
| Put UPS in Bypass mode then High Efficiency (aka ECO) mode
| experimental.ecomode.stop.auto | usbhid-ups => mge-hid (Eaton/MGE)
| Take the UPS out of High Efficiency (aka ECO) mode after exiting Bypass mode
| experimental.essmode.enable | usbhid-ups => mge-hid (Eaton/MGE)
| Put UPS in Energy Saver System (aka ESS) mode
| experimental.essmode.disable | usbhid-ups => mge-hid (Eaton/MGE)
| Take the UPS out of Energy Saver System (aka ESS) mode
|========================================================================

Currently the commands above are present in one subdriver and are specific
to the vendor's proposed power state machine. The plan is to generalize the
concept with vendor specifics, similarly to `experimental.ups.mode.buzzwords`.
6 changes: 5 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 3349 utf-8
personal_ws-1.1 en 3353 utf-8
AAC
AAS
ABI
Expand Down Expand Up @@ -673,6 +673,7 @@ MKDIRPROG
MLH
MMM
MNU
MODENAME
MONITORed
MOXA
MPSU
Expand Down Expand Up @@ -1356,6 +1357,7 @@ V'ger
VALIGN
VARDESC
VARTYPE
VENDORNAME
VER
VERFW
VFI
Expand Down Expand Up @@ -1653,6 +1655,7 @@ buildtype
bullseye
busport
busybox
buzzmode
bv
bypassOff
bypassOn
Expand Down Expand Up @@ -2177,6 +2180,7 @@ interruptonly
interruptsize
intltool
inverter
invmode
io
ioLogik
ioLogikE
Expand Down
Loading