[Nut-upsdev] new(er) SEC driver.
Julius P. Malkiewicz
julius at sonartech.com.au
Mon Dec 12 23:13:51 UTC 2005
Hi!
I've recently come into to the possesion of a couple of Belkin Omniguard
3200 UPS's. Given the horrible apache/java based software they come with, I
decided to see if I cannot get them to work with NUT.
To cut a long story short, they work out to be SEC -based, and extensively
modifying the nut-1.4 driver code to come up with the attached two files.
I've marked them experimental, as they seem to work on the two UPS I have,
but the driver is generic ... and have only tested the features that the
omniguard support ... I cannot fully test all features. That said, the driver
seems stable and has been working here for a few days now :).
The only 'feature' that is missing is alarms, but it seems that alarms are
an incomplete feature with nut ....
I hope this driver can be re-instated into the 2.X series of nut. Hopefully
it will help others out there who have a SEC-based UPS and have been stuck with
the nut ...
- Julius.
-------------- next part --------------
/* -*- Mode: C -*-
* sec.h -- header for sec protocol module
*
* Copyright (C) 2000 John Marley
* The author can be contacted at: John.Marley at alcatel.com.au
*
* Author : John Marley
* Created On : Thu Mar 29 11:07:01 2001
* Last Modified By: John Marley
* Last Modified On: Fri May 4 16:26:00 2001
* Update Count : 53
* Status : Unknown, Use with caution!
* $Locker: $
* $Log: sec.h,v $
* Revision 1.2 2001/05/08 03:21:09 marleyj
* Added supported() macro.
*
* Revision 1.1 2001/05/02 04:54:33 marleyj
* Initial revision
*
*/
#ifndef _SEC_H_
#define _SEC_H_
#define SEC_MSG_STARTCHAR '^'
#define SEC_POLLCMD 'P'
#define SEC_SETCMD 'S'
#define SEC_DATAMSG 'D'
#define SEC_UPSMSG '*'
#define SEC_ACK '1'
#define SEC_NAK '0'
/* commands */
#define SEC_CMD_AVAILP1 "AP1" /* Part1 of available variables */
#define SEC_CMD_AVAILP2 "AP2" /* Part1 of available variables */
#define SEC_CMD_AUTORESTART "ATR" /* Enable/disable auto restart */
#define SEC_CMD_MFR "MAN" /* UPS Manufacturer */
#define SEC_CMD_MOD "MOD" /* UPS Model */
#define SEC_CMD_NOMINAL "NOM" /* Nominal Values */
#define SEC_CMD_SHUTDOWN "PSD" /* Shutdown after delay/cancel */
#define SEC_CMD_REBOOT "RWD" /* Reboot with duration/cancel */
#define SEC_CMD_SHUTTYPE "SDA" /* Shutdown Type */
#define SEC_CMD_BATTSTAT "ST1" /* Battery Status */
#define SEC_CMD_INPUTSTAT "ST2" /* Input Status */
#define SEC_CMD_OUTPUTSTAT "ST3" /* Output Status */
#define SEC_CMD_BYPASSSTAT "ST4" /* Bypass Status */
#define SEC_CMD_ALARMSTAT "ST5" /* UPS Alarms */
#define SEC_CMD_STARTDELAY "STD" /* Startup after delay */
#define SEC_CMD_TESTRESULT "STR" /* Test Results */
#define SEC_CMD_TEST "TST" /* UPS Test/abort */
#define SEC_CMD_BAUDRATE "UBR" /* UPS Baud Rate */
#define SEC_CMD_UPSID "UID" /* UPS Identifier */
#define SEC_CMD_VERSION "VER" /* UPS Software Version */
/* variables */
#define SEC_UNUSED ( 0)
#define SEC_ALARM_PWRWAIT ( 1)
#define SEC_ALARM_BADBYPASS ( 2)
#define SEC_ALARM_CHARGFAIL ( 3)
#define SEC_ALARM_FANFAIL ( 4)
#define SEC_ALARM_FUSEFAIL ( 5)
#define SEC_ALARM_GENERAL ( 6)
#define SEC_ALARM_BADINPUT ( 7)
#define SEC_ALARM_BADOUTPUT ( 8)
#define SEC_ALARM_OUTPUTOFF ( 9)
#define SEC_ALARM_OVERLOAD (10)
#define SEC_ALARM_IMMSHUT (11)
#define SEC_ALARM_PENDSHUT (12)
#define SEC_ALARM_SYSOFF (13)
#define SEC_ALARM_TEMP (14)
#define SEC_ALARM_UPSSHUT (15)
#define SEC_AUDIBLE_ALARM (16)
#define SEC_AUTORESTART (17)
#define SEC_BATTERY_CHARGE (18)
#define SEC_BATTERY_COND (19)
#define SEC_BATTERY_CURRENT (20)
#define SEC_BATTERY_INSTALLED (21)
#define SEC_BATTERY_STATUS (22)
#define SEC_BATTERY_TEMPERATURE (23)
#define SEC_BATTERY_VOLTAGE (24)
#define SEC_BATTERY_CURRENT_1 (25)
#define SEC_BATTERY_CURRENT_2 (26)
#define SEC_BATTERY_CURRENT_3 (27)
#define SEC_BYPASS_FREQUENCY (28)
#define SEC_BYPASS_NUM_LINES (29)
#define SEC_BYPASS_POWER_1 (30)
#define SEC_BYPASS_POWER_2 (31)
#define SEC_BYPASS_POWER_3 (32)
#define SEC_BYPASS_VOLTAGE_1 (33)
#define SEC_BYPASS_VOLTAGE_2 (34)
#define SEC_BYPASS_VOLTAGE_3 (35)
#define SEC_ESTIMATED_CHARGE (36)
#define SEC_ESTIMATED_MINUTES (37)
#define SEC_HIGH_VOLT_XFER_POINT (38)
#define SEC_UPS_IDENTIFICATION (39)
#define SEC_INPUT_CURRENT_1 (40)
#define SEC_INPUT_CURRENT_2 (41)
#define SEC_INPUT_CURRENT_3 (42)
#define SEC_INPUT_FREQUENCY_1 (43)
#define SEC_INPUT_FREQUENCY_2 (44)
#define SEC_INPUT_FREQUENCY_3 (45)
#define SEC_INPUT_LINE_BADS (46)
#define SEC_INPUT_NUM_LINES (47)
#define SEC_INPUT_POWER_1 (48)
#define SEC_INPUT_POWER_2 (49)
#define SEC_INPUT_POWER_3 (50)
#define SEC_INPUT_VOLTAGE_1 (51)
#define SEC_INPUT_VOLTAGE_2 (52)
#define SEC_INPUT_VOLTAGE_3 (53)
#define SEC_LOW_VOLT_XFER_POINT (54)
#define SEC_MANUFACTURER (55)
#define SEC_MODEL (56)
#define SEC_NOM_BATTERY_LIFE (57)
#define SEC_NOM_INPUT_FREQUENCY (58)
#define SEC_NOM_INPUT_VOLTAGE (59)
#define SEC_NOM_LOW_BATTERY_TIME (60)
#define SEC_NOM_OUTPUT_FREQUENCY (61)
#define SEC_NOM_OUTPUT_POWER (62)
#define SEC_NOM_OUTPUT_VOLTAGE (63)
#define SEC_NOM_VA_RATING (64)
#define SEC_OUTPUT_CURRENT_1 (65)
#define SEC_OUTPUT_CURRENT_2 (66)
#define SEC_OUTPUT_CURRENT_3 (67)
#define SEC_OUTPUT_FREQUENCY (68)
#define SEC_OUTPUT_LOAD_1 (69)
#define SEC_OUTPUT_LOAD_2 (70)
#define SEC_OUTPUT_LOAD_3 (71)
#define SEC_OUTPUT_NUM_LINES (72)
#define SEC_OUTPUT_POWER_1 (73)
#define SEC_OUTPUT_POWER_2 (74)
#define SEC_OUTPUT_POWER_3 (75)
#define SEC_OUTPUT_SOURCE (76)
#define SEC_OUTPUT_VOLTAGE_1 (77)
#define SEC_OUTPUT_VOLTAGE_2 (78)
#define SEC_OUTPUT_VOLTAGE_3 (79)
#define SEC_REBOOT_WITH_DURATION (80)
#define SEC_SECONDS_ON_BATTERY (81)
#define SEC_SHUTDOWN_TYPE (82)
#define SEC_SHUTDOWN_AFTER_DELAY (83)
#define SEC_SOFTWARE_VERSION (84)
#define SEC_STARTUP_AFTER_DELAY (85)
#define SEC_TEST_RESULTS_DETAIL (86)
#define SEC_TEST_RESULTS_SUMMARY (87)
#define SEC_TEST_TYPE (88)
#define SEC_BAUD_RATE (89)
/* Some baud rates for setup_serial() */
#define SEC_NUMBAUDS 5
struct baud_rate_t {
int rate;
int name;
};
/*
* Exhaustive information for each possible variable
*/
#define SEC_NUMVARS 90
#define SEC_MAX_VARSIZE 65
struct sec_varlist_t {
char *infotag; /* variable names from new-names.txt */
char *name; /* Human readable text (also in shared-tables.h) */
float unit; /* Variable should be scaled by this */
char *cmd; /* Command to send to get/set variable */
int field; /* Which returned field variable corresponsd to */
int size; /* string length/integer max/enum count */
int flags; /* Flags for addinfo() */
};
/*
* List of possible enumerations
*/
struct sec_enumdata_t {
int type; /* corresponding variable */
int index; /* offset from first enum */
const char *value; /* enumerated value */
};
/* a type for the supported variables */
#define SEC_QUERYLIST_LEN 17
#define SEC_MAXFIELDS 16
struct sec_querylist_t {
char *command; /* sec command */
int varnum[SEC_MAXFIELDS]; /* sec variable number for each field */
} sec_querylist[SEC_QUERYLIST_LEN];
#define sqv(a,b) sec_querylist[a].varnum[b]
#endif /* _SEC_H_ */
-------------- next part --------------
/* -*- Mode: C -*-
* sec.c -- new style SEC UPS Protocol driver
*
* Copyright (C) 2001 John Marley
* The author can be contacted at: John.Marley at alcatel.com.au
*
* 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
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author : John Marley
* Created On : Wed Mar 28 14:48:26 2001
* Last Modified By: Jules Taplin
* Last Modified On: Sunday August 11, 2002 19:30 GMT
* Update Count : 198
* Status : Unknown, Use with caution!
* $Locker: $
* $Log: sec.c,v $
*
* Revision 1.6 11 August 2002 Jules Taplin. jules at netsitepro.co.uk
* increased size of buffer that holds data returned from UPS during
* probing of baud rate in function setup_serial().
*
* Move function call nolongertimeout() in sec_upsrecv outside of loop
* so the timeout is reset only when a valid message start character
* from the UPS is received.
*
* Revision 1.5 10 August 2002 Eric Lawson. elawson at inficad.com
* Fix setup_serial so that any valid response from the UPS is accepted
* as having found the correct serial port baud rate. During serial port
* baud rate probing, some UPS units get confused by commands sent at
* the wrong baud rate and always reports a command failed the first time
* the command is sent at the correct baud rate.
*
* Revision 1.4 17 July 2002 Eric Lawson. elawson at inficad.com
* added shutdown feature
*
* Revison 1.3 15 July 2002 Eric Lawson. elawson at inficad.com
*
* Use actual sec protocol command instead of bogus command when
* probing for a ups on the serial port in the setup_serial function.
*
* Zero msglen before calling sec_cmd the 2nd time in upsdrv_initinfo
* function. This fixes driver's use of sec protocol parameters
* numbered 47 and up.
*
* Revision 1.2 2001/05/08 03:05:21 marleyj
* Added synthetic variables SEC_STATUS, INFO_ACFREQ, INFO_UTILITY,
* SEC_CURRENT, INFO_LOADPCT, INFO_LOADPWR and INFO_OUTVOLT.
*
* Revision 1.1 2001/05/02 04:54:19 marleyj
* Initial revision
*/
#define SEC_DRIVER_VERSION "$Revision: 1.4 $"
#include "main.h"
#include "serial.h"
#define SEC_LOG_DEBUG (1)
#define SEC_LOG_ERROR (1)
#define SEC_LOG_INFO (2)
#define SEC_LOG_LOWLEVEL (3)
#define SEC_FLAG_NORMAL (0 )
#define SEC_FLAG_ORIGINAL (1<<0)
#define SEC_FLAG_ENUM (1<<2)
#define SEC_FLAG_STRING (1<<3)
#define SEC_FLAG_NUMBER (1<<4)
#define SEC_FLAG_RD (1<<5)
#define SEC_FLAG_WR (1<<6)
#define SEC_FLAG_RW (SEC_FLAG_RD|SEC_FLAG_WR)
/* extra */
/* ------ */
#define SEC_STR_STATUS "ups.status"
#define SEC_STR_INPUT_FREQUENCY "input.frequency"
#define SEC_STR_INPUT_VOLTAGE "input.voltage"
#define SEC_STR_OUTPUT_CURRENT "output.current"
#define SEC_STR_OUTPUT_LOAD "output.load"
#define SEC_STR_OUTPUT_POWER "output.power"
#define SEC_STR_OUTPUT_VOLTAGE "output.voltage"
#define SEC_SIZE (256)
#include "sec.h"
/* Some baud rates for setup_serial() */
#define SEC_NUMBAUDS 5
static struct baud_rate_t baud_rates[SEC_NUMBAUDS] = {
{ B1200, 1200 },
{ B2400, 2400 },
{ B4800, 4800 },
{ B9600, 9600 },
{ B19200, 19200 },
};
/* Exhaustive information for each possible variable */
static const struct sec_varlist_t sec_varlist[SEC_NUMVARS] = {
{ SEC_UNUSED, "", 0.0, "", 0, 0, 0 },
/* infotag name unit cmd field size flags */
{ "ups.alarm", "Alarm Awaiting Power", 1.0, SEC_CMD_ALARMSTAT, 13, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Bypass Bad", 1.0, SEC_CMD_ALARMSTAT, 5, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Charger Failure", 1.0, SEC_CMD_ALARMSTAT, 8, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Fan Failure", 1.0, SEC_CMD_ALARMSTAT, 10, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Fuse Failure", 1.0, SEC_CMD_ALARMSTAT, 11, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm General Fault", 1.0, SEC_CMD_ALARMSTAT, 12, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Input Bad", 1.0, SEC_CMD_ALARMSTAT, 2, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Output Bad", 1.0, SEC_CMD_ALARMSTAT, 3, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Output Off", 1.0, SEC_CMD_ALARMSTAT, 6, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Overload", 1.0, SEC_CMD_ALARMSTAT, 4, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Shutdown Imminent", 1.0, SEC_CMD_ALARMSTAT, 15, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Shutdown Pending", 1.0, SEC_CMD_ALARMSTAT, 14, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm System Off", 1.0, SEC_CMD_ALARMSTAT, 9, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm Temperature", 1.0, SEC_CMD_ALARMSTAT, 1, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.alarm", "Alarm UPS Shutdown", 1.0, SEC_CMD_ALARMSTAT, 7, 2, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.beeper.status", "Audible Alarm", 1.0, SEC_CMD_NOMINAL, 8, 4, SEC_FLAG_NORMAL |SEC_FLAG_ENUM |SEC_FLAG_RW },
{ "ups.autorestart", "Auto Restart", 1.0, SEC_CMD_AUTORESTART, 1, 2, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RW },
{ "battery.charge.state", "Battery Charge", 1.0, SEC_CMD_BATTSTAT, 3, 4, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "battery.condition", "Battery Condition", 1.0, SEC_CMD_BATTSTAT, 1, 3, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "battery.current", "Battery Current", 0.1, SEC_CMD_BATTSTAT, 8, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "battery.date", "Battery Installed", 1.0, SEC_CMD_NOMINAL, 11, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RW },
{ "battery.status", "Battery Status", 1.0, SEC_CMD_BATTSTAT, 2, 3, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "battery.temperature", "Battery Temperature", 1.0, SEC_CMD_BATTSTAT, 9, 99, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "battery.voltage", "Battery Voltage", 0.1, SEC_CMD_BATTSTAT, 7, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.current", "Bypass Current 1", 0.1, SEC_CMD_BYPASSSTAT, 4, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.current", "Bypass Current 2", 0.1, SEC_CMD_BYPASSSTAT, 7, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.current", "Bypass Current 3", 0.1, SEC_CMD_BYPASSSTAT, 10, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.frequency", "Bypass Frequency", 0.1, SEC_CMD_BYPASSSTAT, 1, 999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.lines", "Bypass Num Lines", 1.0, SEC_CMD_BYPASSSTAT, 2, 9, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.power", "Bypass Power 1", 1.0, SEC_CMD_BYPASSSTAT, 5, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.power", "Bypass Power 2", 1.0, SEC_CMD_BYPASSSTAT, 8, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.power", "Bypass Power 3", 1.0, SEC_CMD_BYPASSSTAT, 11, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.voltage", "Bypass Voltage 1", 0.1, SEC_CMD_BYPASSSTAT, 3, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.voltage", "Bypass Voltage 2", 0.1, SEC_CMD_BYPASSSTAT, 6, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "bypass.voltage", "Bypass Voltage 3", 0.1, SEC_CMD_BYPASSSTAT, 9, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "battery.charge", "Estimated Charge", 1.0, SEC_CMD_BATTSTAT, 6, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "battery.runtime", "Estimated Minutes", 60.0, SEC_CMD_BATTSTAT, 5, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.transfer.high", "High Volt Xfer Pt", 1.0, SEC_CMD_NOMINAL, 10, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "ups.id", "Identification", 1.0, SEC_CMD_UPSID, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RW },
{ "input.current", "Input Current 1", 0.1, SEC_CMD_INPUTSTAT, 5, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.current", "Input Current 2", 0.1, SEC_CMD_INPUTSTAT, 9, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.current", "Input Current 3", 0.1, SEC_CMD_INPUTSTAT, 13, 9999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.frequency", "Input Frequency 1", 0.1, SEC_CMD_INPUTSTAT, 3, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.frequency", "Input Frequency 2", 0.1, SEC_CMD_INPUTSTAT, 7, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.frequency", "Input Frequency 3", 0.1, SEC_CMD_INPUTSTAT, 11, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.line_bads", "Input Line Bads", 1.0, SEC_CMD_INPUTSTAT, 1, 999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.lines", "Input Num Lines", 1.0, SEC_CMD_INPUTSTAT, 2, 9, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.power", "Input Power 1", 1.0, SEC_CMD_INPUTSTAT, 6, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.power", "Input Power 2", 1.0, SEC_CMD_INPUTSTAT, 10, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.power", "Input Power 3", 1.0, SEC_CMD_INPUTSTAT, 14, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.voltage", "Input Voltage 1", 0.1, SEC_CMD_INPUTSTAT, 4, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.voltage", "Input Voltage 2", 0.1, SEC_CMD_INPUTSTAT, 8, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.voltage", "Input Voltage 3", 0.1, SEC_CMD_INPUTSTAT, 12, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "input.transfer.low", "Low Volt Xfer Pt", 1.0, SEC_CMD_NOMINAL, 9, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "ups.mfr", "Manufacturer", 1.0, SEC_CMD_MFR, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RD },
{ "ups.model", "Model", 1.0, SEC_CMD_MOD, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RD },
{ "battery.life.nominal", "Nominal Battery Life", 1.0, SEC_CMD_NOMINAL, 12, 16, SEC_FLAG_ORIGINAL|SEC_FLAG_STRING|SEC_FLAG_RW },
{ "input.frequency.nominal", "Nominal Input Frequency", 0.1, SEC_CMD_NOMINAL, 2, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "input.voltage.nominal", "Nominal Input Voltage", 1.0, SEC_CMD_NOMINAL, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "battery.runtime.low", "Nominal Low Battery Time",60.0, SEC_CMD_NOMINAL, 7, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RW },
{ "output.frequency.nominal","Nominal Output Frequency", 0.1, SEC_CMD_NOMINAL, 4, 999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "ups.power.nominal", "Nominal Output Power", 1.0, SEC_CMD_NOMINAL, 6, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RW },
{ "output.voltage.nominal", "Nominal Output Voltage", 1.0, SEC_CMD_NOMINAL, 3, 16, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "ups.power.nominal", "Nominal VA Rating", 1.0, SEC_CMD_NOMINAL, 5, 16, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RW },
{ "output.current", "Output Current 1", 0.1, SEC_CMD_OUTPUTSTAT, 5, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.current", "Output Current 2", 0.1, SEC_CMD_OUTPUTSTAT, 9, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.current", "Output Current 3", 0.1, SEC_CMD_OUTPUTSTAT, 13, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.frequency", "Output Frequency", 0.1, SEC_CMD_OUTPUTSTAT, 2, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "ups.load", "Output Load 1", 1.0, SEC_CMD_OUTPUTSTAT, 7, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "ups.load", "Output Load 2", 1.0, SEC_CMD_OUTPUTSTAT, 11, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "ups.load", "Output Load 3", 1.0, SEC_CMD_OUTPUTSTAT, 15, 999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.lines", "Output Num Lines", 1.0, SEC_CMD_OUTPUTSTAT, 3, 9, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.power", "Output Power 1", 1.0, SEC_CMD_OUTPUTSTAT, 6, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.power", "Output Power 2", 1.0, SEC_CMD_OUTPUTSTAT, 10, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.power", "Output Power 3", 1.0, SEC_CMD_OUTPUTSTAT, 14, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.source", "Output Source", 1.0, SEC_CMD_OUTPUTSTAT, 1, 6, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "output.voltage", "Output Voltage 1", 0.1, SEC_CMD_OUTPUTSTAT, 4, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.voltage", "Output Voltage 2", 0.1, SEC_CMD_OUTPUTSTAT, 8, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "output.voltage", "Output Voltage 3", 0.1, SEC_CMD_OUTPUTSTAT, 12, 9999, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "ups.delay.reboot", "Reboot With Duration", 1.0, SEC_CMD_REBOOT, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_WR },
{ "battery.ontime", "Seconds on Battery", 1.0, SEC_CMD_BATTSTAT, 4, 99999, SEC_FLAG_ORIGINAL|SEC_FLAG_NUMBER|SEC_FLAG_RD },
{ "ups.shutdown.type", "Shutdown Type", 1.0, SEC_CMD_SHUTTYPE, 1, 2, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RW },
{ "ups.delay.shutdown", "Shutdown After Delay", 1.0, SEC_CMD_SHUTDOWN, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_WR },
{ "ups.firmware", "Software Version", 1.0, SEC_CMD_VERSION, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RD },
{ "ups.delay.start", "Startup After Delay", 1.0, SEC_CMD_STARTDELAY, 1, 16, SEC_FLAG_NORMAL |SEC_FLAG_NUMBER|SEC_FLAG_WR },
{ "ups.test.result", "Test Results Detail", 1.0, SEC_CMD_TESTRESULT, 2, 16, SEC_FLAG_NORMAL |SEC_FLAG_STRING|SEC_FLAG_RD },
{ "ups.test.result.summary", "Test Results Summary", 1.0, SEC_CMD_TESTRESULT, 1, 6, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RD },
{ "ups.test.type", "Test Type", 1.0, SEC_CMD_TEST, 1, 5, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_WR },
{ "ups.baudrate", "Baud Rate", 1.0, SEC_CMD_BAUDRATE, 1, 19200, SEC_FLAG_ORIGINAL|SEC_FLAG_ENUM |SEC_FLAG_RW },
};
/* List of possible enumerations */
static const struct sec_enumdata_t sec_enumdata[] = {
{ SEC_ALARM_PWRWAIT, 0, "Not Awaiting Power" },
{ SEC_ALARM_PWRWAIT, 1, "Awaiting Power" },
{ SEC_ALARM_BADBYPASS, 0, "Bypass OK" },
{ SEC_ALARM_BADBYPASS, 1, "Bypass Fault" },
{ SEC_ALARM_CHARGFAIL, 0, "Charger OK" },
{ SEC_ALARM_CHARGFAIL, 1, "Charger Fault" },
{ SEC_ALARM_FANFAIL, 0, "Fan OK" },
{ SEC_ALARM_FANFAIL, 1, "Fan Fault" },
{ SEC_ALARM_FUSEFAIL, 0, "Fuse OK" },
{ SEC_ALARM_FUSEFAIL, 1, "Fuse Fault" },
{ SEC_ALARM_GENERAL, 0, "General system OK" },
{ SEC_ALARM_GENERAL, 1, "General system Fault" },
{ SEC_ALARM_BADINPUT, 0, "Input OK" },
{ SEC_ALARM_BADINPUT, 1, "Input Fault" },
{ SEC_ALARM_BADOUTPUT, 0, "Output OK" },
{ SEC_ALARM_BADOUTPUT, 1, "Output Fault" },
{ SEC_ALARM_OUTPUTOFF, 0, "Output On" },
{ SEC_ALARM_OUTPUTOFF, 1, "Output off" },
{ SEC_ALARM_OVERLOAD, 0, "UPS not overloaded" },
{ SEC_ALARM_OVERLOAD, 1, "UPS Overloaded" },
{ SEC_ALARM_IMMSHUT, 0, "No Shutdown Imminent" },
{ SEC_ALARM_IMMSHUT, 1, "Shutdown Imminent" },
{ SEC_ALARM_PENDSHUT, 0, "No Shutdown Pending" },
{ SEC_ALARM_PENDSHUT, 1, "Shutdown Pending" },
{ SEC_ALARM_SYSOFF, 0, "System On" },
{ SEC_ALARM_SYSOFF, 1, "System off" },
{ SEC_ALARM_TEMP, 0, "Temperature OK" },
{ SEC_ALARM_TEMP, 1, "Over Temperature" },
{ SEC_ALARM_UPSSHUT, 0, "UPS not Shutdown" },
{ SEC_ALARM_UPSSHUT, 1, "UPS Shutdown" },
{ SEC_AUDIBLE_ALARM, 0, "Disabled" },
{ SEC_AUDIBLE_ALARM, 1, "Enabled" },
{ SEC_AUDIBLE_ALARM, 2, "Muted" },
{ SEC_AUDIBLE_ALARM, 3, "Low Battery" },
{ SEC_AUTORESTART, 1, "Automatic Restart" },
{ SEC_AUTORESTART, 2, "Manual Restart" },
{ SEC_BATTERY_CHARGE, 0, "Floating" },
{ SEC_BATTERY_CHARGE, 1, "Charging" },
{ SEC_BATTERY_CHARGE, 2, "Resting" },
{ SEC_BATTERY_CHARGE, 3, "Discharging" },
{ SEC_BATTERY_COND, 0, "Good" },
{ SEC_BATTERY_COND, 1, "Weak" },
{ SEC_BATTERY_COND, 2, "Replace" },
{ SEC_BATTERY_STATUS, 0, "Battery OK" },
{ SEC_BATTERY_STATUS, 1, "Battery Low" },
{ SEC_BATTERY_STATUS, 2, "Battery Depleted" },
{ SEC_OUTPUT_SOURCE, 0, "Normal" },
{ SEC_OUTPUT_SOURCE, 1, "On Battery" },
{ SEC_OUTPUT_SOURCE, 2, "On Bypass" },
{ SEC_OUTPUT_SOURCE, 3, "Reducing" },
{ SEC_OUTPUT_SOURCE, 4, "Boosting" },
{ SEC_OUTPUT_SOURCE, 5, "Other" },
{ SEC_SHUTDOWN_TYPE, 1, "UPS output only" },
{ SEC_SHUTDOWN_TYPE, 2, "Entire UPS" },
{ SEC_TEST_RESULTS_SUMMARY, 0, "No test performed" },
{ SEC_TEST_RESULTS_SUMMARY, 1, "Test Passed" },
{ SEC_TEST_RESULTS_SUMMARY, 2, "Test in progress" },
{ SEC_TEST_RESULTS_SUMMARY, 3, "General Test failed" },
{ SEC_TEST_RESULTS_SUMMARY, 4, "Battery Test failed" },
{ SEC_TEST_RESULTS_SUMMARY, 5, "Deep Battery Test failed" },
{ SEC_TEST_TYPE, 0, "No test" },
{ SEC_TEST_TYPE, 1, "General Systems Test" },
{ SEC_TEST_TYPE, 2, "Quick Battery Test" },
{ SEC_TEST_TYPE, 3, "Deep Battery Calibration" },
{ SEC_BAUD_RATE, 0, "1200 Baud" },
{ SEC_BAUD_RATE, 1, "2400 Baud" },
{ SEC_BAUD_RATE, 2, "4800 Baud" },
{ SEC_BAUD_RATE, 3, "9600 Baud" },
{ SEC_BAUD_RATE, 4, "19200 Baud" },
{ SEC_UNUSED, 0 , 0 }
};
#define sec_support(i) (_sec_meta[(i)%SEC_NUMVARS].supported)
#define sec_edi(i) (_sec_meta[(i)%SEC_NUMVARS].edi)
#define sec_value(i) (_sec_meta[(i)%SEC_NUMVARS].value)
struct _sec_meta {
int supported;
int edi; /* enumeratated data index */
char value[SEC_MAX_VARSIZE];
};
static struct _sec_meta _sec_meta[SEC_NUMVARS] = { { 0, 0 } };
/*
* sec_recv(fd, buf, len)
*
* Read a SEC format message (^<cmdchar><len><data>) from the UPS
* and store in <data> in buf.
*
* Return -2 if failed to read valid response
* Return -1 if command failed (^0)
* Return 0 if command succeeded (^1)
* Return len if data returned
*
*/
static int
sec_recv(int fd, char *buf, int len)
{
char *p;
int i, l, r;
errno = EINVAL;
upsdebugx(SEC_LOG_LOWLEVEL, "sec_recv...");
if (len < 4) { /* min length */
upsdebugx(SEC_LOG_LOWLEVEL, " invalid buffer length %d", len);
return -2;
}
/* look for the startchar */
*buf = '\0';
for (i=0; i<30 && (*buf != SEC_MSG_STARTCHAR); i++) { /* up to 30 characters, OR 3 seconds */
if (ser_get_char(fd, buf, 0, 100000)<0) /* failed to read */
*buf = '\0';
}
if (*buf != SEC_MSG_STARTCHAR) {
upsdebugx(SEC_LOG_LOWLEVEL, " FAILED to find start char %c",SEC_MSG_STARTCHAR);
return -2;
}
/* we found a msg, which one? */
upsdebugx(SEC_LOG_LOWLEVEL, " found start char...");
if (ser_get_char(fd, buf, 0, 100000)<0) { /* failed to read next char */
upsdebugx(SEC_LOG_LOWLEVEL, " FAILED to receive type char");
return -2;
}
r = -2;
switch (*buf) {
case SEC_DATAMSG:
upsdebugx(SEC_LOG_LOWLEVEL, " got a %c!", SEC_DATAMSG);
/* data being returned - get the length */
if ((r=ser_get_buf_len(fd, buf, 3, 1, 0)) < 3) {
upsdebugx(SEC_LOG_LOWLEVEL, " OOPS only %d of 3 chars read (%c)",r,*buf);
return -2;
}
*(buf+3) = '\0';
p = buf;
l = strtol(buf, &p, 10);
if ((p == buf) || (*p != '\0')) { /* invalid number */
upsdebugx(SEC_LOG_LOWLEVEL, " OOPS invalid length: (%s)", buf);
return -2;
}
if (l >= len) {
upsdebugx(SEC_LOG_LOWLEVEL, " OOPS not enough buffer space");
return -2;
}
upsdebugx(SEC_LOG_LOWLEVEL, " UPS returning %d bytes of data...", l);
if ((r=ser_get_buf_len(fd, buf, l, 0, 500000)) != l) {
upsdebugx(SEC_LOG_LOWLEVEL, " OOPS less than %d chars read (%d)", l, r);
r = -2;
} else
upsdebugx(SEC_LOG_LOWLEVEL, " OK, read %d bytes of data",l);
break;
case '0':
upsdebugx(SEC_LOG_LOWLEVEL, "UPS returned: command failed");
r = -1;
break;
case '1':
upsdebugx(SEC_LOG_LOWLEVEL, "UPS returned: command succeeded!");
r = 0;
break;
default:
r = -2;
break;
}
return r;
}
/*
* sec_send(fd, mode, command, buffer, length)
*
* Sends mode command to the UPS with the given data.
*
* Returns -1 if command fails
* 0 if command succeeds, but returns no data
* length of returned data if data is returned
*/
static int
sec_send(int fd, const char mode, const char *command, char *buf, int len)
{
char msg[SEC_SIZE];
int ret;
errno = EINVAL;
/* create the message string */
if (len > 0) {
snprintf(msg, SEC_SIZE, "%c%c%03d%s%s", SEC_MSG_STARTCHAR,
mode, len+3, command, buf);
} else {
snprintf(msg, SEC_SIZE, "%c%c003%s", SEC_MSG_STARTCHAR,
mode, command);
}
upsdebugx(SEC_LOG_LOWLEVEL, "PC-->UPS: \"%s\"", msg);
ret = ser_send_buf(fd, msg, strlen(msg));
upsdebugx(SEC_LOG_LOWLEVEL, " send returned: %d",ret);
return ret;
}
/* set up the serial connection */
static int
sec_setup_serial(const char *port)
{
char tmp[SEC_SIZE];
int i, ret, fd;
/* The SEC protocol alows for different baud rates. My daddy told me
"Never assume ...", so we try the different rates to see which works. */
fd = ser_open(port);
for (i=0; i<SEC_NUMBAUDS; i++) {
upsdebugx(SEC_LOG_LOWLEVEL, "Trying to connect at %d baud",baud_rates[i].name);
ser_set_speed(fd, port, baud_rates[i].rate);
ser_flush_in(fd, "", 0); /* drain input */
upsdebugx(SEC_LOG_LOWLEVEL, " sending probing command...");
sec_send(fd, SEC_POLLCMD, SEC_CMD_MFR, tmp, 0);
upsdebugx(SEC_LOG_LOWLEVEL, " reading reply...");
ret = sec_recv(fd, tmp, SEC_SIZE);
if (ret >= -1) /* valid reply */
break;
upsdebugx(SEC_LOG_LOWLEVEL, " no connection.");
}
if (i == 5) {
printf("Can't talk to UPS on port %s!\n",port);
printf("Check the cabling and portname and try again\n");
exit (1);
}
return fd;
}
/*
* addquery(cmd, field, varnum)
*
* Records that sec variable <varnum> is supported by this UPS and should be
* queried as part of the regular update. We need to record which command
* (cmd) to send, and which <field> corresponds to the variable.
*/
static void
sec_addquery(char *cmd, int field, int varnum)
{
int q;
for (q=0; q<SEC_QUERYLIST_LEN; q++) {
if (sec_querylist[q].command == NULL) {
/* command has not been recorded yet */
sec_querylist[q].command = cmd;
upsdebugx(SEC_LOG_LOWLEVEL, " Query %d is %s",q,cmd);
}
if (sec_querylist[q].command == cmd) {
sec_querylist[q].varnum[field-1] = varnum;
upsdebugx(SEC_LOG_LOWLEVEL, " Querying varnum %d",varnum);
break;
}
}
}
/*
* sec_setinfo(varnum, value)
*
* Update variable number <varnum> to value <value> in the info array.
*/
static void
sec_setinfo(int varnum, char *value)
{
char *s, buf[SEC_SIZE];
int i, e;
double d;
if (dstate_getinfo(sec_varlist[varnum].infotag) == 0) /* nothing to do */
return;
s = value;
if (sec_varlist[varnum].flags & SEC_FLAG_ENUM) {
i = strtol(value, &s, 10);
if ((s == value) || (*s != '\0')) { /* some problem */
upsdebugx(SEC_LOG_ERROR, "Error parsing enum for %s (%s)", sec_varlist[varnum].name, value);
return;
}
for (e=sec_edi(varnum); sec_enumdata[e].type == varnum; e++) {
if (sec_enumdata[e].index == i)
break;
}
if (sec_enumdata[e].type == varnum) { /* out of bounds otherwise */
snprintf(buf, SEC_SIZE, "\"%s\" (ENUM)", sec_enumdata[e].value);
dstate_setinfo(sec_varlist[varnum].infotag, "%s", sec_enumdata[e].value);
} else
snprintf(buf, SEC_SIZE, "\"%d\" (Invalid ENUM)", i);
} else if (sec_varlist[varnum].flags & SEC_FLAG_STRING) {
snprintf(buf, SEC_SIZE, "\"%s\" (STRING)", s);
dstate_setinfo(sec_varlist[varnum].infotag, "%s", s);
} else if (sec_varlist[varnum].flags & SEC_FLAG_NUMBER) {
if (sec_varlist[varnum].unit == 1.0) { /* integer */
i = strtol(value, &s, 10);
if ((s == value) || (*s != '\0')) { /* some problem */
upsdebugx(SEC_LOG_ERROR, "Error parsing number for %s (%s)", sec_varlist[varnum].name, value);
return;
}
snprintf(buf, SEC_SIZE, "\"%d\" (Integer)", i);
dstate_setinfo(sec_varlist[varnum].infotag, "%d", i);
} else {
d = strtod(value, &s);
if ((s == value) || (*s != '\0')) { /* some problem */
upsdebugx(SEC_LOG_ERROR, "Error parsing number for %s (%s)", sec_varlist[varnum].name, value);
return;
}
d = d * sec_varlist[varnum].unit;
snprintf(buf, SEC_SIZE, "\"%.1f\" (Float)", d);
dstate_setinfo(sec_varlist[varnum].infotag, "%.1f", d);
}
}
upsdebugx(SEC_LOG_INFO, "Updating variable %d (%s), new value is %s",
varnum, sec_varlist[varnum].name, buf);
}
/*
* sec_update_pseudovars
*
* There are a number of non-SEC variables that are functions of the real
* variables. We update them here.
*
* OFF - UPS is off - SEC_ALARM_OUTPUTOFF is "Output off"
* OL - UPS is online - SEC_OUTPUT_SOURCE is "Normal"
* OB - UPS is on battery - SEC_OUTPUT_SOURCE is "On Battery"
* BY - UPS is on bypass - SEC_OUTPUT_SOURCE is "On Bypass"
*
* OVER - UPS is overloaded - SEC_ALARM_OVERLOAD is "UPS Overloaded"
* LB - UPS battery is low - SEC_BATTERY_STATUS is "Battery Low"
* RB - UPS replace battery - SEC_BATTERY_COND is "Replace"
*
* TRIM - UPS is trimming - SEC_OUTPUT_SOURCE is "Reducing"
* BOOST - UPS is boosting - SEC_OUTPUT_SOURCE is "Boosting"
*
* FSD - UPS is shutdown - SEC_ALARM_SYSTEM_OFF is "System off"
*/
static void
sec_update_pseudovars( void )
{
const char *v, *s;
int n;
float fsum;
s = 0;
if (sec_support(SEC_ALARM_OUTPUTOFF)) {
v = sec_value(SEC_ALARM_OUTPUTOFF);
if (strcmp("Output off", v) == 0)
s = "OFF";
}
if (sec_support(SEC_OUTPUT_SOURCE)) {
v = sec_value(SEC_OUTPUT_SOURCE);
/* enum value */
if (strcmp("0", v) == 0) /* Normal */
s = "OL";
if (strcmp("1", v) == 0) /* On Battery */
s = "OB";
if (strcmp("2", v) == 0) /* On Bypass */
s = "BYPASS";
if (strcmp("3", v) == 0) /* Reducing */
s = "TRIM";
if (strcmp("4", v) == 0) /* Boosting */
s = "BOOST";
}
if (sec_support(SEC_ALARM_OVERLOAD)) {
v = sec_value(SEC_ALARM_OVERLOAD);
if (strcmp("1", v) == 0) /* Alarm Active */
s = "OVER";
}
if (sec_support(SEC_BATTERY_STATUS)) {
v = sec_value(SEC_BATTERY_STATUS);
if (strcmp("1", v) == 0) /* Low */
s = "LB";
}
if (sec_support(SEC_BATTERY_COND)) {
v = sec_value(SEC_BATTERY_COND);
if (strcmp("2", v) == 0) /* replace */
s = "RB";
}
if (sec_support(SEC_ALARM_SYSOFF)) {
v = sec_value(SEC_ALARM_SYSOFF);
if (strcmp("1", v) == 0) /* Alarm Active */
s = "OFF";
}
if (s) {
upsdebugx(SEC_LOG_INFO, "Synthesizing status (%s)", s);
status_init();
status_set(s);
status_commit();
}
upsdebugx(SEC_LOG_INFO, "Synthesizing averages...");
/* Average stats */
fsum = 0.0;
n = 0;
if (sec_support(SEC_INPUT_FREQUENCY_1) && ((v=dstate_getinfo(sec_varlist[SEC_INPUT_FREQUENCY_1].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_INPUT_FREQUENCY_2) && ((v=dstate_getinfo(sec_varlist[SEC_INPUT_FREQUENCY_2].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_INPUT_FREQUENCY_3) && ((v=dstate_getinfo(sec_varlist[SEC_INPUT_FREQUENCY_3].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (n)
dstate_setinfo(SEC_STR_INPUT_FREQUENCY, "%.1f", fsum / n);
fsum = 0.0;
n = 0;
if (sec_support(SEC_INPUT_VOLTAGE_1) && ((v=dstate_getinfo(sec_varlist[SEC_INPUT_VOLTAGE_1].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_INPUT_VOLTAGE_2) && ((v=dstate_getinfo(sec_varlist[SEC_INPUT_VOLTAGE_2].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_INPUT_VOLTAGE_3) && ((v=dstate_getinfo(sec_varlist[SEC_INPUT_VOLTAGE_3].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (n)
dstate_setinfo(SEC_STR_INPUT_VOLTAGE, "%.1f", fsum / n);
fsum = 0.0;
n = 0;
if (sec_support(SEC_OUTPUT_CURRENT_1) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_CURRENT_1].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_CURRENT_2) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_CURRENT_2].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_CURRENT_3) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_CURRENT_3].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (n)
dstate_setinfo(SEC_STR_OUTPUT_CURRENT, "%.1f", fsum / n);
fsum = 0.0;
n = 0;
if (sec_support(SEC_OUTPUT_LOAD_1) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_LOAD_1].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_LOAD_2) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_LOAD_2].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_LOAD_3) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_LOAD_3].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (n)
dstate_setinfo(SEC_STR_OUTPUT_LOAD, "%.1f", fsum / n);
fsum = 0.0;
n = 0;
if (sec_support(SEC_OUTPUT_POWER_1) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_POWER_1].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_POWER_2) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_POWER_2].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_POWER_3) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_POWER_3].infotag)) != 0)) {
fsum += strtod(v,0);
n++;
}
if (n)
dstate_setinfo(SEC_STR_OUTPUT_POWER, "%.1f", fsum / n);
fsum = 0.0;
n = 0;
if (sec_support(SEC_OUTPUT_VOLTAGE_1) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_VOLTAGE_1].infotag))!=0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_VOLTAGE_2) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_VOLTAGE_2].infotag))!=0)) {
fsum += strtod(v,0);
n++;
}
if (sec_support(SEC_OUTPUT_VOLTAGE_3) && ((v=dstate_getinfo(sec_varlist[SEC_OUTPUT_VOLTAGE_3].infotag))!=0)) {
fsum += strtod(v,0);
n++;
}
if (n)
dstate_setinfo(SEC_STR_OUTPUT_VOLTAGE, "%.1f", fsum / n);
}
static int
sec_setvar(const char *name, const char *val)
{
int i, e;
char *p, buf[SEC_SIZE];
for (i=1; i<SEC_NUMVARS; i++) {
if (strcasecmp(name, sec_varlist[i].infotag) == 0) { /* found entry */
p = buf;
for (e=1; e<sec_varlist[i].field; e++)
*(p++) = ',';
*p = '\0';
if (sec_varlist[i].flags&SEC_FLAG_ENUM) { /* enumerated */
for (e = sec_edi(i); sec_enumdata[e].type == i; e++) {
if (strcasecmp(val, sec_enumdata[e].value) == 0) { /* a match */
snprintf(p, SEC_SIZE-1-(p-buf), "%d", sec_enumdata[e].index);
break;
}
}
if (sec_enumdata[e].type != i)
return STAT_SET_UNKNOWN;
} else /* SEC_FLAG_STRING|SEC_FLAG_NUMBER */
strncpy(p, val, SEC_SIZE-1-(p-buf));
*(buf+SEC_SIZE-1) = '\0';
/* set the command .. in buf */
if (sec_support(i)) {
if (sec_varlist[i].flags & SEC_FLAG_WR) { /* can be stored, will return on next poll */
upsdebugx(SEC_LOG_INFO, "setting: %s:%s -> %s", name, val, buf);
sec_send(upsfd, SEC_SETCMD, sec_varlist[i].cmd, buf, strlen(buf));
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_SET_UNKNOWN;
} else {
strncpy(sec_value(i), val, SEC_MAX_VARSIZE-1);
*(sec_value(i)+SEC_MAX_VARSIZE-1) = '\0';
}
}
return STAT_SET_HANDLED;
}
}
return STAT_SET_UNKNOWN;
}
static int
sec_getnum(const char *value)
{
char *p;
int i;
i = 0;
if (value) {
p = (char*)value;
i = strtol(value, &p, 10); /* convert */
if ((p==value) || (*p)) /* invalid number */
i = 0; /* default */
}
return i;
}
static int
sec_instcmd(const char *cmd, const char *extra)
{
char *p, buf[SEC_SIZE];
int i;
if (strcasecmp(cmd, "load.off") == 0) {
/* send SDA:0, PSD:1 */
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_TYPE].cmd, "0", 1); /* shutdown type: UPS output */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_AFTER_DELAY].cmd, "0", 1); /* shutdown now */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
return STAT_INSTCMD_HANDLED;
}
if (strcasecmp(cmd, "load.on") == 0) {
/* send SDA:0, STD:1 */
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_TYPE].cmd, "0", 1); /* shutdown type: UPS output */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_STARTUP_AFTER_DELAY].cmd, "0", 1); /* startup now */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
return STAT_INSTCMD_HANDLED;
}
if (strcasecmp(cmd, "shutdown.return") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_TYPE].cmd, "1", 1); /* shutdown type: UPS system */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_AUTORESTART].cmd, "1", 1); /* automatic restart */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
snprintf(buf, SEC_SIZE, "%d", sec_getnum(sec_value(SEC_SHUTDOWN_AFTER_DELAY)));
*(buf+SEC_SIZE-1) = '\0';
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_AFTER_DELAY].cmd, buf, strlen(buf)); /* shutdown ... */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
return STAT_INSTCMD_HANDLED;
}
if (strcasecmp(cmd, "shutdown.reboot") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_TYPE].cmd, "1", 1); /* shutdown type: UPS system */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
snprintf(buf, SEC_SIZE, "%d", sec_getnum(sec_value(SEC_REBOOT_WITH_DURATION)));
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_REBOOT_WITH_DURATION].cmd, buf, strlen(buf)); /* reboot now */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "shutdown.stayoff") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_TYPE].cmd, "1", 1); /* shutdown type: UPS system */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_AUTORESTART].cmd, "0", 1); /* manual restart */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
snprintf(buf, SEC_SIZE, "%d", sec_getnum(sec_value(SEC_SHUTDOWN_AFTER_DELAY)));
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_AFTER_DELAY].cmd, buf, strlen(buf)); /* shutdown now */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "shutdown.stop") == 0) { /* does this matter which reboot/shutdown */
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_REBOOT_WITH_DURATION].cmd, "-1", 2); /* abort reboot */
i = sec_recv(upsfd, buf, SEC_SIZE); /* not succeeded? */
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_SHUTDOWN_AFTER_DELAY].cmd, "-1", 2); /* abort shutdown */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "test.panel.start") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_TEST_TYPE].cmd, "0", 1); /* general test */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "test.panel.stop") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_TEST_TYPE].cmd, "-1", 2); /* abort */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "test.battery.start") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_TEST_TYPE].cmd, "1", 1); /* battery test */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "test.battery.start.deep") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_TEST_TYPE].cmd, "2", 1); /* deep battery test */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "test.battery.stop") == 0) {
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_TEST_TYPE].cmd, "-1", 2); /* abort */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "beeper.on") == 0) {
p = buf;
for (i=0; i<sec_varlist[SEC_AUDIBLE_ALARM].field; i++)
*(p++) = ',';
*(p++) = '1';
*(p++) = '\0';
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_AUDIBLE_ALARM].cmd , buf, p-buf); /* AUDIBLE_ALARM_STATE: Enabled */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
return STAT_INSTCMD_UNKNOWN;
}
if (strcasecmp(cmd, "beeper.off") == 0) {
p = buf;
for (i=0; i<sec_varlist[SEC_AUDIBLE_ALARM].field; i++)
*(p++) = ',';
*(p++) = '0'; /* Disabled */
*(p++) = '\0';
sec_send(upsfd, SEC_SETCMD, sec_varlist[SEC_AUDIBLE_ALARM].cmd , buf, p-buf); /* AUDIBLE_ALARM_STATE: Disabled */
if (sec_recv(upsfd, buf, SEC_SIZE) != 0) /* not succeeded */
return STAT_INSTCMD_UNKNOWN;
return STAT_INSTCMD_UNKNOWN;
}
return STAT_INSTCMD_UNKNOWN;
}
/*-------------------------------------------------------------------------
*
* Here are the mandatory functions called from the shared main.c
*/
void
upsdrv_initinfo(void)
{
int l, e, f, v, err;
char *p, *q, buf[SEC_SIZE];
/* find out which variables/commands this UPS supports */
sec_send(upsfd, SEC_POLLCMD, SEC_CMD_AVAILP1, 0, 0);
l = sec_recv(upsfd, buf, SEC_SIZE);
p = buf + l;
if (l)
*p++ = ',';
sec_send(upsfd, SEC_POLLCMD, SEC_CMD_AVAILP2, 0, 0);
l = sec_recv(upsfd, p, SEC_SIZE-l);
*(p+l) = '\0';
if (strlen(buf) == 0)
fatalx("No available variables found!");
upsdebugx(SEC_LOG_INFO, "List of available vars: %s", buf);
/* scan list adding variables to info array */
p = buf;
while (*p) {
q = p;
v = strtol(p, &q, 10); /* variable number of supported variable */
/* check for errors */
err = (p == q);
err |= (*q && (*q != ','));
err |= !((v > 0) && (v < SEC_NUMVARS));
if (err) { /* an error occured */
upsdebugx(SEC_LOG_ERROR, "Invalid number received: %p (%02x)", p, *p);
for (p=q; *p && *p != ','; p++); /* next list element */
} else {
/* don't bother adding a write-only variable */
if (sec_varlist[v].infotag) {
sec_support(v) = 1;
if ((sec_varlist[v].flags&SEC_FLAG_RW) != SEC_FLAG_WR)
sec_addquery(sec_varlist[v].cmd, sec_varlist[v].field, v);
if ((sec_varlist[v].flags & SEC_FLAG_ORIGINAL) == 0) {
upsdebugx(SEC_LOG_INFO, "Adding variable %d (%s)",v,sec_varlist[v].name);
f = 0;
if ((sec_varlist[v].flags&SEC_FLAG_WR))
f |= ST_FLAG_RW;
if (sec_varlist[v].flags & (SEC_FLAG_STRING|SEC_FLAG_NUMBER))
f |= ST_FLAG_STRING;
if (sec_varlist[v].flags & SEC_FLAG_NUMBER)
dstate_setinfo(sec_varlist[v].infotag, "0");
else
dstate_setinfo(sec_varlist[v].infotag, "(nil)");
dstate_setflags(sec_varlist[v].infotag, f);
dstate_setaux(sec_varlist[v].infotag, sec_varlist[v].size);
/* find entries in enumdata for current variable */
if (sec_varlist[v].flags & SEC_FLAG_ENUM) {
e = 0;
while ((sec_enumdata[e].type != SEC_UNUSED) && (sec_enumdata[e].type != v))
e++;
if (sec_enumdata[e].type == SEC_UNUSED)
upsdebugx(SEC_LOG_ERROR, "Could not find ENUMs for (%d) %s",v,sec_varlist[v].name);
else
sec_edi(v) = e;
/* add entries for enumerated variable */
while (sec_enumdata[e].type == v) {
upsdebugx(SEC_LOG_INFO, " adding enumval \"%s\" (%d)",sec_enumdata[e].value,e);
dstate_addenum(sec_varlist[sec_enumdata[e].type].infotag, sec_enumdata[e].value);
e++;
}
}
}
}
}
for (p = q; *p == ','; p++); /* skip over ',' */
}
/* variables */
dstate_setinfo("driver.version.internal", "%s", SEC_DRIVER_VERSION);
/* -- commands -- */
if (sec_support(SEC_SHUTDOWN_TYPE)) {
if (sec_support(SEC_REBOOT_WITH_DURATION)) {
dstate_setinfo("ups.delay.reboot", "60");
dstate_addcmd("shutdown.reboot"); /* RWDN ... see also ups.delay.reboot */
dstate_addcmd("shutdown.stop"); /* RWD-1 */
if (sec_support(SEC_AUTORESTART))
dstate_addcmd("shutdown.return"); /* RWDN ... see also ups.delay.reboot */
}
if (sec_support(SEC_SHUTDOWN_AFTER_DELAY)) {
dstate_setinfo("ups.delay.shutdown", "20");
if (sec_support(SEC_AUTORESTART))
dstate_addcmd("shutdown.stayoff"); /* SDA1 PSD0 */
dstate_addcmd("load.off"); /* SDA0 PSD0 ... see also ups.delay.shutdown */
dstate_addcmd("shutdown.stop"); /* PSD-1 */
}
if (sec_support(SEC_STARTUP_AFTER_DELAY)) {
dstate_setinfo("ups.delay.start", "0");
dstate_addcmd("load.on"); /* SDA0 STD0 ... see also ups.delay.start */
}
}
if (sec_support(SEC_TEST_TYPE)) {
dstate_addcmd("test.panel.start"); /* TST0 */
dstate_addcmd("test.panel.stop"); /* TST-1 */
dstate_addcmd("test.battery.start"); /* TST1 */
/* dstate_addcmd("test.battery.start.deep"); */ /* TST2 */
dstate_addcmd("test.battery.stop"); /* TST-1 */
}
if (sec_support(SEC_AUDIBLE_ALARM)) {
dstate_addcmd("beeper.on");
dstate_addcmd("beeper.off");
}
upsh.instcmd = sec_instcmd;
upsh.setvar = sec_setvar;
}
/*
* upsdrv_updateinfo()
*
* For a SEC protocol UPS, we only need to query those variables that are
* supported.
*/
void
upsdrv_updateinfo(void)
{
char buf[128],*r,*n;
int ret, q, f;
upsdebugx(SEC_LOG_INFO, "--------Updating--------------------------");
/* loop over each query (a single SEC poll command)*/
for (q=0; q<SEC_QUERYLIST_LEN; q++) {
if (sec_querylist[q].command == NULL) break;
upsdebugx(SEC_LOG_INFO, "Polling %s...", sec_querylist[q].command);
sec_send(upsfd, SEC_POLLCMD, sec_querylist[q].command, 0, 0);
ret = sec_recv(upsfd, buf, 128);
if (ret <=0) {
upslog(LOG_WARNING, "Warning sending poll cmd \"%s\" to UPS (%d)",
sec_querylist[q].command, ret);
continue;
}
r = buf;
*(r+ret) = '\0';
for (f=0; f<SEC_MAXFIELDS; f++) {
n = strchr(r, ',');
if (n != NULL)
*n = '\0';
/* is field/variable supported? */
if (sqv(q,f) > 0) {
/* only update the value if it's changed */
if (strcmp(sec_value(sqv(q,f)), r) != 0) {
snprintf(sec_value(sqv(q,f)), SEC_MAX_VARSIZE, "%s", r);
sec_setinfo(sqv(q,f), r);
}
}
if (n == NULL) break;
r = n+1;
}
}
/* update the non-sec variables */
sec_update_pseudovars();
dstate_dataok();
}
void
upsdrv_shutdown(void)
{
char buf[16];
/* supposedly the serial port stuff is already set
* since a serial port was found a moment ago, I won't bother seeing if
* the UPS is found here, except for final shutdown command
* so cancel any running shutdowns, set auto restart when AC returns,
* set to shutdown entire UPS and actually shutdown the UPS.
* This UPS momentarily shuts off power to computer even when commercial
* power is available.
*/
sec_send(upsfd, SEC_SETCMD, SEC_CMD_SHUTDOWN, "-1", 2); /* cancel shutdown command */
sec_recv(upsfd, buf, 16);
sec_send(upsfd, SEC_SETCMD, SEC_CMD_AUTORESTART, "1", 1);
sec_recv(upsfd, buf, 16);
sec_send(upsfd, SEC_SETCMD, SEC_CMD_SHUTTYPE, "2", 1);
sec_recv(upsfd, buf, 16);
sec_send(upsfd, SEC_SETCMD, SEC_CMD_SHUTDOWN, "5", 1);
sec_recv(upsfd, buf, 16);
}
void
upsdrv_help(void)
{
}
void
upsdrv_makevartable(void)
{
/* allow '-x xyzzy' */
/* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */
/* allow '-x foo=<some value>' */
/* addvar(VAR_VALUE, "foo", "Override foo setting"); */
}
void
upsdrv_banner(void)
{
printf("Network UPS Tools (%s) - SEC protocol UPS driver %s\n", UPS_VERSION, SEC_DRIVER_VERSION);
experimental_driver = 1;
}
void
upsdrv_initups(void)
{
/* probe ups type */
upsfd = sec_setup_serial(device_path);
/* to get variables and flags from the command line, use this:
*
* first populate with upsdrv_buildvartable above, then...
*
* set flag foo : /bin/driver -x foo
* set variable 'cable' to '1234' : /bin/driver -x cable=1234
*
* to test flag foo in your code:
*
* if (testvar("foo"))
* do_something();
*
* to show the value of cable:
*
* if ((cable == getval("cable")))
* printf("cable is set to %s\n", cable);
* else
* printf("cable is not set!\n");
*
* don't use NULL pointers - test the return result first!
*/
/* the upsh handlers can't be done here, as they get initialized
* shortly after upsdrv_initups returns to main.
*/
/* don't try to detect the UPS here */
}
void upsdrv_cleanup(void)
{
ser_close(upsfd, device_path);
}
More information about the Nut-upsdev
mailing list