diff --git a/drivers/belkin-hid.c b/drivers/belkin-hid.c
index d3b294b..ce665e8 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 <arnaud.quette@free.fr>
  *  2005        Peter Selinger <selinger@users.sourceforge.net>
+ *  2011        Charles Lepple <clepple+nut@gmail>
  *
  *  Sponsored by MGE UPS SYSTEMS <http://www.mgeups.com>
  *
@@ -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 },
