[Nut-upsdev] [PATCH 32/36] Add basic temperature monitoring support to apcsmart.

Greg A. Woods woods at planix.com
Thu Mar 8 23:21:43 UTC 2012


From: "Greg A. Woods" <woods at planix.com>

The "ups.temperature" value is monitored, if available, and if it
exceeds the configured "maxtemp" variable's value, or 150 by default,
the UPS reports that it is DYING so that NUT can shut everything down
and then turn it off with the upsdrv_shutdown(epo=1) call.
---
 docs/man/apcsmart.txt |   33 ++++++++++++++++++++++
 drivers/apcsmart.c    |   74 ++++++++++++++++++++++++++++++++++++++++++++++---
 drivers/apcsmart.h    |    3 ++
 3 files changed, 106 insertions(+), 4 deletions(-)

diff --git a/docs/man/apcsmart.txt b/docs/man/apcsmart.txt
index fba4a28..a7df349 100644
--- a/docs/man/apcsmart.txt
+++ b/docs/man/apcsmart.txt
@@ -282,6 +282,39 @@ Furthermore, this allows to specify conditions similary to how it's done in
 apcupsd daemon, so it should be welcome by people used to that software.
 
 
+MONITORING FOR UNHEALTHY ENVIRONMENTS
+-------------------------------------
+
+Many models of APC SmartUPS units can report on their internal
+temperature (see the *ups.temperature* variable, and possibly, if
+supported, the *ambient.temperature* variable).  The driver will report
+a *DYING* state if this temperature exceeds some pre-set value.  The
+default maximum temperature is set to 150 degrees Celsius, the point
+where many plastics begin to melt, but far past the point where the
+batteries have probably already self-destructed.  A reasonable value to
+use may be somewhere between 45 and 50.  Most battery specifications
+warn that operating over 40 C for long will seriously lower the
+battery's expected lifetime.  Note that the battery may not be at the
+same temperature as the sensor.
+
+This maximum operating temperature can be set with the following option:
+
+*maxtemp*=[1-150]::
+
+    This option takes a numeric value in the range of 1 to 150
+    representing the temperature in degrees Celsius above which point
+    this driver will report that the UPS is in the emergency *DYING*
+    state.  Typically this will cause connected systems to be safely
+    shut down and the monitored UPS to be powered down completely (if
+    possible) so that human intervention will be required to restart
+    things.
+
+You may wish to observe the normal operating temperature of your UPS as
+reported by this driver, and perhaps measure it independently by hand as
+well, before setting a maximum temperature which will protect your UPS
+and nearby systems from certain physical damage.
+
+
 SUPPORTED INSTANT COMMANDS
 --------------------------
 
diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c
index fed51b2..932dd9c 100644
--- a/drivers/apcsmart.c
+++ b/drivers/apcsmart.c
@@ -254,6 +254,8 @@ static void ups_status_set(void)
 		status_set("LB");		/* low battery */
 	if (ups_status & APC_STAT_RB)
 		status_set("RB");		/* replace batt */
+	if (ups_status & APC_STAT_DYING)
+		status_set("DYING");		/* dying! */
 
 	if (ups_status == 0)
 		status_set("OFF");
@@ -261,6 +263,45 @@ static void ups_status_set(void)
 	status_commit();
 }
 
+static void check_temperature(void)
+{
+	char *val;
+	int maxtemp = APC_MELTING_TEMP;
+
+	if ((val = getval("maxtemp"))) {
+		maxtemp = atoi(val);
+	}
+	/*
+	 * XXX potentially we should have "ups.temperature.maximum" and
+	 * "ambient.temperature.maxium" as configuration settings and then
+	 * compare each against the respective variables reported by the UPS.
+	 */
+	if ((val = dstate_getinfo("ups.temperature")) ||
+	    (val = dstate_getinfo("ambient.temperature"))) {
+		int curtemp = atoi(val);
+
+		if (curtemp > maxtemp) {
+			upslogx(LOG_EMERG, "current temperature of %d C exceeds maximum temperature of %d C", curtemp, maxtemp);
+
+			/*
+			 * XXX if apcsmart also supported alarms we would also
+			 * set an alarm status bit and set the description of
+			 * the alarm in ups.alarm
+			 *
+			 * XXX we could also have an alarmtemp
+			 * (ups.temperature.alarm) setting so that the alarm
+			 * could be raised before the maxtemp
+			 * (ups.temperature.maximum) is reached and give the
+			 * operator a chance to do something else externally
+			 * before an emergency shutdown occurs (such as turn on
+			 * a fan, open a window or door, etc.).
+			 */
+			ups_status |= APC_STAT_DYING;
+			ups_status_set();
+		}
+	}
+}
+
 static void alert_handler(char ch)
 {
 	switch (ch) {
@@ -901,7 +942,6 @@ static int update_status(void)
 	}
 
 	ups_status = strtol(buf, 0, 16) & 0xff;
-	ups_status_set();
 
 	dstate_dataok();
 
@@ -930,12 +970,15 @@ static void oldapcsetup(void)
 	query_ups("ups.temperature");
 	query_ups("ups.load");
 
-	update_status();
+	/* look for out-of-bounds values, perhaps trigger DYING state */
+	check_temperature();
 
 	/*
 	 * If we have come down this path then we dont do capabilities and
 	 * other shiny features.
 	 */
+
+	(void) update_status();
 }
 
 static void protocol_verify(unsigned char cmd)
@@ -1591,6 +1634,9 @@ void upsdrv_shutdown(int epo)
 		upsdrv_shutdown_simple(epo);
 }
 
+/*
+ * update vars marked with the APC_POLL flag
+ */
 static void update_info_normal(void)
 {
 	int	i;
@@ -1611,6 +1657,9 @@ static void update_info_normal(void)
 	upsdebugx(3, "update_info_normal: done");
 }
 
+/*
+ * update all vars
+ */
 static void update_info_all(void)
 {
 	int	i;
@@ -1992,6 +2041,7 @@ void upsdrv_makevartable(void)
 	addvar(VAR_VALUE, "sdtype", "specify simple shutdown method (0 - " APC_SDMAX ", default=0)");
 	addvar(VAR_VALUE, "epotype", "specify emergency power off method (0 or 3, default=3)");
 	addvar(VAR_VALUE, "advorder", "enable advanced shutdown control (list of values 0-4)");
+	addvar(VAR_VALUE, "maxtemp", "maximum temperature (degrees C)");
 }
 
 void upsdrv_initups(void)
@@ -2035,6 +2085,14 @@ void upsdrv_initups(void)
 			}
 		}
 	}
+
+	if ((val = getval("maxtemp"))) {
+		int maxtemp = atoi(val);
+
+		if (maxtemp < 0 || maxtemp > APC_MELTING_TEMP) {
+			fatalx(EXIT_FAILURE, "maxtemp (%d) out of range (0-%d)", maxtemp, APC_MELTING_TEMP);
+		}
+	}
 }
 
 void upsdrv_cleanup(void)
@@ -2109,12 +2167,20 @@ void upsdrv_updateinfo(void)
 	time(&now);
 
 	/* refresh all variables hourly */
+	/* XXX perhaps we should use a timeout based on a configurable value! */
 	/* does not catch measure-ups II insertion/removal */
 	if (difftime(now, last_full) > 3600) {
 		last_full = now;
 		update_info_all();
-		return;
+	} else {
+		/* just update vars marked with the APC_POLL flag */
+		update_info_normal();
 	}
 
-	update_info_normal();
+	/* look for out-of-bounds values, perhaps trigger DYING state */
+	check_temperature();
+
+	ups_status_set();
+
+	return;
 }
diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h
index 542a035..195c566 100644
--- a/drivers/apcsmart.h
+++ b/drivers/apcsmart.h
@@ -150,4 +150,7 @@
 #define APC_SDFMT	"^[0-5]$"
 #define APC_EDFMT	"^[03]$"
 
+/* maximum temperature before the UPS literally melts */
+#define APC_MELTING_TEMP	150	/* degrees C; probably actually too high */
+
 #endif
-- 
1.7.9.2




More information about the Nut-upsdev mailing list