From 207fed2282ce0bdf36a2fbb87aa8acb7180e2dd7 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 21 May 2012 10:33:10 +0000 Subject: [PATCH] Fix Liebert firmware (incorrect exponents) There are at least two Liebert firmware types which both report a VID:PID of 10af:0001. The newer ones tend not to have the Belkin broken Usage Pages (and therefore use standard HID PDC paths) but they have incorrect exponents for some fields. This patch fixes the values for the latter implementation Author: Charles Lepple Fossil-ID: SVN r3607 --- drivers/belkin-hid.c | 152 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 7 deletions(-) diff --git a/drivers/belkin-hid.c b/drivers/belkin-hid.c index d3b294b862..ce665e8e33 100644 --- a/drivers/belkin-hid.c +++ b/drivers/belkin-hid.c @@ -1,8 +1,9 @@ -/* belkin-hid.h - data to monitor Belkin UPS Systems USB/HID devices with NUT +/* belkin-hid.c - data to monitor Belkin UPS Systems USB/HID devices with NUT * * Copyright (C) * 2003 - 2008 Arnaud Quette * 2005 Peter Selinger + * 2011 Charles Lepple * * Sponsored by MGE UPS SYSTEMS * @@ -28,13 +29,18 @@ #include "belkin-hid.h" #include "usb-common.h" -#define BELKIN_HID_VERSION "Belkin HID 0.12" +#define BELKIN_HID_VERSION "Belkin HID 0.15" /* Belkin */ #define BELKIN_VENDORID 0x050d /* Liebert */ #define LIEBERT_VENDORID 0x10af +/* Note that there are at least two Liebert firmware types which both report + * a VID:PID of 10af:0001. The newer ones tend not to have the Belkin broken + * Usage Pages (and therefore use standard HID PDC paths) but they have + * incorrect exponents for some fields. + */ /* USB IDs device table */ static usb_device_id_t belkin_usb_device_table[] = { @@ -64,6 +70,123 @@ static usb_device_id_t belkin_usb_device_table[] = { { -1, -1, NULL } }; +static const char *liebert_online_fun(double value); +static const char *liebert_discharging_fun(double value); +static const char *liebert_charging_fun(double value); +static const char *liebert_lowbatt_fun(double value); +static const char *liebert_replacebatt_fun(double value); +static const char *liebert_shutdownimm_fun(double value); +static const char *liebert_config_voltage_fun(double value); +static const char *liebert_line_voltage_fun(double value); + +static info_lkp_t liebert_online_info[] = { + { 0, NULL, liebert_online_fun } +}; + +static info_lkp_t liebert_discharging_info[] = { + { 0, NULL, liebert_discharging_fun } +}; + +static info_lkp_t liebert_charging_info[] = { + { 0, NULL, liebert_charging_fun } +}; + +static info_lkp_t liebert_lowbatt_info[] = { + { 0, NULL, liebert_lowbatt_fun } +}; + +static info_lkp_t liebert_replacebatt_info[] = { + { 0, NULL, liebert_replacebatt_fun } +}; + +static info_lkp_t liebert_shutdownimm_info[] = { + { 0, NULL, liebert_shutdownimm_fun } +}; + +static info_lkp_t liebert_config_voltage_info[] = { + { 0, NULL, liebert_config_voltage_fun }, +}; + +static info_lkp_t liebert_line_voltage_info[] = { + { 0, NULL, liebert_line_voltage_fun }, +}; + +static double liebert_config_voltage_mult = 1.0; +static double liebert_line_voltage_mult = 1.0; +static char liebert_conversion_buf[10]; + +/* These lookup functions also cover the 1e-7 factor which seems to be due to a + * broken report descriptor in certain Liebert units. + */ +static const char *liebert_online_fun(double value) +{ + return value ? "online" : "!online"; +} + +static const char *liebert_discharging_fun(double value) +{ + return value ? "dischrg" : "!dischrg"; +} + +static const char *liebert_charging_fun(double value) +{ + return value ? "chrg" : "!chrg"; +} + +static const char *liebert_lowbatt_fun(double value) +{ + return value ? "lowbatt" : "!lowbatt"; +} + +static const char *liebert_replacebatt_fun(double value) +{ + return value ? "replacebatt" : "!replacebatt"; +} + +static const char *liebert_shutdownimm_fun(double value) +{ + return value ? "shutdownimm" : "!shutdownimm"; +} + +/*! Apply heuristics to Liebert ConfigVoltage for correction of other values. + * Logic is weird since the ConfigVoltage item comes after InputVoltage and + * OutputVoltage. + */ +static const char *liebert_config_voltage_fun(double value) +{ + if( value < 1 ) { + if( abs(value - 1e-7) < 1e-9 ) { + liebert_config_voltage_mult = 1e8; + liebert_line_voltage_mult = 1e7; /* stomp this in case input voltage was low */ + upsdebugx(2, "ConfigVoltage = %g -> assuming correction factor = %g", + value, liebert_config_voltage_mult); + } else { + upslogx(LOG_NOTICE, "ConfigVoltage exponent looks wrong, but not correcting."); + } + } + + snprintf(liebert_conversion_buf, sizeof(liebert_conversion_buf), "%.1f", + value * liebert_config_voltage_mult); + return liebert_conversion_buf; +} + +static const char *liebert_line_voltage_fun(double value) +{ + if( value < 1 ) { + if( abs(value - 1e-7) < 1e-9 ) { + liebert_line_voltage_mult = 1e7; + upsdebugx(2, "Input/OutputVoltage = %g -> assuming correction factor = %g", + value, liebert_line_voltage_mult); + } else { + upslogx(LOG_NOTICE, "LineVoltage exponent looks wrong, but not correcting."); + } + } + + snprintf(liebert_conversion_buf, sizeof(liebert_conversion_buf), "%.1f", + value * liebert_line_voltage_mult); + return liebert_conversion_buf; +} + /* some conversion functions specific to Belkin */ /* returns statically allocated string - must not use it again before @@ -338,12 +461,27 @@ static hid_info_t belkin_hid2nut[] = { { "ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion }, { "ups.test.result", 0, 0, "UPS.BELKINControls.BELKINTest", NULL, "%s", 0, belkin_test_info }, { "ups.type", 0, 0, "UPS.BELKINDevice.BELKINUPSType", NULL, "%s", 0, belkin_upstype_conversion }, - + /* Liebert PSA: */ + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, /* why .broken above? */ + { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%s", 0, divide_by_10_conversion }, + { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, + { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, + /* You would think these next two would be off by the same factor. You'd be wrong. */ + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, liebert_config_voltage_info }, + { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, /* status */ - { "BOOL", 0, 0, "UPS.PowerSummary.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_discharging_info }, /* might not need to be liebert_* version */ + { "BOOL", 0, 0, "UPS.PowerSummary.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.ShutdownImminent", NULL, NULL, 0, liebert_shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, liebert_shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_replacebatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_lowbatt_info }, + /* { "BOOL", 0, 0, "UPS.PowerSummary.BelowRemainingCapacityLimit", NULL, "%s", 0, lowbatt_info }, broken! */ { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_overload_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_overheat_conversion },