Matthias Goebl
2006-Nov-26  14:57 UTC
[Nut-upsdev] Patch for optiups to support Zinto D from ONLINE USV-Systeme AG
Hi Arnaud,
Hi Scott,
Hi list,
Here is a patch to support the Zinto D from ONLINE USV-Systeme AG.
I already sent a version to Russell Kroll (2006-04-09), without no response
and I cannot find support for Zinto in svn until now.
I found a discussion on this list about the Xanto from ONLINE, but the Zinto
seems to use different commands.
The commands are quite similar to those for Opti-UPS, so I decided not to fork,
but to patch optiups. Where I found differences, I added
if(testvar(OPTI_ZINTO)).
As soon as someone adds another device with similar commands, one could decide
how to differentiate within the driver (where to use select-case).
What do you think?
Yours,
Matthias
-------------- next part --------------
Index: data/driver.list
==================================================================---
data/driver.list	(revision 606)
+++ data/driver.list	(working copy)
@@ -318,6 +318,7 @@
 "Oneac" "EG/ON Series"	"advanced interface"
"oneac"
 
 "Online"	"P-Series"	""	"genericups
upstype=14"
+"Online"	"Zinto D"	""	"optiups zinto=1"
 
 "OnLite"	"AQUA"		"50"	"megatec"
 
Index: man/optiups.8
==================================================================---
man/optiups.8	(revision 606)
+++ man/optiups.8	(working copy)
@@ -52,6 +52,12 @@
 nut should power down the system soon after you pull the plug.  When you are
done
 testing, you should remove this flag.
 
+.IP "zinto"
+
+Set this flag if your UPS is a Zinto D (or similar) from ONLINE USV-Systeme AG
+(www.online-ups.com). The commands are quite similar to those for Opti-UPS,
+but there are minor differences. The UPS has an additional switchable outlet.
+
 .SH BUGS
 
 On the 420E, ups.serial and ups.temperature are unsupported features.  This
Index: drivers/optiups.c
==================================================================---
drivers/optiups.c	(revision 606)
+++ drivers/optiups.c	(working copy)
@@ -3,6 +3,9 @@
    Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
    Copyright (C) 2006  Scott Heavner [Use my alioth acct: sheavner]
 
+   Support for Zinto D from ONLINE USV (only minor differences to OptiSafe UPS)
+   added by Matthias Goebl <matthias.goebl@goebl.net>
+
    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
    the Free Software Foundation; either version 2 of the License, or
@@ -50,6 +53,7 @@
 #define OPTI_MINPOLL		"status_only"
 #define OPTI_FAKELOW		"fake_lowbatt"
 #define OPTI_NOWARN_NOIMP	"nowarn_noimp"
+#define OPTI_ZINTO		"zinto"
 
 /* All serial commands put their response in the same buffer space */
 static char _buf[256];
@@ -79,6 +83,14 @@
 	{ "FF", "input.frequency", 0.1 },
 	{ "BT", "ups.temperature" },
 };
+static ezfill _pollv_zinto[] = {
+	{ "NV", "input.voltage", 2.0 },
+	{ "OL", "ups.load", 1.0 },
+	{ "OV", "output.voltage", 2.0 },
+	{ "OF", "output.frequency", 0.1 },
+	{ "NF", "input.frequency", 0.1 },
+	{ "BT", "ups.temperature" },
+};
 
 /* model "IO" is parsed differently in upsdrv_initinfo() */
 static ezfill _initv[] = {
@@ -105,6 +117,10 @@
 			r=-2;
 			upsdebugx(1, "READ: <unsupported command>");
 		}
+		if ( _buf[0] == 0x06 )
+		{
+			upsdebugx(2, "READ: <command done>");
+		}
 		else
 		{
 			upsdebugx(2, "READ: \"%s\"", _buf );
@@ -125,6 +141,7 @@
 {
 	upsdebugx(2, "SEND: \"%s\"", cmd );
 	ser_send( upsfd, cmd );
+	if ( testvar(OPTI_ZINTO) ) ser_send( upsfd, "\r\n" );
 	return optireadline();
 }
 
@@ -179,12 +196,26 @@
 	{
 		/* You do realize this will kill power to ourself.  Would probably only
 		 *   be useful for killing power for a slave computer */
+		if ( testvar(OPTI_ZINTO) )
+		{
+			optiquery( "Ct1" );
+			optiquery( "Cs0000000" );
+			sleep(2);
+			return STAT_INSTCMD_HANDLED;
+		}
 		optiquery( "Ct0" );
 		optiquery( "Cs00000000" );
                 return STAT_INSTCMD_HANDLED;
         }
 	else if (!strcasecmp(cmdname, "load.on"))
 	{
+		if ( testvar(OPTI_ZINTO) )
+		{
+			optiquery( "Ct1" );
+			optiquery( "Cu0000000" );
+			sleep(2);
+			return STAT_INSTCMD_HANDLED;
+		}
 		optiquery( "Ct0" );
 		optiquery( "Cu00000000" );
                 return STAT_INSTCMD_HANDLED;
@@ -193,6 +224,13 @@
 	{
 		/* This shuts down the UPS.  When the power returns to the UPS,
 		 *   it will power back up in its default state. */
+		if ( testvar(OPTI_ZINTO) )
+		{
+			optiquery( "Ct1" );
+			optiquery( "Cu0000010" );
+			optiquery( "Cs0000001" );
+			return STAT_INSTCMD_HANDLED;
+		}
 		optiquery( "Ct1" );
 		optiquery( "Cs00000010" );
                 return STAT_INSTCMD_HANDLED;
@@ -202,6 +240,12 @@
 		/* This actually stays off as long as the batteries hold,
 		 *   if the line power comes back before the batteries die,
 		 *   the UPS will never powerup its output stage!!! */
+		if ( testvar(OPTI_ZINTO) )
+		{
+			optiquery( "Ct1" );
+			optiquery( "Cs0000001" );
+			return STAT_INSTCMD_HANDLED;
+		}
 		optiquery( "Ct0" );
 		optiquery( "Cs00000010" );
                 return STAT_INSTCMD_HANDLED;
@@ -217,11 +261,52 @@
         return STAT_INSTCMD_UNKNOWN;
 }
 
+/* Handle variable setting */
+static int setvar(const char *varname, const char *val)
+{
+	int status;
 
+	if (sscanf(val, "%d", &status) != 1) {
+		return STAT_SET_UNKNOWN;
+	}
+
+	if (strcasecmp(varname, "outlet.1.switch") == 0) {    
+		status = status==1 ? 1 : 0;
+		dstate_setinfo( "outlet.1.switch", "%d", status);
+		optiquery(status ? "Oi11" : "Oi10");
+		dstate_dataok();
+		return STAT_SET_HANDLED;
+	}
+
+	return STAT_SET_UNKNOWN;
+}
+
 void upsdrv_initinfo(void)
 {
 	int r;
 
+	if ( testvar(OPTI_ZINTO) )
+	{
+		/* If UPS is off, switch it on first */
+		/* Online-UPS send only "2" when off, without "\r\n" */
+		/* Therefore without power we cannot identify the ups  */
+		if ( optiquery( "AG" ) < 1 )
+		{
+			ser_send( upsfd, "AG\r\n" );
+			r = ser_get_char(upsfd, &_buf[0], 1, 0);
+			if ( r == 1 && _buf[0] == '2' )
+			{
+				upslogx( LOG_WARNING, "ups was off, switched on" );
+				optiquery( "Ct1" );
+				optiquery( "Cu0000000" );
+				sleep(12);
+			}
+		}
+		optiquery( "Om11" );
+		optiquery( "Om21" );
+		optiquery( "ON" );
+	}
+
 	dstate_setinfo("driver.version.internal", "%s",
DRV_VERSION);
 
 	optifill( _initv, sizeof(_initv)/sizeof(_initv[0]) );
@@ -248,15 +333,45 @@
         dstate_addcmd("test.failure.start");
         dstate_addcmd("load.off");
         dstate_addcmd("load.on");
-        dstate_addcmd("shutdown.stop");
+	if( !testvar(OPTI_ZINTO) )
+        	dstate_addcmd("shutdown.stop");
         dstate_addcmd("shutdown.return");
         dstate_addcmd("shutdown.stayoff");
 	upsh.instcmd = instcmd;
+
+	if ( testvar(OPTI_ZINTO) )
+	{
+		dstate_setinfo("outlet.0.desc", "%s", "Main Outlet
1+2");
+		dstate_setinfo("outlet.1.desc", "%s", "Switchable
Outlet 3+4");
+		dstate_setinfo("outlet.0.id", "%d", 1);
+		dstate_setinfo("outlet.1.id", "%d", 2);
+		dstate_setinfo("outlet.0.switchable", "%d", 0);
+		dstate_setinfo("outlet.1.switchable", "%d", 1);
+		dstate_setinfo("outlet.1.switch", "%d", 1);
+		dstate_setflags("outlet.1.switch", ST_FLAG_RW | ST_FLAG_STRING);
+		dstate_setaux("outlet.1.switch", 1);
+		upsh.setvar = setvar;	
+	}
 }
 
 void upsdrv_updateinfo(void)
 {
 	int r = optiquery( "AG" );
+
+	/* Online-UPS send only "2" when off, without "\r\n" */
+	if ( r < 1 && testvar(OPTI_ZINTO) )
+	{
+		ser_send( upsfd, "AG\r\n" );
+		r = ser_get_char(upsfd, &_buf[0], 1, 0);
+		if ( r == 1 && _buf[0] == '2' )
+		{
+			status_init();
+			status_set("OFF");
+			status_commit();
+			return;
+		}
+	}
+
 	if ( r < 1 )
 	{
 		upslogx(LOG_ERR, "can't retrieve ups status" );
@@ -289,7 +404,10 @@
 		return;
 
 	/* read some easy settings */
-	optifill( _pollv, sizeof(_pollv)/sizeof(_pollv[0]) );
+	if ( testvar(OPTI_ZINTO) )
+		optifill( _pollv_zinto, sizeof(_pollv_zinto)/sizeof(_pollv_zinto[0]) );
+	else
+		optifill( _pollv, sizeof(_pollv)/sizeof(_pollv[0]) );
 
 	/* Battery voltage is harder */
 	r = optiquery( "BV" );
@@ -330,6 +448,24 @@
 	 *   turn off ups if on battery */
 	optiquery( "Ct1" );
 
+	/* What happens, if the power comes back *after* reading the ups status and
+     * before the shutdown command? For "Online-UPS Zinto D" *always*
asking for
+     * "shutdown shortly and power-up later" works perfectly, because
it forces
+     * a power cycle, even for the named race condition.*/
+	 * For Opti-UPS I have no information, so I didn't dare to change it.
+	 * BTW, Zinto expects only 7 digits after Cu/Cs.
+     * (Matthias Goebl)
+     */
+	if ( testvar(OPTI_ZINTO) )
+	{
+		/* On line power: Power up in 60 seconds (30 seconds after the following
shutdown) */
+        /* On battery: Power up when the line power returns */
+		optiquery( "Cu0000060" );
+		/* Shutdown in 30 seconds */
+		optiquery( "Cs0000030" );
+		return;
+	}
+
 	/* Just cycling power, schedule output stage to come back on in 60 seconds */
 	if ( !(s&OPTISBIT_ON_BATTERY_POWER) )
 		optiquery( "Cu00000600" );
@@ -349,6 +485,7 @@
 	addvar(VAR_FLAG, OPTI_MINPOLL, "Only poll for critical status
variables");
 	addvar(VAR_FLAG, OPTI_FAKELOW, "Fake a low battery status" );
 	addvar(VAR_FLAG, OPTI_NOWARN_NOIMP, "Supress warnings of unsupported
commands");
+	addvar(VAR_FLAG, OPTI_ZINTO, "UPS is a Zinto D from ONLINE UPS");
 }
 
 void upsdrv_banner(void)
Peter Selinger
2006-Nov-26  18:08 UTC
[Nut-upsdev] Patch for optiups to support Zinto D from ONLINE
Matthias, NUT drivers should always detect devices automatically if possible, rather than requiring the user to set a variable such as "zinto". Can you modify upsdrv_initinfo to detect the device type based on the model string (and/or if necessary, based on ups.mfr)? -- Peter Matthias Goebl wrote:> > > Hi Arnaud, > Hi Scott, > Hi list, > > Here is a patch to support the Zinto D from ONLINE USV-Systeme AG. > I already sent a version to Russell Kroll (2006-04-09), without no response > and I cannot find support for Zinto in svn until now. > I found a discussion on this list about the Xanto from ONLINE, but the Zinto > seems to use different commands. > > The commands are quite similar to those for Opti-UPS, so I decided not to fork, > but to patch optiups. Where I found differences, I added if(testvar(OPTI_ZINTO)). > As soon as someone adds another device with similar commands, one could decide > how to differentiate within the driver (where to use select-case). > What do you think? > > Yours, > Matthias > > --82I3+IH0IqGh5yIs > Content-Type: text/plain; charset=us-ascii > Content-Disposition: attachment; filename="nut.zinto.patch.mg" > > Index: data/driver.list > ==================================================================> --- data/driver.list (revision 606) > +++ data/driver.list (working copy) > @@ -318,6 +318,7 @@ > "Oneac" "EG/ON Series" "advanced interface" "oneac" > > "Online" "P-Series" "" "genericups upstype=14" > +"Online" "Zinto D" "" "optiups zinto=1" > > "OnLite" "AQUA" "50" "megatec" > > Index: man/optiups.8 > ==================================================================> --- man/optiups.8 (revision 606) > +++ man/optiups.8 (working copy) > @@ -52,6 +52,12 @@ > nut should power down the system soon after you pull the plug. When you are done > testing, you should remove this flag. > > +.IP "zinto" > + > +Set this flag if your UPS is a Zinto D (or similar) from ONLINE USV-Systeme AG > +(www.online-ups.com). The commands are quite similar to those for Opti-UPS, > +but there are minor differences. The UPS has an additional switchable outlet. > + > .SH BUGS > > On the 420E, ups.serial and ups.temperature are unsupported features. This > Index: drivers/optiups.c > ==================================================================> --- drivers/optiups.c (revision 606) > +++ drivers/optiups.c (working copy) > @@ -3,6 +3,9 @@ > Copyright (C) 1999 Russell Kroll <rkroll@exploits.org> > Copyright (C) 2006 Scott Heavner [Use my alioth acct: sheavner] > > + Support for Zinto D from ONLINE USV (only minor differences to OptiSafe UPS) > + added by Matthias Goebl <matthias.goebl@goebl.net> > + > 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 > the Free Software Foundation; either version 2 of the License, or > @@ -50,6 +53,7 @@ > #define OPTI_MINPOLL "status_only" > #define OPTI_FAKELOW "fake_lowbatt" > #define OPTI_NOWARN_NOIMP "nowarn_noimp" > +#define OPTI_ZINTO "zinto" > > /* All serial commands put their response in the same buffer space */ > static char _buf[256]; > @@ -79,6 +83,14 @@ > { "FF", "input.frequency", 0.1 }, > { "BT", "ups.temperature" }, > }; > +static ezfill _pollv_zinto[] = { > + { "NV", "input.voltage", 2.0 }, > + { "OL", "ups.load", 1.0 }, > + { "OV", "output.voltage", 2.0 }, > + { "OF", "output.frequency", 0.1 }, > + { "NF", "input.frequency", 0.1 }, > + { "BT", "ups.temperature" }, > +}; > > /* model "IO" is parsed differently in upsdrv_initinfo() */ > static ezfill _initv[] = { > @@ -105,6 +117,10 @@ > r=-2; > upsdebugx(1, "READ: <unsupported command>"); > } > + if ( _buf[0] == 0x06 ) > + { > + upsdebugx(2, "READ: <command done>"); > + } > else > { > upsdebugx(2, "READ: \"%s\"", _buf ); > @@ -125,6 +141,7 @@ > { > upsdebugx(2, "SEND: \"%s\"", cmd ); > ser_send( upsfd, cmd ); > + if ( testvar(OPTI_ZINTO) ) ser_send( upsfd, "\r\n" ); > return optireadline(); > } > > @@ -179,12 +196,26 @@ > { > /* You do realize this will kill power to ourself. Would probably only > * be useful for killing power for a slave computer */ > + if ( testvar(OPTI_ZINTO) ) > + { > + optiquery( "Ct1" ); > + optiquery( "Cs0000000" ); > + sleep(2); > + return STAT_INSTCMD_HANDLED; > + } > optiquery( "Ct0" ); > optiquery( "Cs00000000" ); > return STAT_INSTCMD_HANDLED; > } > else if (!strcasecmp(cmdname, "load.on")) > { > + if ( testvar(OPTI_ZINTO) ) > + { > + optiquery( "Ct1" ); > + optiquery( "Cu0000000" ); > + sleep(2); > + return STAT_INSTCMD_HANDLED; > + } > optiquery( "Ct0" ); > optiquery( "Cu00000000" ); > return STAT_INSTCMD_HANDLED; > @@ -193,6 +224,13 @@ > { > /* This shuts down the UPS. When the power returns to the UPS, > * it will power back up in its default state. */ > + if ( testvar(OPTI_ZINTO) ) > + { > + optiquery( "Ct1" ); > + optiquery( "Cu0000010" ); > + optiquery( "Cs0000001" ); > + return STAT_INSTCMD_HANDLED; > + } > optiquery( "Ct1" ); > optiquery( "Cs00000010" ); > return STAT_INSTCMD_HANDLED; > @@ -202,6 +240,12 @@ > /* This actually stays off as long as the batteries hold, > * if the line power comes back before the batteries die, > * the UPS will never powerup its output stage!!! */ > + if ( testvar(OPTI_ZINTO) ) > + { > + optiquery( "Ct1" ); > + optiquery( "Cs0000001" ); > + return STAT_INSTCMD_HANDLED; > + } > optiquery( "Ct0" ); > optiquery( "Cs00000010" ); > return STAT_INSTCMD_HANDLED; > @@ -217,11 +261,52 @@ > return STAT_INSTCMD_UNKNOWN; > } > > +/* Handle variable setting */ > +static int setvar(const char *varname, const char *val) > +{ > + int status; > > + if (sscanf(val, "%d", &status) != 1) { > + return STAT_SET_UNKNOWN; > + } > + > + if (strcasecmp(varname, "outlet.1.switch") == 0) { > + status = status==1 ? 1 : 0; > + dstate_setinfo( "outlet.1.switch", "%d", status); > + optiquery(status ? "Oi11" : "Oi10"); > + dstate_dataok(); > + return STAT_SET_HANDLED; > + } > + > + return STAT_SET_UNKNOWN; > +} > + > void upsdrv_initinfo(void) > { > int r; > > + if ( testvar(OPTI_ZINTO) ) > + { > + /* If UPS is off, switch it on first */ > + /* Online-UPS send only "2" when off, without "\r\n" */ > + /* Therefore without power we cannot identify the ups */ > + if ( optiquery( "AG" ) < 1 ) > + { > + ser_send( upsfd, "AG\r\n" ); > + r = ser_get_char(upsfd, &_buf[0], 1, 0); > + if ( r == 1 && _buf[0] == '2' ) > + { > + upslogx( LOG_WARNING, "ups was off, switched on" ); > + optiquery( "Ct1" ); > + optiquery( "Cu0000000" ); > + sleep(12); > + } > + } > + optiquery( "Om11" ); > + optiquery( "Om21" ); > + optiquery( "ON" ); > + } > + > dstate_setinfo("driver.version.internal", "%s", DRV_VERSION); > > optifill( _initv, sizeof(_initv)/sizeof(_initv[0]) ); > @@ -248,15 +333,45 @@ > dstate_addcmd("test.failure.start"); > dstate_addcmd("load.off"); > dstate_addcmd("load.on"); > - dstate_addcmd("shutdown.stop"); > + if( !testvar(OPTI_ZINTO) ) > + dstate_addcmd("shutdown.stop"); > dstate_addcmd("shutdown.return"); > dstate_addcmd("shutdown.stayoff"); > upsh.instcmd = instcmd; > + > + if ( testvar(OPTI_ZINTO) ) > + { > + dstate_setinfo("outlet.0.desc", "%s", "Main Outlet 1+2"); > + dstate_setinfo("outlet.1.desc", "%s", "Switchable Outlet 3+4"); > + dstate_setinfo("outlet.0.id", "%d", 1); > + dstate_setinfo("outlet.1.id", "%d", 2); > + dstate_setinfo("outlet.0.switchable", "%d", 0); > + dstate_setinfo("outlet.1.switchable", "%d", 1); > + dstate_setinfo("outlet.1.switch", "%d", 1); > + dstate_setflags("outlet.1.switch", ST_FLAG_RW | ST_FLAG_STRING); > + dstate_setaux("outlet.1.switch", 1); > + upsh.setvar = setvar; > + } > } > > void upsdrv_updateinfo(void) > { > int r = optiquery( "AG" ); > + > + /* Online-UPS send only "2" when off, without "\r\n" */ > + if ( r < 1 && testvar(OPTI_ZINTO) ) > + { > + ser_send( upsfd, "AG\r\n" ); > + r = ser_get_char(upsfd, &_buf[0], 1, 0); > + if ( r == 1 && _buf[0] == '2' ) > + { > + status_init(); > + status_set("OFF"); > + status_commit(); > + return; > + } > + } > + > if ( r < 1 ) > { > upslogx(LOG_ERR, "can't retrieve ups status" ); > @@ -289,7 +404,10 @@ > return; > > /* read some easy settings */ > - optifill( _pollv, sizeof(_pollv)/sizeof(_pollv[0]) ); > + if ( testvar(OPTI_ZINTO) ) > + optifill( _pollv_zinto, sizeof(_pollv_zinto)/sizeof(_pollv_zinto[0]) ); > + else > + optifill( _pollv, sizeof(_pollv)/sizeof(_pollv[0]) ); > > /* Battery voltage is harder */ > r = optiquery( "BV" ); > @@ -330,6 +448,24 @@ > * turn off ups if on battery */ > optiquery( "Ct1" ); > > + /* What happens, if the power comes back *after* reading the ups status and > + * before the shutdown command? For "Online-UPS Zinto D" *always* asking for > + * "shutdown shortly and power-up later" works perfectly, because it forces > + * a power cycle, even for the named race condition.*/ > + * For Opti-UPS I have no information, so I didn't dare to change it. > + * BTW, Zinto expects only 7 digits after Cu/Cs. > + * (Matthias Goebl) > + */ > + if ( testvar(OPTI_ZINTO) ) > + { > + /* On line power: Power up in 60 seconds (30 seconds after the following shutdown) */ > + /* On battery: Power up when the line power returns */ > + optiquery( "Cu0000060" ); > + /* Shutdown in 30 seconds */ > + optiquery( "Cs0000030" ); > + return; > + } > + > /* Just cycling power, schedule output stage to come back on in 60 seconds */ > if ( !(s&OPTISBIT_ON_BATTERY_POWER) ) > optiquery( "Cu00000600" ); > @@ -349,6 +485,7 @@ > addvar(VAR_FLAG, OPTI_MINPOLL, "Only poll for critical status variables"); > addvar(VAR_FLAG, OPTI_FAKELOW, "Fake a low battery status" ); > addvar(VAR_FLAG, OPTI_NOWARN_NOIMP, "Supress warnings of unsupported commands"); > + addvar(VAR_FLAG, OPTI_ZINTO, "UPS is a Zinto D from ONLINE UPS"); > } > > void upsdrv_banner(void) > > --82I3+IH0IqGh5yIs > Content-Type: text/plain; charset="us-ascii" > MIME-Version: 1.0 > Content-Transfer-Encoding: 7bit > Content-Disposition: inline > > _______________________________________________ > Nut-upsdev mailing list > Nut-upsdev@lists.alioth.debian.org > http://lists.alioth.debian.org/mailman/listinfo/nut-upsdev > --82I3+IH0IqGh5yIs-- >