--- powerp-txt.c	2016-01-09 16:48:41.000000000 -0500
+++ /Users/clepple/Downloads/powerp-txt.c	2016-01-28 08:58:05.000000000 -0500
@@ -6,6 +6,9 @@
  *	2007        Doug Reynolds <mav@wastegate.net>
  *	2007-2008   Arjen de Korte <adkorte-guest@alioth.debian.org>
  *	2012        Timothy Pearson <kb9vqf@pearsoncomputing.net>
+ *  2016        Ben Kamen <bkamen@benjammin.net>
+ *              * Rewrite powpan_status() to deal with additional UPS models...
+ *              * ... and to handle parsing differently (better?)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,6 +36,10 @@
 
 #include "powerp-txt.h"
 
+#define ON_BATTERY		0x40
+#define LOW_BATTERY		0x20
+#define TEST_BATTERY	0x08
+
 typedef struct {
 	float          i_volt;
 	float          o_volt;
@@ -230,7 +237,7 @@
 	 * was used for autodetection of the UPS. No need to do it again.
 	 */
 	if ((s = strtok(&powpan_answer[1], ",")) != NULL) {
-		dstate_setinfo("ups.model", "%s", str_rtrim(s, ' '));
+		dstate_setinfo("ups.model", "%s", rtrim(s, ' '));
 	}
 	if ((s = strtok(NULL, ",")) != NULL) {
 		dstate_setinfo("ups.firmware", "%s", s);
@@ -239,7 +246,7 @@
 		dstate_setinfo("ups.serial", "%s", s);
 	}
 	if ((s = strtok(NULL, ",")) != NULL) {
-		dstate_setinfo("ups.mfr", "%s", str_rtrim(s, ' '));
+		dstate_setinfo("ups.mfr", "%s", rtrim(s, ' '));
 	}
 
 	/*
@@ -364,16 +371,22 @@
 
 static int powpan_status(status_t *status)
 {
-	int	ret;
+	const char      dChars []   = "IOLBVTHFRQCS";
+	int             d = 0, ret = 0;
+	float           f;
+	char            *ptr;
+	unsigned int    i, got_value = 0, criticalCount = 0, count = 0;
+
+	// these seem silly to put here.
+	status->has_b_volt = 0;
+	status->has_o_freq = 0;
+	status->has_runtime = 0;
 
 	ser_flush_io(upsfd);
 
-	/*
-	 * WRITE D\r
-	 * READ #I119.0O119.0L000B100T027F060.0S..\r
-	 *      01234567890123456789012345678901234
-	 *      0         1         2         3
-	 */
+	upsdebugx(3, "---------------------------------------");
+	upsdebug_hex(3, "send", "D\r", 2);
+
 	ret = ser_send_pace(upsfd, UPSDELAY, "D\r");
 
 	if (ret < 0) {
@@ -386,12 +399,12 @@
 		return -1;
 	}
 
-	upsdebug_hex(3, "send", "D\r", 2);
-
 	usleep(200000);
 
-	ret = ser_get_buf_len(upsfd, powpan_answer, 35, SER_WAIT_SEC, SER_WAIT_USEC);
+	ret = ser_get_buf_len(upsfd, powpan_answer, 55, SER_WAIT_SEC, SER_WAIT_USEC);
 
+	// Old code that times out and returns with an error. we're not doing it that way anymore. (maybe not the way?)
+/*
 	if (ret < 0) {
 		upsdebug_with_errno(3, "read");
 		upsdebug_hex(4, "  \\_", powpan_answer, 35);
@@ -403,53 +416,181 @@
 		upsdebug_hex(4, "  \\_", powpan_answer, 35);
 		return -1;
 	}
+*/
 
 	upsdebug_hex(3, "read", powpan_answer, ret);
 
+	/*
+	 * WRITE D\r
+	 * READ #I119.0O119.0L000B100T027F060.0S..\r
+	 *      01234567890123456789012345678901234
+	 *      0         1         2         3         4
+	 *      01234567890123456789012345678901234567890123456789
+	 *      #I120.0O120.0L018B100T027H060.0F060.0R051Q002Sxx		<-- this is from a PR1500LCDRTXL2Ua
+	 *
+	 */
+
+	upsdebugx(6, "ret=%d string=%s", ret, powpan_answer);
+
+	for (i = 0; i < strlen(dChars); i++) {
+
+		ptr = strchr(powpan_answer, dChars[i] );
+
+		if (!ptr) {
+			continue;
+		}
+
+		upsdebugx(7, "Recv String = %s", ptr);
+
+		ptr++;
+		got_value = 0;
+
+		switch (dChars[i]) {
+			case 'O':
+			case 'I':
+			case 'H':
+			case 'F':
+			case 'V':
+			case 'Q':
+						if (sscanf(ptr, "%f", &f) ) { got_value = 1; }
+						break;
+			case 'T':
+			case 'L':
+			case 'B':
+			case 'R':
+			case 'C':
+						if (sscanf(ptr, "%d", &d) ) { got_value = 1; }
+						break;
+
+			case 'S':	if ( sscanf(ptr, "%2c", status->flags) ) {
+							got_value = 1;
+						}
+						break;
+		}
+
+		if (got_value) {
+			switch (dChars[i]) {
+				case 'I':	status->i_volt = f;
+							upsdebugx(3, "Input Voltage = %f", status->i_volt);
+							criticalCount++;
+							break;
+				case 'O':	status->o_volt = f;
+							upsdebugx(3, "Output Voltage = %f", status->o_volt);
+							criticalCount++;
+							break;
+				case 'F':	status->i_freq = f;
+							upsdebugx(3, "Input  Frequency = %f", status->i_freq);
+							criticalCount++;
+							break;
+				case 'H':	status->o_freq = f;
+							upsdebugx(3, "Output Frequency = %f", status->o_freq);
+							status->has_o_freq = 1;
+							break;
+				case 'V':	status->b_volt = f;
+							upsdebugx(3, "Battery Voltage = %f", status->b_volt);
+							status->has_b_volt = 1;
+							break;
+				case 'L':	status->o_load = d;
+							upsdebugx(3, "Output Load = %03d%%", status->o_load);
+							criticalCount++;
+							break;
+				case 'B':	status->b_chrg = d;
+							upsdebugx(3, "Battery Capacity = %03d%%", status->b_chrg);
+							criticalCount++;
+							break;
+				case 'T':	status->u_temp = d;
+							upsdebugx(3, "UPS Temperature = %03d", status->u_temp);	// not sure if this ever goes negative
+							criticalCount++;
+							break;
+				case 'R':	status->runtime = d;
+							upsdebugx(3, "UPS Runtime = %d", status->runtime);
+							status->has_runtime = 1;
+							break;
+				case 'C':	status->c_unknwn = d;
+							upsdebugx(3, "Unknown Value C = %d", status->c_unknwn);
+							break;
+				case 'Q':	status->q_unknwn = f;
+							upsdebugx(3, "Unknown Value Q = %f", status->q_unknwn);
+							break;
+				case 'S':	upsdebugx(3, "UPS Status =0x%X%X", status->flags[0], status->flags[1]);
+							criticalCount++;
+							break;
+			}
+
+			count++;
+		}
+	}
+
+	if ( criticalCount < 7 ) {
+		upsdebugx(3, "Insufficient Cricital Parameter Count = %u", criticalCount);
+		// return -1; // fail at this point?
+	}
+
+	upsdebugx(3, "Total Parameter Count = %u", count);
+
+/*
 	ret = sscanf(powpan_answer, "#I%fO%fL%dB%dT%dF%fS%2c\r",
-		&status->i_volt, &status->o_volt, &status->o_load,
-		&status->b_chrg, &status->u_temp, &status->i_freq,
-		status->flags);
+			&status->i_volt,
+			&status->o_volt,
+			&status->o_load,
+			&status->b_chrg,
+			&status->u_temp,
+			&status->i_freq,
+			status->flags
+		);
 
 	if (ret >= 7) {
 		status->has_b_volt = 0;
 		status->has_o_freq = 0;
 		status->has_runtime = 0;
-	}
-	else {
-		ret = ser_get_buf_len(upsfd, powpan_answer+35, 23, SER_WAIT_SEC, SER_WAIT_USEC);
+	} else {
+
+		ret = ser_get_buf_len(upsfd, powpan_answer+35, 25, SER_WAIT_SEC, SER_WAIT_USEC);
 
 		if (ret < 0) {
 			upsdebug_with_errno(3, "read");
-			upsdebug_hex(4, "  \\_", powpan_answer+35, 23);
+			upsdebug_hex(4, "  \\_", powpan_answer+35, 25);
 			return -1;
 		}
 		
 		if (ret == 0) {
 			upsdebugx(3, "read: timeout");
-			upsdebug_hex(4, "  \\_", powpan_answer+35, 23);
+			upsdebug_hex(4, "  \\_", powpan_answer+35, 25);
 			return -1;
 		}
 		
 		upsdebug_hex(3, "read", powpan_answer, ret);
 
 		ret = sscanf(powpan_answer, "#I%fO%fL%dB%dV%fT%dF%fH%fR%dC%dQ%fS%2c\r",
-		&status->i_volt, &status->o_volt, &status->o_load,
-		&status->b_chrg, &status->b_volt, &status->u_temp,
-		&status->i_freq, &status->o_freq, &status->runtime,
-		&status->c_unknwn, &status->q_unknwn, status->flags);
+				&status->i_volt,	// Innn.n
+				&status->o_volt,	// Onnn.n
+				&status->o_load,	// Lnnn
+				&status->b_chrg,	// Bnnn
+				&status->b_volt,	// Vnnn.n
+				&status->u_temp,	// Tnnn
+				&status->i_freq,	// Fnnn
+				&status->o_freq,	// Hnnn.n
+				&status->runtime,	// Rnnn.n
+				&status->c_unknwn,	// Unknown
+				&status->q_unknwn,	// Unknown
+				status->flags		// Unknown
+			);
+
 		status->has_b_volt = 1;
 		status->has_o_freq = 1;
 		status->has_runtime = 1;
-		dstate_setinfo("battery.voltage.nominal", "%g", 72.0);
+
+		dstate_setinfo("battery.voltage.nominal", "%g", 72.0);	// this should be set to battery.voltage.nominal * battery.packs for the PR1500LCDRTXL2Ua
 		dstate_setinfo("output.voltage.nominal", "%g", 120.0);
 	}
 
 	if (ret < 7) {
-		upsdebugx(4, "Parsing status string failed");
+		upsdebugx(4, "Too Few Status Parameters (%u) for operation.", ret );
 		return -1;
 	}
 
+*/
+
 	return 0;
 }
 
@@ -467,6 +608,7 @@
 	dstate_setinfo("input.frequency", "%.1f", status.i_freq);
 	dstate_setinfo("ups.temperature", "%d", status.u_temp);
 	dstate_setinfo("battery.charge", "%d", status.b_chrg);
+
 	if (status.has_b_volt) {
 		dstate_setinfo("battery.voltage", "%.1f", status.b_volt);
 	}
@@ -479,18 +621,18 @@
 
 	status_init();
 
-	if (status.flags[0] & 0x40) {
+	if (status.flags[0] & ON_BATTERY) {
 		status_set("OB");
 	} else {
 		status_set("OL");
 	}
 
-	if (status.flags[0] & 0x20) {
+	if (status.flags[0] & LOW_BATTERY) {
 		status_set("LB");
 	}
 
 	/* !OB && !TEST */
-	if (!(status.flags[0] & 0x48)) {
+	if (! (status.flags[0] & (ON_BATTERY | TEST_BATTERY) ) ) {
 
 		if (status.o_volt < 0.5 * status.i_volt) {
 			upsdebugx(2, "%s: output voltage too low", __func__);
@@ -505,7 +647,7 @@
 		}
 	}
 
-	if (status.flags[0] & 0x08) {
+	if (status.flags[0] & TEST_BATTERY) {
 		status_set("TEST");
 	}
 
@@ -515,7 +657,7 @@
 
 	status_commit();
 
-	return (status.flags[0] & 0x40) ? 1 : 0;
+	return (status.flags[0] & ON_BATTERY) ? 1 : 0;
 }
 
 static int powpan_initups(void)
