diff --git a/NEWS.adoc b/NEWS.adoc index 110acbb3ce..d7ed5784a6 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -205,6 +205,19 @@ https://github.com/networkupstools/nut/milestone/11 approach seems to have good potential to expand into covering more devices and perhaps platforms. [#2430] + - Introduced `ECO` status concept for "ECO mode" (or "High Efficiency" mode, + or "Energy Saver System"...) as named and defined by hardware vendors. + One common aspect is that this is a balance of electrical efficiency vs. + robust outage protection (which may be overkill for IT equipment whose + PSU can survive several milliseconds on capacitors alone) which can be + selected at run-time. Previously such choice was made at the time of + purchase, with the UPSes only supporting some one protection strategy. + [issue #2495, PR #2637] + * Updated documentation, end-user clients (CGI, NUT-Monitor UI); + * Updated `upsmon` client with ability to report entering and exiting + the ECO mode if reported by the driver; + * Initial implementation for Eaton devices with `usbhid-ups` driver. + - Introduced handling for the `ALARM` status, which already existed as a common denominator for devices seen with active `ups.alarm` variables. UPS devices in an `ALARM` status are generally considered volatile and @@ -218,7 +231,7 @@ https://github.com/networkupstools/nut/milestone/11 * Updated `upsmon` client with ability to report entering and exiting the ALARM status if reported by the driver; * Updated `upsmon` client with setting to toggle whether an `ALARM` - status can prompt the UPS to become critical in certain situations; + status can prompt the UPS to become critical in certain situations. - upsmon: * it was realized that the `POWERDOWNFLAG` must be explicitly set in the diff --git a/clients/status.h b/clients/status.h index 1fb1285b07..19ce2874e4 100644 --- a/clients/status.h +++ b/clients/status.h @@ -45,6 +45,7 @@ static struct { { "BOOST", "VOLTAGE BOOST", 1 }, { "CAL", "CALIBRATION", 1 }, { "BYPASS", "BYPASS", 2 }, + { "ECO", "ECO", 2 }, { "ALARM", "ALARM", 2 }, { NULL, NULL, 0 } }; diff --git a/clients/upsmon.c b/clients/upsmon.c index b177527dc5..647418c4ba 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -192,7 +192,9 @@ static int flag_isset(int num, int flag) static int try_restore_pollfreq(utype_t *ups) { /* Use relaxed pollfreq if we are not in a hardware - * power state that is prone to UPS disappearance */ + * power state that is prone to UPS disappearance. + * Note: ECO mode not considered easily fatal here! + */ if (!flag_isset(ups->status, ST_ONBATT | ST_OFF | ST_BYPASS | ST_ALARM | ST_CAL)) { sleepval = pollfreq; return 1; @@ -716,6 +718,42 @@ static void ups_is_notbypass(utype_t *ups) } } +static void ups_is_eco(utype_t *ups) +{ + if (flag_isset(ups->status, ST_ECO)) { /* no change */ + upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); + return; + } + + /* For example, Eaton defines ECO (High Efficiency) mode + * as a sort of hardware-monitored bypass with switch-over + * under 10ms into Online mode in case of troubles, instead + * of always running in dual-conversion mode (wasteful, safe). + * No reason to monitor it closely on NUT side. + */ + /*sleepval = pollfreqalert;*/ /* bump up polling frequency */ + + ups->ecostate = 1; /* if we lose comms, consider it AWOL */ + + upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); + + /* must have changed from !ECO to ECO, so notify */ + + do_notify(ups, NOTIFY_ECO); + setflag(&ups->status, ST_ECO); +} + +static void ups_is_noteco(utype_t *ups) +{ + /* Called when ECO is NOT among known states */ + ups->ecostate = 0; + if (flag_isset(ups->status, ST_ECO)) { /* actual change */ + do_notify(ups, NOTIFY_NOTECO); + clearflag(&ups->status, ST_ECO); + try_restore_pollfreq(ups); + } +} + static void ups_is_alarm(utype_t *ups) { if (flag_isset(ups->status, ST_ALARM)) { /* no change */ @@ -1125,6 +1163,7 @@ static int is_ups_critical(utype_t *ups) time(&now); if (ups->commstate == 0) { + /* Note: ECO mode not considered easily fatal here */ if (flag_isset(ups->status, ST_CAL)) { upslogx(LOG_WARNING, "UPS [%s] was last known to be calibrating " @@ -1350,6 +1389,7 @@ static void recalc(void) * whether this is really the best thing to do is undecided */ /* crit = (FSD) || (OB & LB) > HOSTSYNC seconds || (CAL || BYPASS || ALARM || OFF) && nocomms */ + /* Note: ECO mode not considered easily fatal here */ if (is_ups_critical(ups)) upsdebugx(1, "Critical UPS: %s", ups->sys); else @@ -1456,6 +1496,10 @@ static void drop_connection(utype_t *ups) if(flag_isset(ups->status, ST_CAL)) upsdebugx(2, "Disconnected UPS [%s] was last seen in status CAL, this UPS might be considered critical later.", ups->sys); + /* Note: ECO mode not considered easily fatal here */ + if(ups->ecostate == 1 || flag_isset(ups->status, ST_ECO)) + upsdebugx(2, "Disconnected UPS [%s] was last seen in status ECO.", ups->sys); + ups->commstate = 0; /* forget poll-failure logging throttling */ @@ -2412,6 +2456,8 @@ 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); @@ -2439,6 +2485,8 @@ static void parse_status(utype_t *ups, char *status) ups_is_off(ups); if (!strcasecmp(statword, "BYPASS")) ups_is_bypass(ups); + if (!strcasecmp(statword, "ECO")) + ups_is_eco(ups); if (!strcasecmp(statword, "ALARM")) ups_is_alarm(ups); /* do it last to override any possible OL */ diff --git a/clients/upsmon.h b/clients/upsmon.h index 32d018f8ca..ec68bb1a07 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -36,7 +36,8 @@ #define ST_CAL (1 << 7) /* UPS calibration in progress (CAL) */ #define ST_OFF (1 << 8) /* UPS is administratively off or asleep (OFF) */ #define ST_BYPASS (1 << 9) /* UPS is on bypass so not protecting */ -#define ST_ALARM (1 << 10) /* UPS has at least one active alarm */ +#define ST_ECO (1 << 10) /* UPS is in ECO (High Efficiency) mode or similar tweak, e.g. Energy Saver System mode */ +#define ST_ALARM (1 << 11) /* UPS has at least one active alarm */ /* required contents of flag file */ #define SDMAGIC "upsmon-shutdown-file" @@ -70,6 +71,8 @@ typedef struct { /* be delayed vs. seeing OFF state */ int bypassstate; /* fire on a 0->1 transition; */ /* delays not implemented now */ + int ecostate; /* fire on a 0->1 transition; */ + /* delays not implemented now */ int alarmstate; /* fire on a 0->1 transition; */ /* delays not implemented now */ @@ -96,7 +99,7 @@ typedef struct { #define NOTIFY_ONBATT 1 /* UPS went on battery */ #define NOTIFY_LOWBATT 2 /* UPS went to low battery */ #define NOTIFY_FSD 3 /* Primary upsmon set FSD flag */ -#define NOTIFY_COMMOK 4 /* Communication established */ +#define NOTIFY_COMMOK 4 /* Communication established */ #define NOTIFY_COMMBAD 5 /* Communication lost */ #define NOTIFY_SHUTDOWN 6 /* System shutdown in progress */ #define NOTIFY_REPLBATT 7 /* UPS battery needs to be replaced */ @@ -108,8 +111,10 @@ typedef struct { #define NOTIFY_NOTOFF 13 /* UPS is not anymore administratively OFF or asleep*/ #define NOTIFY_BYPASS 14 /* UPS is administratively on bypass */ #define NOTIFY_NOTBYPASS 15 /* UPS is not anymore administratively on bypass */ -#define NOTIFY_ALARM 16 /* UPS has at least one active alarm */ -#define NOTIFY_NOTALARM 17 /* UPS has no active alarms */ +#define NOTIFY_ECO 16 /* UPS is in ECO mode or similar */ +#define NOTIFY_NOTECO 17 /* UPS is not anymore in ECO mode or similar */ +#define NOTIFY_ALARM 18 /* UPS has at least one active alarm */ +#define NOTIFY_NOTALARM 19 /* UPS has no active alarms */ #define NOTIFY_SUSPEND_STARTING 30 /* OS is entering sleep/suspend/hibernate slumber mode, and we know it */ #define NOTIFY_SUSPEND_FINISHED 31 /* OS just finished sleep/suspend/hibernate slumber mode, and we know it */ @@ -158,6 +163,8 @@ static struct { { NOTIFY_NOTOFF, "NOTOFF", NULL, "UPS %s: no longer administratively OFF or asleep", NOTIFY_DEFAULT }, { NOTIFY_BYPASS, "BYPASS", NULL, "UPS %s: on bypass (powered, not protecting)", NOTIFY_DEFAULT }, { NOTIFY_NOTBYPASS,"NOTBYPASS",NULL, "UPS %s: no longer on bypass", NOTIFY_DEFAULT }, + { NOTIFY_ECO, "ECO", NULL, "UPS %s: in ECO mode (as defined by vendor)", NOTIFY_DEFAULT }, + { NOTIFY_NOTECO, "NOTECO", NULL, "UPS %s: no longer in ECO mode", NOTIFY_DEFAULT }, { NOTIFY_ALARM, "ALARM", NULL, "UPS %s is in an alarm state (has active alarms)", NOTIFY_DEFAULT }, { NOTIFY_NOTALARM, "NOTALARM", NULL, "UPS %s is no longer in an alarm state (no active alarms)", NOTIFY_DEFAULT }, diff --git a/common/nutconf.cpp b/common/nutconf.cpp index a03797286d..cd8a76d5e9 100644 --- a/common/nutconf.cpp +++ b/common/nutconf.cpp @@ -1316,6 +1316,10 @@ UpsmonConfiguration::NotifyType UpsmonConfiguration::NotifyTypeFromString(const return NOTIFY_BYPASS; else if(str=="NOTBYPASS") return NOTIFY_NOTBYPASS; + else if(str=="ECO") + return NOTIFY_ECO; + else if(str=="NOTECO") + return NOTIFY_NOTECO; else if(str=="ALARM") return NOTIFY_ALARM; else if(str=="NOTALARM") diff --git a/common/nutwriter.cpp b/common/nutwriter.cpp index ae86819c26..cafac79e48 100644 --- a/common/nutwriter.cpp +++ b/common/nutwriter.cpp @@ -404,6 +404,8 @@ const NotifyFlagsStrings::TypeStrings NotifyFlagsStrings::type_str = { "NOTOFF", // NOTIFY_NOTOFF "BYPASS", // NOTIFY_BYPASS "NOTBYPASS", // NOTIFY_NOTBYPASS + "ECO\t", // NOTIFY_ECO (including padding) + "NOTECO", // NOTIFY_NOTECO "ALARM", // NOTIFY_ALARM "NOTALARM", // NOTIFY_NOTALARM "SUSPEND_STARTING", // NOTIFY_SUSPEND_STARTING diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index 7b38403b34..e5d7235859 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -332,6 +332,8 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # NOTIFYMSG NOTOFF "UPS %s: no longer administratively OFF or asleep" # NOTIFYMSG BYPASS "UPS %s: on bypass (powered, not protecting)" # NOTIFYMSG NOTBYPASS "UPS %s: no longer on bypass" +# NOTIFYMSG ECO "UPS %s: in ECO mode (as defined by vendor)" +# NOTIFYMSG NOTECO "UPS %s: no longer in ECO mode (as defined by vendor)" # NOTIFYMSG ALARM "UPS %s is in an alarm state (has active alarms)" # NOTIFYMSG NOTALARM "UPS %s is no longer in an alarm state (no active alarms)" # @@ -381,6 +383,8 @@ POWERDOWNFLAG "@POWERDOWNFLAG@" # NOTIFYFLAG NOTOFF SYSLOG+WALL # NOTIFYFLAG BYPASS SYSLOG+WALL # NOTIFYFLAG NOTBYPASS SYSLOG+WALL +# NOTIFYFLAG ECO SYSLOG+WALL +# NOTIFYFLAG NOTECO SYSLOG+WALL # NOTIFYFLAG ALARM SYSLOG+WALL # NOTIFYFLAG NOTALARM SYSLOG+WALL # diff --git a/data/cmdvartab b/data/cmdvartab index 78fdef4712..ba915cd114 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -67,6 +67,10 @@ VARDESC input.transfer.low.min "smallest settable low voltage transfer point (V) VARDESC input.transfer.low.max "greatest settable low voltage transfer point (V)" VARDESC input.transfer.high.min "smallest settable high voltage transfer point (V)" VARDESC input.transfer.high.max "greatest settable high voltage transfer point (V)" +VARDESC input.eco.switchable "Input High Efficiency (aka ECO) mode switch" +VARDESC input.transfer.forced "Input forced transfer modes enable/disable" +VARDESC input.bypass.switch.on "Put the UPS in bypass mode" +VARDESC input.bypass.switch.off "Take the UPS out of bypass mode" VARDESC input.sensitivity "Input power sensitivity" VARDESC input.quality "Input power quality" VARDESC input.current "Input current (A)" @@ -161,6 +165,7 @@ VARDESC outlet.id "Outlet system identifier" VARDESC outlet.desc "Outlet description" VARDESC outlet.switch "Outlet switch control" VARDESC outlet.status "Outlet switch status" +VARDESC outlet.protect.status "Outlet protection status" VARDESC outlet.switchable "Outlet switch ability" VARDESC outlet.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" @@ -169,7 +174,9 @@ VARDESC outlet.1.id "Outlet system identifier" VARDESC outlet.1.desc "Outlet description" VARDESC outlet.1.switch "Outlet switch control" VARDESC outlet.1.status "Outlet switch status" +VARDESC outlet.1.protect.status "Outlet protection status" VARDESC outlet.1.switchable "Outlet switch ability" +VARDESC outlet.1.ecocontrol "Master Outlet used to automatically power off the slave outlets" VARDESC outlet.1.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.1.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.1.delay.start "Interval to wait before restarting this outlet (seconds)" @@ -177,7 +184,9 @@ VARDESC outlet.2.id "Outlet system identifier" VARDESC outlet.2.desc "Outlet description" VARDESC outlet.2.switch "Outlet switch control" VARDESC outlet.2.status "Outlet switch status" +VARDESC outlet.2.protect.status "Outlet protection status" VARDESC outlet.2.switchable "Outlet switch ability" +VARDESC outlet.2.ecocontrol "Master Outlet used to automatically power off the slave outlets" VARDESC outlet.2.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.2.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.2.delay.start "Interval to wait before restarting this outlet (seconds)" @@ -227,6 +236,10 @@ 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 reset.input.minmax "Reset minimum and maximum input voltage status" CMDDESC reset.watchdog "Reset watchdog timer" CMDDESC beeper.on "Obsolete (use beeper.enable)" diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 3f606450da..144c115ee9 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -275,6 +275,11 @@ BYPASS;; UPS on bypass (powered, not protecting) NOTBYPASS;; UPS no longer on bypass +ECO;; UPS in ECO or similar mode (as defined and named by vendor); +for more details see linkman:upsmon[8]. + +NOTECO;; UPS no longer in ECO mode (see above) + ALARM;; UPS is in an alarm state (has active alarms) NOTALARM;; UPS is no longer in an alarm state (no active alarms) diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 47000c8f57..7529bf2e72 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -178,6 +178,20 @@ UPS on bypass (powered, not protecting). *NOTBYPASS*:: UPS no longer on bypass. +*ECO*:: +UPS in ECO or similar mode (as defined and named by vendor); other +names include High Efficiency mode and Energy Saver System. ++ +For example, Eaton docs define High Efficiency mode as a sort of +hardware-monitored bypass with switch-over under 10ms into Online +mode in case of troubles (so what was known as Line-Interactive +in the older days), which can be *chosen* instead of always running +in a dual-conversion mode (safer, but more wasteful and with a toll +on battery life). Older devices only implemented one or the other. + +*NOTECO*:: +UPS no longer in ECO mode (see above). + *ALARM*:: UPS is in an alarm state (has active alarms). @@ -508,15 +522,15 @@ implementations throughout the various UPS drivers and generally anytime that a "ups.alarm" variable is seen reported by the specific UPS driver. Alarms can be acted upon by the user using the "ALARM" and "NOTALARM" notifiers, -which are reported by the "upsmon" when the "ALARM" status occurs or disappears. +which are reported by the `upsmon` when the "ALARM" status occurs or disappears. -As alarms raised by the UPS are usually severe in nature, the "upsmon" watches +As alarms raised by the UPS are usually severe in nature, the `upsmon` watches a UPS in such a state more closely, bumping up the polling frequency as needed. -When connection loss occurs in such an alarm state, the "upsmon" will by default +When connection loss occurs in such an alarm state, the `upsmon` will by default consider the volatile UPS as critical/dead and this may lead to false shutdowns if the actual alarm was in fact mundane in nature (e.g. caused by a HE/ECO mode). -This can be changed by utilizing the "ALARMCRITICAL" setting within "upsmon.conf". +This can be changed by utilizing the `ALARMCRITICAL` setting within `upsmon.conf`. RELOADING NUANCES ----------------- diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 6c7dbd5b2c..07a62bbcc0 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -262,6 +262,8 @@ input: Incoming line/power information voltage transfer point (V) | 131 | 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 | input.sensitivity | Input power sensitivity | H (high) | input.quality | Input power quality (*** opaque) | FF @@ -305,6 +307,10 @@ input: Incoming line/power information point (percent of nominal Hz) | 5 | input.transfer.hysteresis | Threshold of switching protection modes, voltage transfer point (V) | 10 +| input.transfer.forced | Input forced transfer modes + (enabled or disabled) | enabled +| input.bypass.switch.on | Put the UPS in bypass mode | on +| input.bypass.switch.off | Take the UPS out of bypass mode | disabled | input.load | Load on (ePDU) input (percent of full) | 25 | input.realpower | Current sum value of all (ePDU) @@ -693,11 +699,16 @@ of the user manual. (on/off) | on | outlet.n.status | Outlet switch status (on/off) | on +| outlet.n.protect.status | Outlet protection status + (0-2) | protected | outlet.n.alarm | Alarms for outlets and PDU, published in ups.alarm | outlet 1 low voltage warning | outlet.n.switchable | Outlet switch ability (yes/no) | yes +| outlet.n.ecocontrol | Master Outlet used to + automatically power off the + slave outlets | The outlet is not ECO controlled | outlet.n.autoswitch.charge.low | Remaining battery level to power off this outlet (percent) | 80 @@ -862,6 +873,10 @@ 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 diff --git a/docs/nut.dict b/docs/nut.dict index 210eb1a796..645ff5e71c 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -321,6 +321,7 @@ EPFCLCD EPO EPS ESC +ESS ESV ESXi ETIME @@ -743,6 +744,7 @@ NOPARENT NOTALARM NOTBYPASS NOTCAL +NOTECO NOTIFYCMD NOTIFYFLAG NOTIFYFLAGS @@ -1604,6 +1606,8 @@ bullseye busport busybox bv +bypassOff +bypassOn cStandard cablepower calloc @@ -1840,6 +1844,7 @@ ePDUs eaton ec eco +ecomode edb editorconfig edl @@ -1868,6 +1873,7 @@ epdu eq errno esac +essmode esupssmart et etapro diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index a869cc594d..f27afe923d 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -689,6 +689,174 @@ static info_lkp_t pegasus_yes_no_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t outlet_eco_yes_no_info[] = { + { 0, "The outlet is not ECO controlled", NULL, NULL }, + { 1, "The outlet is ECO controlled", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Function to check if the current High Efficiency (aka ECO) mode voltage/frequency is within the configured limits */ +static const char *eaton_input_eco_mode_check_range(double value) +{ + double bypass_voltage; + double eco_low; + double eco_high; + double out_nominal; + double out_frequency_nominal; + double bypass_frequency; + double frequency_range; + double lower_frequency_limit; + double upper_frequency_limit; + + /* Get the Eco mode voltage/frequency and transfer points */ + const char* bypass_voltage_str = dstate_getinfo("input.bypass.voltage"); + const char* eco_low_str = dstate_getinfo("input.transfer.eco.low"); + const char* eco_high_str = dstate_getinfo("input.transfer.eco.high"); + const char* out_nominal_str = dstate_getinfo("output.voltage.nominal"); + const char* out_nominal_frequency_str = dstate_getinfo("output.frequency.nominal"); + const char* frequency_range_str = dstate_getinfo("input.transfer.frequency.eco.range"); + const char* bypass_frequency_str = dstate_getinfo("input.bypass.frequency"); + + NUT_UNUSED_VARIABLE(value); + + if (eco_low_str == NULL || eco_high_str == NULL || bypass_voltage_str == NULL || bypass_frequency_str == NULL || + out_nominal_str == NULL || out_nominal_frequency_str == NULL || frequency_range_str == NULL) { + upsdebugx(1, "Failed to get values: %s, %s, %s, %s, %s, %s, %s", + eco_low_str, eco_high_str, bypass_voltage_str, bypass_frequency_str, out_nominal_str, + out_nominal_frequency_str, frequency_range_str); + return NULL; /* Handle the error appropriately */ + } + + str_to_double(bypass_voltage_str, &bypass_voltage, 10); + str_to_double(eco_low_str, &eco_low, 10); + str_to_double(eco_high_str, &eco_high, 10); + str_to_double(out_nominal_str, &out_nominal, 10); + str_to_double(out_nominal_frequency_str, &out_frequency_nominal, 10); + str_to_double(frequency_range_str, &frequency_range, 10); + str_to_double(bypass_frequency_str, &bypass_frequency, 10); + + /* Default values if user-defined limits are not available or out of range + 5% below nominal output voltage + 5% above nominal output voltage + 5% below/above output frequency nominal */ + + /* Set the frequency limit */ + if (frequency_range > 0) { + lower_frequency_limit = out_frequency_nominal - (out_frequency_nominal / 100 * frequency_range); + upper_frequency_limit = out_frequency_nominal + (out_frequency_nominal / 100 * frequency_range); + } else { + lower_frequency_limit = out_frequency_nominal - (out_frequency_nominal / 100 * 5); + upper_frequency_limit = out_frequency_nominal + (out_frequency_nominal / 100 * 5); + } + + /* Check if user-defined limits are available and within valid range */ + if ((eco_low > 0 && eco_high > 0) && + (bypass_voltage >= eco_low && bypass_voltage <= eco_high) && + (bypass_frequency >= lower_frequency_limit && bypass_frequency <= upper_frequency_limit)) { + return "ECO"; /* Enter Eco mode */ + } + + /* Default values if user-defined limits are not available or out of range */ + if ((bypass_voltage >= out_nominal * 0.95 && bypass_voltage <= out_nominal * 1.05) && + (bypass_frequency >= lower_frequency_limit && bypass_frequency <= upper_frequency_limit)) { + return "ECO"; /* Enter Eco mode */ + } else { + return NULL; /* Do not enter Eco mode */ + } +} + +/* High Efficiency (aka ECO) mode */ +static info_lkp_t eaton_input_mode_info[] = { + { 0, "normal", NULL, NULL }, + { 1, "ECO", eaton_input_eco_mode_check_range, NULL }, /* NOTE: "ecomode" = checked and working fine */ + { 2, "ESS", NULL, NULL }, /* Energy Saver System, makes sense for UPS that implements this mode */ + { 0, NULL, NULL, NULL } +}; + +/* Function to check if the current bypass voltage/frequency is within the configured limits */ +static const char *eaton_input_bypass_check_range(double value) +{ + double bypass_voltage; + double bypass_low; + double bypass_high; + double out_nominal; + double bypass_frequency; + double frequency_range; + double lower_frequency_limit; + double upper_frequency_limit; + double out_frequency_nominal; + + + /* Get the bypass voltage/frequency and transfer points */ + const char* bypass_voltage_str = dstate_getinfo("input.bypass.voltage"); + const char* bypass_low_str = dstate_getinfo("input.transfer.bypass.low"); + const char* bypass_high_str = dstate_getinfo("input.transfer.bypass.high"); + const char* out_nominal_str = dstate_getinfo("output.voltage.nominal"); + const char* bypass_frequency_str = dstate_getinfo("input.bypass.frequency"); + const char* frequency_range_str = dstate_getinfo("input.transfer.frequency.bypass.range"); + const char* out_nominal_frequency_str = dstate_getinfo("output.frequency.nominal"); + + NUT_UNUSED_VARIABLE(value); + + if (bypass_voltage_str == NULL || bypass_low_str == NULL || bypass_high_str == NULL || out_nominal_str == NULL || + bypass_frequency_str == NULL || frequency_range_str == NULL || out_nominal_frequency_str == NULL) { + upsdebugx(1, "Failed to get values: %s, %s, %s, %s, %s, %s, %s", + bypass_voltage_str, bypass_low_str, bypass_high_str, out_nominal_str, + bypass_frequency_str, frequency_range_str, out_nominal_frequency_str); + return NULL; /* Handle the error appropriately */ + } + + str_to_double(bypass_voltage_str, &bypass_voltage, 10); + str_to_double(bypass_low_str, &bypass_low, 10); + str_to_double(bypass_high_str, &bypass_high, 10); + str_to_double(out_nominal_str, &out_nominal, 10); + str_to_double(bypass_frequency_str, &bypass_frequency, 10); + str_to_double(frequency_range_str, &frequency_range, 10); + str_to_double(out_nominal_frequency_str, &out_frequency_nominal, 10); + + /* Default values if user-defined limits are not available or out of range + 20% below nominal output voltage + 15% above nominal output voltage + 10% below/above output frequency nominal */ + + /* Set the frequency limit */ + if (frequency_range > 0) { + lower_frequency_limit = out_frequency_nominal - (out_frequency_nominal / 100 * frequency_range); + upper_frequency_limit = out_frequency_nominal + (out_frequency_nominal / 100 * frequency_range); + } else { + lower_frequency_limit = out_frequency_nominal - (out_frequency_nominal / 100 * 10); + upper_frequency_limit = out_frequency_nominal + (out_frequency_nominal / 100 * 10); + } + + /* Check if user-defined limits are available and within valid range */ + if ((bypass_low > 0 && bypass_high > 0) && + (bypass_voltage >= bypass_low && bypass_voltage <= bypass_high) && + (bypass_frequency >= lower_frequency_limit && bypass_frequency <= upper_frequency_limit)) { + return "on"; /* Enter bypass mode */ + } + + if ((bypass_voltage >= out_nominal * 0.8 && bypass_voltage <= out_nominal * 1.15) && + (bypass_frequency >= lower_frequency_limit && bypass_frequency <= upper_frequency_limit)) { + return "on"; /* Enter bypass mode */ + } else { + return NULL; /* Do not enter bypass mode */ + } +} + +/* Automatic Bypass mode on */ +static info_lkp_t eaton_input_bypass_mode_on_info[] = { + { 0, "disabled", NULL, NULL }, + { 1, "on", eaton_input_bypass_check_range, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Automatic Bypass mode Off */ +static info_lkp_t eaton_input_bypass_mode_off_info[] = { + { 0, "disabled", NULL, NULL }, + { 1, "off", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + /* Determine country using UPS.PowerSummary.Country. * If not present: * if PowerConverter.Output.Voltage >= 200 => "Europe" @@ -864,6 +1032,13 @@ static info_lkp_t eaton_converter_online_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t eaton_outlet_protection_status_info[] = { + { 0, "not powered", NULL, NULL }, + { 1, "not protected", NULL, NULL }, + { 2, "protected", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ @@ -871,10 +1046,11 @@ static info_lkp_t eaton_converter_online_info[] = { /* Eaton / MGE HID usage table */ static usage_lkp_t mge_usage_lkp[] = { { "Undefined", 0xffff0000 }, - { "STS", 0xffff0001 }, + { "STS", 0xffff0001 }, { "Environment", 0xffff0002 }, - { "Statistic", 0xffff0003 }, + { "Statistic", 0xffff0003 }, { "StatisticSystem", 0xffff0004 }, + { "USB", 0xffff0005 }, /* 0xffff0005-0xffff000f => Reserved */ { "Phase", 0xffff0010 }, { "PhaseID", 0xffff0011 }, @@ -1014,8 +1190,12 @@ static usage_lkp_t mge_usage_lkp[] = { { "Reset", 0xffff00ad }, { "WatchdogReset", 0xffff00ae }, /* 0xffff00af-0xffff00df => Reserved */ + { "iDesignator", 0xffff00ba }, { "COPIBridge", 0xffff00e0 }, - /* 0xffff00e1-0xffff00ef => Reserved */ + { "Gateway", 0xffff00e1 }, + { "System", 0xffff00e5 }, + { "Status", 0xffff00e9 }, + /* 0xffff00ee-0xffff00ef => Reserved */ { "iModel", 0xffff00f0 }, { "iVersion", 0xffff00f1 }, { "iTechnicalLevel", 0xffff00f2 }, @@ -1253,7 +1433,10 @@ static hid_info_t mge_hid2nut[] = /* Device collection */ /* Just declared to call *hid2info */ { "device.country", ST_FLAG_STRING, 20, "UPS.PowerSummary.Country", NULL, "Europe", HU_FLAG_STATIC, eaton_check_country_info }, - + { "device.usb.version", ST_FLAG_STRING, 20, "UPS.System.USB.iVersion", NULL, NULL, HU_FLAG_STATIC, stringid_conversion }, /* FIXME */ + /* { "device.usb.mode", ST_FLAG_STRING, 20, "UPS.System.USB.Mode", NULL, NULL, HU_FLAG_STATIC, stringid_conversion }, */ /* not useful ,not a string (1 to set in bootloader ) */ + /*{ "device.gateway.power.rate", ST_FLAG_STRING, 20, "UPS.System.Gateway.PowerRate", NULL, NULL, HU_FLAG_STATIC, stringid_conversion }, */ /* not useful , not a string (level of power provided by the UPS to the network card */ + /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimitSetting", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -1286,7 +1469,10 @@ static hid_info_t mge_hid2nut[] = /* Refer to Note 1 (This point will need more clarification!) { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.Used", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_legacy_info }, */ /* This data is the actual ABM status information */ - { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_status_info }, + { "battery.charger.mode", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_status_info }, /* needs both ? from https://github.com/networkupstools/nut/pull/2637#discussion_r1772730590 */ + { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.Status", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_status_info }, + /* FIXME: should better use UPS.BatterySystem.Charger.Status should work on 9E Models */ + /* UPS page */ { "ups.efficiency", 0, 0, "UPS.PowerConverter.Output.Efficiency", NULL, "%.0f", 0, NULL }, @@ -1366,6 +1552,8 @@ static hid_info_t mge_hid2nut[] = { "BOOL", 0, 0, "UPS.PowerConverter.Input.[2].PresentStatus.Used", NULL, NULL, 0, bypass_auto_info }, /* Automatic bypass */ /* NOTE: entry [3] is above as mge_onbatt_info */ { "BOOL", 0, 0, "UPS.PowerConverter.Input.[4].PresentStatus.Used", NULL, NULL, 0, bypass_manual_info }, /* Manual bypass */ + /* NOTE: needs to be tested */ + { "BOOL", 0, 0, "UPS.PowerConverter.Input.[5].PresentStatus.Used", NULL, NULL, 0, eco_mode_info }, /* ECO/HE Mode */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FanFailure", NULL, NULL, 0, fanfail_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.Present", NULL, NULL, 0, nobattery_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.InternalFailure", NULL, NULL, 0, chargerfail_info }, @@ -1406,7 +1594,7 @@ static hid_info_t mge_hid2nut[] = /* same as "input.transfer.boost.low" */ { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.eco.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageEcoTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, - { "input.transfer.bypass.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBypassTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.transfer.bypass.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBypassTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.boost.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBoostTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HighVoltageBoostTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBuckTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -1417,6 +1605,8 @@ static hid_info_t mge_hid2nut[] = { "input.transfer.frequency.bypass.range", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.FrequencyRangeBypassTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.frequency.eco.range", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.FrequencyRangeEcoTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.hysteresis", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HysteresisVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + /* input.transfer.forced = 1 needs for Bypass Switch On/Off */ + { "input.transfer.forced", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerConverter.Input.[2].ForcedTransferEnable", NULL, "%.0f", HU_FLAG_SEMI_STATIC, eaton_enable_disable_info }, { "input.transfer.trim.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HighVoltageBuckTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.sensitivity", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerConverter.Output.SensitivityMode", NULL, "%s", HU_FLAG_SEMI_STATIC, mge_sensitivity_info }, { "input.voltage.extended", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.ExtendedVoltageMode", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, @@ -1439,6 +1629,15 @@ static hid_info_t mge_hid2nut[] = { "input.bypass.frequency", 0, 0, "UPS.PowerConverter.Input.[2].Frequency", NULL, "%.1f", 0, NULL }, { "input.bypass.frequency.nominal", 0, 0, "UPS.Flow.[2].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + /* ECO(HE) Mode switch */ + { "input.eco.switchable", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerConverter.Input.[5].Switchable", NULL, "%.0f", HU_FLAG_SEMI_STATIC, eaton_input_mode_info }, + + /* Auto Bypass Mode on/off */ + /* needs check this variable, maybe "Bypass switch ability" like Qualify bypass */ + /* { "input.bypass.switchable", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerConverter.Input.[2].Switchable", NULL, "%.0f", HU_FLAG_SEMI_STATIC, eaton_input_bypass_mode_info }, */ + { "input.bypass.switch.on", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerConverter.Input.[2].SwitchOnControl", NULL, "%.0f", HU_FLAG_SEMI_STATIC, eaton_input_bypass_mode_on_info }, + { "input.bypass.switch.off", ST_FLAG_RW | ST_FLAG_STRING, 12, "UPS.PowerConverter.Input.[2].SwitchOffControl", NULL, "%.0f", HU_FLAG_SEMI_STATIC, eaton_input_bypass_mode_off_info }, + /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.L1-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[1].Voltage", NULL, "%.1f", 0, NULL }, @@ -1476,7 +1675,10 @@ static hid_info_t mge_hid2nut[] = { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.[2].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "outlet.1.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[2].OutletID", NULL, "PowerShare Outlet 1", HU_FLAG_ABSENT, NULL }, { "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.Switchable", NULL, "%s", HU_FLAG_STATIC, yes_no_info }, + /* FIXME: should better use UPS.OutletSystem.Outlet.[1].Status? */ { "outlet.1.status", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.SwitchOn/Off", NULL, "%s", 0, on_off_info }, + { "outlet.1.protect.status", 0, 0, "UPS.OutletSystem.Outlet.[1].Status", NULL, "%s", 0, eaton_outlet_protection_status_info }, + { "outlet.1.designator", 0, 0, "UPS.OutletSystem.Outlet.[1].iDesignator", NULL, NULL, HU_FLAG_STATIC, stringid_conversion }, /* FIXME */ /* For low end models, with 1 non backup'ed outlet */ { "outlet.1.status", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, "%s", 0, on_off_info }, /* FIXME: change to outlet.1.battery.charge.low, as in mge-xml.c?! */ @@ -1487,6 +1689,8 @@ static hid_info_t mge_hid2nut[] = { "outlet.1.realpower", 0, 0, "UPS.OutletSystem.Outlet.[2].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.1.current", 0, 0, "UPS.OutletSystem.Outlet.[2].Current", NULL, "%.2f", 0, NULL }, { "outlet.1.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[2].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ + /* 0: The outlet is not ECO controlled. / 1 : The outlet is ECO controlled. => Readonly! use some yes_no_info */ + { "outlet.1.ecocontrol", 0, 0, "UPS.OutletSystem.Outlet.[2].ECOControl", NULL, "%s", HU_FLAG_SEMI_STATIC, outlet_eco_yes_no_info}, /* Second outlet */ { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet.[3].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "outlet.2.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[3].OutletID", NULL, "PowerShare Outlet 2", HU_FLAG_ABSENT, NULL }, @@ -1496,6 +1700,8 @@ static hid_info_t mge_hid2nut[] = /* Generic version (RO) for other models */ { "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", NULL, "%s", 0, yes_no_info }, { "outlet.2.status", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.SwitchOn/Off", NULL, "%s", 0, on_off_info }, + { "outlet.2.protect.status", 0, 0, "UPS.OutletSystem.Outlet.[3].Status", NULL, "%s", 0, eaton_outlet_protection_status_info }, + /* FIXME: should better use UPS.OutletSystem.Outlet.[1].Status? */ { "outlet.2.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.2.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.2.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].StartupTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -1503,6 +1709,8 @@ static hid_info_t mge_hid2nut[] = { "outlet.2.realpower", 0, 0, "UPS.OutletSystem.Outlet.[3].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.2.current", 0, 0, "UPS.OutletSystem.Outlet.[3].Current", NULL, "%.2f", 0, NULL }, { "outlet.2.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[3].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ + /* 0: The outlet is not ECO controlled. / 1 : The outlet is ECO controlled. => Readonly! use some yes_no_info */ + { "outlet.2.ecocontrol", 0, 0, "UPS.OutletSystem.Outlet.[3].ECOControl", NULL, "%s", HU_FLAG_SEMI_STATIC, outlet_eco_yes_no_info}, /* instant commands. */ /* splited into subset while waiting for extradata support @@ -1534,6 +1742,16 @@ static hid_info_t mge_hid2nut[] = { "outlet.2.load.off", 0, 0, "UPS.OutletSystem.Outlet.[3].DelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL }, { "outlet.2.load.on", 0, 0, "UPS.OutletSystem.Outlet.[3].DelayBeforeStartup", NULL, "0", HU_TYPE_CMD, NULL }, + /* Command to switch ECO Mode */ + { "ecomode.disable", 0, 0, "UPS.PowerConverter.Input.[5].Switchable", NULL, "0", HU_TYPE_CMD, NULL }, + { "ecomode.enable", 0, 0, "UPS.PowerConverter.Input.[5].Switchable", NULL, "1", HU_TYPE_CMD, NULL }, + { "essmode.enable", 0, 0, "UPS.PowerConverter.Input.[5].Switchable", NULL, "2", HU_TYPE_CMD, NULL }, + { "essmode.disable", 0, 0, "UPS.PowerConverter.Input.[5].Switchable", NULL, "0", HU_TYPE_CMD, NULL }, + + /* Command to switch Automatic Bypass Mode On/Off */ + { "bypass.start", 0, 0, "UPS.PowerConverter.Input.[2].SwitchOnControl", NULL, "1", HU_TYPE_CMD, NULL }, + { "bypass.stop", 0, 0, "UPS.PowerConverter.Input.[2].SwitchOffControl", NULL, "1", HU_TYPE_CMD, NULL }, + /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index b5968d0a02..9cd1e83b06 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -280,6 +280,7 @@ static status_lkp_t status_info[] = { { "boost", STATUS(BOOST) }, { "bypassauto", STATUS(BYPASSAUTO) }, { "bypassman", STATUS(BYPASSMAN) }, + { "ecomode", STATUS(ECOMODE) }, { "off", STATUS(OFF) }, { "cal", STATUS(CALIB) }, { "overheat", STATUS(OVERHEAT) }, @@ -371,6 +372,12 @@ info_lkp_t bypass_manual_info[] = { { 0, "!bypassman", NULL, NULL }, { 0, NULL, NULL, NULL } }; +info_lkp_t eco_mode_info[] = { + { 0, "normal", NULL, NULL }, + { 1, "ecomode", NULL, NULL }, + { 2, "ESS", NULL, NULL }, /* makes sense for UPS that implements this mode */ + { 0, NULL, NULL, NULL } +}; /* note: this value is reverted (0=set, 1=not set). We report "being off" rather than "being on", so that devices that don't implement this variable are "on" by default */ @@ -2086,6 +2093,9 @@ static void ups_alarm_set(void) if (ups_status & STATUS(BYPASSMAN)) { alarm_set("Manual bypass mode!"); } + /*if (ups_status & STATUS(ECOMODE)) { + alarm_set("ECO(HE) mode!"); + }*/ /* disable alarm for eco as we dont want raise alarm ? */ } /* Return the current value of ups_status */ @@ -2392,6 +2402,9 @@ static void ups_status_set(void) if (ups_status & (STATUS(BYPASSAUTO) | STATUS(BYPASSMAN))) { status_set("BYPASS"); /* on bypass */ } + if (ups_status & STATUS(ECOMODE)) { + status_set("ECO"); /* on ECO Mode */ + } if (ups_status & STATUS(OFF)) { status_set("OFF"); /* ups is off */ } diff --git a/drivers/usbhid-ups.h b/drivers/usbhid-ups.h index f6f67fea72..e992aea330 100644 --- a/drivers/usbhid-ups.h +++ b/drivers/usbhid-ups.h @@ -92,6 +92,7 @@ extern info_lkp_t trim_info[]; extern info_lkp_t boost_info[]; extern info_lkp_t bypass_auto_info[]; extern info_lkp_t bypass_manual_info[]; +extern info_lkp_t eco_mode_info[]; extern info_lkp_t off_info[]; extern info_lkp_t calibration_info[]; extern info_lkp_t nobattery_info[]; @@ -142,6 +143,7 @@ typedef enum { BOOST, /* SmartBoost */ BYPASSAUTO, /* on automatic bypass */ BYPASSMAN, /* on manual/service bypass */ + ECOMODE, /* High Efficiency (aka ECO Mode) */ OFF, /* ups is off */ CALIB, /* calibration */ OVERHEAT, /* overheat; Belkin, TrippLite */ diff --git a/include/nutconf.hpp b/include/nutconf.hpp index d3407a714c..5d555dbc78 100644 --- a/include/nutconf.hpp +++ b/include/nutconf.hpp @@ -1470,6 +1470,8 @@ class UpsmonConfiguration : public Serialisable NOTIFY_NOTOFF, NOTIFY_BYPASS, NOTIFY_NOTBYPASS, + NOTIFY_ECO, + NOTIFY_NOTECO, NOTIFY_ALARM, NOTIFY_NOTALARM, diff --git a/scripts/augeas/nutupsmonconf.aug.in b/scripts/augeas/nutupsmonconf.aug.in index a98c5429c9..e972a406d7 100644 --- a/scripts/augeas/nutupsmonconf.aug.in +++ b/scripts/augeas/nutupsmonconf.aug.in @@ -123,6 +123,8 @@ let upsmon_notify_type = "ONLINE" | "NOTOFF" | "BYPASS" | "NOTBYPASS" + | "ECO" + | "NOTECO" | "ALARM" | "NOTALARM" | "SUSPEND_STARTING" diff --git a/scripts/python/app/NUT-Monitor-py2gtk2.in b/scripts/python/app/NUT-Monitor-py2gtk2.in index 6bdea29171..a242ade5d2 100755 --- a/scripts/python/app/NUT-Monitor-py2gtk2.in +++ b/scripts/python/app/NUT-Monitor-py2gtk2.in @@ -790,6 +790,7 @@ class gui_updater( threading.Thread ) : "RB" : "%s" % _("Replace batteries !"), "ALARM" : "%s" % _("Active alarms !"), "BYPASS" : "Bypass %s" % _("(no battery protection)"), + "ECO" : _("In ECO mode (as defined by vendor)"), "CAL" : _("Performing runtime calibration"), "OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), "OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), diff --git a/scripts/python/app/NUT-Monitor-py3qt5.in b/scripts/python/app/NUT-Monitor-py3qt5.in index 05160dcc9a..cfbffc3f45 100755 --- a/scripts/python/app/NUT-Monitor-py3qt5.in +++ b/scripts/python/app/NUT-Monitor-py3qt5.in @@ -833,6 +833,7 @@ class gui_updater : b"RB" : "%s" % _("Replace batteries !"), b"ALARM" : "%s" % _("Active alarms !"), b"BYPASS" : "Bypass %s" % _("(no battery protection)"), + b"ECO" : _("In ECO mode (as defined by vendor)"), b"CAL" : _("Performing runtime calibration"), b"OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), b"OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), diff --git a/tools/nutconf/nutconf-cli.cpp b/tools/nutconf/nutconf-cli.cpp index 3725e549a6..a12b071eb5 100644 --- a/tools/nutconf/nutconf-cli.cpp +++ b/tools/nutconf/nutconf-cli.cpp @@ -144,7 +144,7 @@ const char * Usage::s_text[] = { " [=]*", "Notification types:", " ONLINE, ONBATT, LOWBATT, FSD, COMMOK, COMMBAD, SHUTDOWN, REPLBATT, NOCOMM, NOPARENT,", - " CAL, NOTCAL, OFF, NOTOFF, BYPASS, NOTBYPASS, ALARM, NOTALARM,", + " CAL, NOTCAL, OFF, NOTOFF, BYPASS, NOTBYPASS, ECO, NOTECO, ALARM, NOTALARM,", " SUSPEND_STARTING, SUSPEND_FINISHED", "Notification flags:", " SYSLOG, WALL, EXEC, IGNORE",