1 /* powercom-hid.c - subdriver to monitor PowerCOM USB/HID devices with NUT
2 *
3 * Copyright (C)
4 * 2003 - 2009 Arnaud Quette <ArnaudQuette@Eaton.com>
5 * 2005 - 2006 Peter Selinger <selinger@users.sourceforge.net>
6 * 2008 - 2009 Arjen de Korte <adkorte-guest@alioth.debian.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "main.h" /* for getval() */
24 #include "usbhid-ups.h"
25 #include "powercom-hid.h"
26 #include "usb-common.h"
27
28 #define POWERCOM_HID_VERSION "PowerCOM HID 0.5"
29 /* FIXME: experimental flag to be put in upsdrv_info */
30
31 /* PowerCOM */
32 #define POWERCOM_VENDORID 0x0d9f
33
34 /* USB IDs device table */
35 static usb_device_id_t powercom_usb_device_table[] = {
36 /* PowerCOM IMP - IMPERIAL Series */
37 { USB_DEVICE(POWERCOM_VENDORID, 0x00a2), NULL },
38 /* PowerCOM SKP - Smart KING Pro (all Smart series) */
39 { USB_DEVICE(POWERCOM_VENDORID, 0x00a3), NULL },
40 /* PowerCOM WOW */
41 { USB_DEVICE(POWERCOM_VENDORID, 0x00a4), NULL },
42 /* PowerCOM VGD - Vanguard */
43 { USB_DEVICE(POWERCOM_VENDORID, 0x00a5), NULL },
44 /* PowerCOM BNT - Black Knight Pro */
45 { USB_DEVICE(POWERCOM_VENDORID, 0x00a6), NULL },
46 /* PowerCOM Vanguard and BNT-xxxAP */
47 { USB_DEVICE(POWERCOM_VENDORID, 0x0004), NULL },
48 { USB_DEVICE(POWERCOM_VENDORID, 0x0001), NULL },
49
50 /* Terminating entry */
51 { 0, 0, NULL }
52 };
53
54 static char powercom_scratch_buf[32];
55
powercom_startup_fun(double value)56 static const char *powercom_startup_fun(double value)
57 {
58 uint16_t i = value;
59
60 snprintf(powercom_scratch_buf, sizeof(powercom_scratch_buf), "%d", 60 * (((i & 0x00FF) << 8) + (i >> 8)));
61 upsdebugx(3, "%s: value = %.0f, buf = %s", __func__, value, powercom_scratch_buf);
62
63 return powercom_scratch_buf;
64 }
65
powercom_startup_nuf(const char * value)66 static double powercom_startup_nuf(const char *value)
67 {
68 const char *s = dstate_getinfo("ups.delay.start");
69 uint16_t val, command;
70 int iv;
71
72 iv = atoi(value ? value : s) / 60;
73 if (iv < 0 || (intmax_t)iv > (intmax_t)UINT16_MAX) {
74 upsdebugx(0, "%s: value = %d is not in uint16_t range", __func__, iv);
75 return 0;
76 }
77
78 /* COMMENTME: What are we doing here, a byte-swap in the word? */
79 val = (uint16_t)iv;
80 command = (uint16_t)(val << 8);
81 command += (uint16_t)(val >> 8);
82 upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command);
83
84 return command;
85 }
86
87 static info_lkp_t powercom_startup_info[] = {
88 { 0, NULL, powercom_startup_fun, powercom_startup_nuf }
89 };
90
powercom_shutdown_fun(double value)91 static const char *powercom_shutdown_fun(double value)
92 {
93 uint16_t i = value;
94
95 snprintf(powercom_scratch_buf, sizeof(powercom_scratch_buf), "%d", 60 * (i & 0x00FF) + (i >> 8));
96 upsdebugx(3, "%s: value = %.0f, buf = %s", __func__, value, powercom_scratch_buf);
97
98 return powercom_scratch_buf;
99 }
100
powercom_shutdown_nuf(const char * value)101 static double powercom_shutdown_nuf(const char *value)
102 {
103 const char *s = dstate_getinfo("ups.delay.shutdown");
104 uint16_t val, command;
105 int iv;
106
107 iv = atoi(value ? value : s);
108 if (iv < 0 || (intmax_t)iv > (intmax_t)UINT16_MAX) {
109 upsdebugx(0, "%s: value = %d is not in uint16_t range", __func__, iv);
110 return 0;
111 }
112
113 val = (uint16_t)iv;
114 val = val ? val : 1; /* 0 sets the maximum delay */
115 command = ((uint16_t)((val % 60) << 8)) + (uint16_t)(val / 60);
116 command |= 0x4000; /* AC RESTART NORMAL ENABLE */
117 upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command);
118
119 return command;
120 }
121
122 static info_lkp_t powercom_shutdown_info[] = {
123 { 0, NULL, powercom_shutdown_fun, powercom_shutdown_nuf }
124 };
125
powercom_stayoff_nuf(const char * value)126 static double powercom_stayoff_nuf(const char *value)
127 {
128 const char *s = dstate_getinfo("ups.delay.shutdown");
129 uint16_t val, command;
130 int iv;
131
132 iv = atoi(value ? value : s);
133 if (iv < 0 || (intmax_t)iv > (intmax_t)UINT16_MAX) {
134 upsdebugx(0, "%s: value = %d is not in uint16_t range", __func__, iv);
135 return 0;
136 }
137
138 val = (uint16_t)iv;
139 val = val ? val : 1; /* 0 sets the maximum delay */
140 command = ((uint16_t)((val % 60) << 8)) + (uint16_t)(val / 60);
141 command |= 0x8000; /* AC RESTART NORMAL DISABLE */
142 upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command);
143
144 return command;
145 }
146
147 static info_lkp_t powercom_stayoff_info[] = {
148 { 0, NULL, NULL, powercom_stayoff_nuf }
149 };
150
151 static info_lkp_t powercom_beeper_info[] = {
152 { 1, "enabled", NULL, NULL },
153 { 2, "disabled", NULL, NULL }, /* muted? */
154 { 0, NULL, NULL, NULL }
155 };
156
powercom_voltage_conversion_fun(double value)157 static const char *powercom_voltage_conversion_fun(double value)
158 {
159 static char buf[20];
160 snprintf(buf, sizeof(buf), "%0.0f", value * 4);
161 return buf;
162 }
163
164 static info_lkp_t powercom_voltage_conversion[] = {
165 { 0, NULL, powercom_voltage_conversion_fun, NULL }
166 };
167
powercom_upsfail_conversion_fun(double value)168 static const char *powercom_upsfail_conversion_fun(double value)
169 {
170 if ((long)value & 0x0001) {
171 return "fanfail";
172 } else {
173 return "!fanfail";
174 }
175 }
176
177 static info_lkp_t powercom_upsfail_conversion[] = {
178 { 0, NULL, powercom_upsfail_conversion_fun, NULL }
179 };
180
powercom_replacebatt_conversion_fun(double value)181 static const char *powercom_replacebatt_conversion_fun(double value)
182 {
183 if ((long)value & 0x0002) {
184 return "replacebatt";
185 } else {
186 return "!replacebatt";
187 }
188 }
189
190 static info_lkp_t powercom_replacebatt_conversion[] = {
191 { 0, NULL, powercom_replacebatt_conversion_fun, NULL }
192 };
193
powercom_test_conversion_fun(double value)194 static const char *powercom_test_conversion_fun(double value)
195 {
196 if ((long)value & 0x0004) {
197 return "cal";
198 } else {
199 return "!cal";
200 }
201 }
202
203 static info_lkp_t powercom_test_conversion[] = {
204 { 0, NULL, powercom_test_conversion_fun, NULL }
205 };
206
powercom_shutdownimm_conversion_fun(double value)207 static const char *powercom_shutdownimm_conversion_fun(double value)
208 {
209 if ((long)value & 0x0010) {
210 return "shutdownimm";
211 } else {
212 return "!shutdownimm";
213 }
214 }
215
216 static info_lkp_t powercom_shutdownimm_conversion[] = {
217 { 0, NULL, powercom_shutdownimm_conversion_fun, NULL }
218 };
219
powercom_online_conversion_fun(double value)220 static const char *powercom_online_conversion_fun(double value)
221 {
222 if ((long)value & 0x0001) {
223 return "!online";
224 } else {
225 return "online";
226 }
227 }
228
229 static info_lkp_t powercom_online_conversion[] = {
230 { 0, NULL, powercom_online_conversion_fun, NULL }
231 };
232
powercom_lowbatt_conversion_fun(double value)233 static const char *powercom_lowbatt_conversion_fun(double value)
234 {
235 if ((long)value & 0x0002) {
236 return "lowbatt";
237 } else {
238 return "!lowbatt";
239 }
240 }
241
242 static info_lkp_t powercom_lowbatt_conversion[] = {
243 { 0, NULL, powercom_lowbatt_conversion_fun, NULL }
244 };
245
powercom_trim_conversion_fun(double value)246 static const char *powercom_trim_conversion_fun(double value)
247 {
248 if (((long)value & 0x0018) == 0x0008) {
249 return "trim";
250 } else {
251 return "!trim";
252 }
253 }
254
255 static info_lkp_t powercom_trim_conversion[] = {
256 { 0, NULL, powercom_trim_conversion_fun, NULL }
257 };
258
powercom_boost_conversion_fun(double value)259 static const char *powercom_boost_conversion_fun(double value)
260 {
261 if (((long)value & 0x0018) == 0x0018) {
262 return "boost";
263 } else {
264 return "!boost";
265 }
266 }
267
268 static info_lkp_t powercom_boost_conversion[] = {
269 { 0, NULL, powercom_boost_conversion_fun, NULL }
270 };
271
powercom_overload_conversion_fun(double value)272 static const char *powercom_overload_conversion_fun(double value)
273 {
274 if ((long)value & 0x0020) {
275 return "overload";
276 } else {
277 return "!overload";
278 }
279 }
280
281 static info_lkp_t powercom_overload_conversion[] = {
282 { 0, NULL, powercom_overload_conversion_fun, NULL }
283 };
284
285 /* --------------------------------------------------------------- */
286 /* Vendor-specific usage table */
287 /* --------------------------------------------------------------- */
288
289 /* POWERCOM usage table */
290 static usage_lkp_t powercom_usage_lkp[] = {
291 { "PowercomUPS", 0x00020004 },
292 { "PowercomBatterySystem", 0x00020010 },
293 { "PowercomPowerConverter", 0x00020016 },
294 { "PowercomInput", 0x0002001a },
295 { "PowercomOutput", 0x0002001c },
296 { "PowercomVoltage", 0x00020030 },
297 { "PowercomFrequency", 0x00020032 },
298 { "PowercomPercentLoad", 0x00020035 },
299 { "PowercomTemperature", 0x00020036 },
300 { "PowercomDelayBeforeStartup", 0x00020056 },
301 { "PowercomDelayBeforeShutdown", 0x00020057 },
302 { "PowercomTest", 0x00020058 },
303 { "PowercomShutdownRequested", 0x00020068 },
304 { "PowercomInternalChargeController", 0x00020081 },
305 { "PowercomPrimaryBatterySupport", 0x00020082 },
306 { "PowercomDesignCapacity", 0x00020083 },
307 { "PowercomSpecificationInfo", 0x00020084 },
308 { "PowercomManufacturerDate", 0x00020085 },
309 { "PowercomSerialNumber", 0x00020086 },
310 { "PowercomManufacturerName", 0x00020087 },
311 { "POWERCOM1", 0x0084002f },
312 { "POWERCOM2", 0xff860060 },
313 { "POWERCOM3", 0xff860080 },
314 { "PCMDelayBeforeStartup", 0x00ff0056 },
315 { "PCMDelayBeforeShutdown", 0x00ff0057 },
316 { NULL, 0 }
317 };
318
319 static usage_tables_t powercom_utab[] = {
320 powercom_usage_lkp,
321 hid_usage_lkp,
322 NULL,
323 };
324
325 /* --------------------------------------------------------------- */
326 /* HID2NUT lookup table */
327 /* --------------------------------------------------------------- */
328
329 static hid_info_t powercom_hid2nut[] = {
330 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, 0, online_info },
331 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info },
332 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, 0, lowbatt_info },
333 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, 0, charging_info },
334 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.CommunicationLost", NULL, NULL, 0, commfault_info },
335 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, 0, discharging_info },
336 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info },
337 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info },
338 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info },
339 { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info },
340 /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.POWERCOM3", NULL, "%.0f", 0, NULL }, */
341 /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownRequested", NULL, "%.0f", 0, NULL }, */
342 /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.VoltageNotRegulated", NULL, "%.0f", 0, NULL }, */
343 { "BOOL", 0, 0, "UPS.PresentStatus.ACPresent", NULL, NULL, 0, online_info },
344 { "BOOL", 0, 0, "UPS.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info },
345 { "BOOL", 0, 0, "UPS.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, 0, lowbatt_info },
346 { "BOOL", 0, 0, "UPS.PresentStatus.Boost", NULL, NULL, 0, boost_info },
347 { "BOOL", 0, 0, "UPS.PresentStatus.Buck", NULL, NULL, 0, trim_info },
348 { "BOOL", 0, 0, "UPS.PresentStatus.Charging", NULL, NULL, 0, charging_info },
349 { "BOOL", 0, 0, "UPS.PresentStatus.CommunicationLost", NULL, NULL, 0, commfault_info },
350 { "BOOL", 0, 0, "UPS.PresentStatus.Discharging", NULL, NULL, 0, discharging_info },
351 { "BOOL", 0, 0, "UPS.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info },
352 { "BOOL", 0, 0, "UPS.PresentStatus.Overload", NULL, NULL, 0, overload_info },
353 { "BOOL", 0, 0, "UPS.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info },
354 { "BOOL", 0, 0, "UPS.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info },
355 /* { "BOOL", 0, 0, "UPS.PresentStatus.POWERCOM3", NULL, "%.0f", 0, NULL }, */
356 /* { "BOOL", 0, 0, "UPS.PresentStatus.ShutdownRequested", NULL, "%.0f", 0, NULL }, */
357 /* { "BOOL", 0, 0, "UPS.PresentStatus.Tested", NULL, "%.0f", 0, NULL }, */
358 /* { "BOOL", 0, 0, "UPS.PresentStatus.VoltageNotRegulated", NULL, "%.0f", 0, NULL }, */
359
360 /*
361 * According to the HID PDC specifications, the below values should report battery.voltage(.nominal)
362 * PowerCOM duplicates the output.voltage(.nominal) here, so we ignore them
363 * { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL },
364 * { "battery.voltage", 0, 0, "UPS.Battery.Voltage", NULL, "%.2f", 0, NULL },
365 * { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL },
366 * { "battery.voltage.nominal", 0, 0, "UPS.Battery.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL },
367 */
368 { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL },
369 { "battery.charge", 0, 0, "UPS.Battery.RemainingCapacity", NULL, "%.0f", 0, NULL },
370 { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", 0, NULL },
371 { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL },
372 { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL },
373 { "battery.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion },
374 { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion },
375 /* { "unmapped.ups.battery.delaybeforestartup", 0, 0, "UPS.Battery.DelayBeforeStartup", NULL, "%.0f", 0, NULL }, */
376 /* { "unmapped.ups.battery.initialized", 0, 0, "UPS.Battery.Initialized", NULL, "%.0f", 0, NULL }, */
377
378 { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, powercom_beeper_info },
379 { "ups.beeper.status", 0, 0, "UPS.AudibleAlarmControl", NULL, "%s", 0, powercom_beeper_info },
380 { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL },
381 { "ups.date", 0, 0, "UPS.PowerSummary.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion },
382 { "ups.test.result", 0, 0, "UPS.Battery.Test", NULL, "%s", 0, test_read_info },
383 /* { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */
384 /* { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */
385 /* { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */
386 /* { "unmapped.ups.iname", 0, 0, "UPS.iName", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */
387 /* { "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */
388
389 /* The implementation of the HID path UPS.PowerSummary.DelayBeforeStartup is unconventional:
390 * Read:
391 * Byte 7, byte 8 (min)
392 * Write:
393 * Command 4, high byte min, low byte min
394 */
395 { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.DelayBeforeStartup", NULL, "60", HU_FLAG_ABSENT, NULL },
396 { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, "%.0f", 0, powercom_startup_info },
397
398 /* The implementation of the HID path UPS.PowerSummary.DelayBeforeShutdown is unconventional:
399 * Read:
400 * Byte 13, Byte 14 (min, sec)
401 * Write:
402 * If Byte(sec), bit7=0 and bit6=0 Then
403 * If Byte 9, bit0=1 Then command 185, 188, min, sec (OL -> shutdown.return)
404 * If Byte 9, bit0=0 Then command 186, 188, min, sec (OB -> shutdown.stayoff)
405 * If Byte(sec), bit7=0 and bit6=1
406 * Then command 185, 188, min, sec (shutdown.return)
407 * If Byte(sec), bit7=1 and bit6=0 Then
408 * Then command 186, 188, min, sec (shutdown.stayoff)
409 * If Byte(sec), bit7=1 and bit6=1 Then
410 * No actions
411 */
412 { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL },
413 { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, powercom_shutdown_info },
414 { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL },
415 { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, powercom_shutdown_info },
416
417 { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL },
418 { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL },
419 { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%.1f", 0, NULL },
420
421 { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL },
422 { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL },
423 { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.1f", 0, NULL },
424
425 /* { "unmapped.ups.powercom1", 0, 0, "UPS.POWERCOM1", NULL, "%.0f", 0, NULL }, broken pipe */
426 /* { "unmapped.ups.powercom2", 0, 0, "UPS.POWERCOM2", NULL, "%.0f", 0, NULL }, broken pipe */
427 /* { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, */
428 /* { "unmapped.ups.shutdownimminent", 0, 0, "UPS.ShutdownImminent", NULL, "%.0f", 0, NULL }, */
429
430 /* instcmds */
431 { "beeper.toggle", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL },
432 { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL },
433 { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "0", HU_TYPE_CMD, NULL },
434 { "test.battery.start.quick", 0, 0, "UPS.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL },
435 { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, NULL, HU_TYPE_CMD, powercom_startup_info },
436 { "shutdown.return", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info },
437 { "shutdown.stayoff", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info },
438 { "load.on", 0, 0, "UPS.PowerSummary.PCMDelayBeforeStartup", NULL, "0", HU_TYPE_CMD, powercom_startup_info },
439 { "load.off", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, powercom_stayoff_info },
440 { "shutdown.return", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info },
441 { "shutdown.stayoff", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info },
442
443 { "ups.serial", 0, 0, "PowercomUPS.PowercomSerialNumber", NULL, "%s", 0, stringid_conversion },
444 { "ups.mfr", 0, 0, "PowercomUPS.PowercomManufacturerName", NULL, "%s", 0, stringid_conversion },
445 /* { "UPS.DesignCapacity", 0, 0, "PowercomUPS.PowercomDesignCapacity", NULL, "%.0f", 0, NULL }, is always 255 */
446 { "ups.mfr.date", 0, 0, "PowercomUPS.PowercomManufacturerDate", NULL, "%s", 0, date_conversion },
447 { "battery.temperature", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomTemperature", NULL, "%.0f", 0, NULL },
448 { "battery.temperature", 0, 0, "UPS.Battery.Temperature", NULL, "%.1f", 0, NULL },
449 { "battery.charge", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomVoltage", NULL, "%.0f", 0, NULL },
450 /* { "UPS.BatterySystem.SpecificationInfo", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomSpecificationInfo", NULL, "%.0f", 0, NULL }, */
451 { "input.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomFrequency", NULL, "%.0f", 0, NULL },
452 { "input.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion },
453 { "output.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion },
454 { "ups.load", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPercentLoad", NULL, "%.0f", 0, NULL },
455 /* flags: 4 - Testing, 8 - Probably mute (it's set on battery with muted beeper and sometimes during ups test)
456 * bit 0 UPS fault (1 = FAILT)
457 * bit 1 Battery status (1 = BAD, 0 = NORMAL)
458 * bit 2 Test mode (1 = TEST, 0 = NORMAL)
459 * bit 3 X
460 * bit 4 Pre-SD count mode (1 = ACTIVE)
461 * bit 5 Schedule count mode (1 = ACTIVE)
462 * bit 6 Disable NO LOAD SHUTDOWN (1 = ACTIVE)
463 * bit 7 0
464 */
465 /* { "UPS.PowerConverter.Output.InternalChargeController", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, "%.0f", 0, NULL }, */
466 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_upsfail_conversion },
467 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_replacebatt_conversion },
468 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_test_conversion },
469 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_shutdownimm_conversion },
470 /* flags: 1 - On battery, 2 - Low Battery, 8 - Trim, 8+16 - Boost
471 * bit 0 is line fail (1 = INV, 0 = LINE)
472 * bit 1 is low battery (1 = BAT_ LOW, 0 = NORMAL)
473 * bit 2 X
474 * bit 3 AVR status (1 = AVR, 0 = NO_AVR)
475 * bit 4 AVR mode (1 = BOOST, 0 = BUCK)
476 * bit 5 Load status (1 = OVER LOAD, 0 = NORMAL)
477 * bit 6 X
478 * bit 7 SD mode display
479 */
480 /* { "UPS.PowerConverter.Output.PrimaryBatterySupport", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, "%.0f", 0, NULL }, */
481 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_online_conversion },
482 /* Low battery status may not work */
483 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_lowbatt_conversion },
484 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_trim_conversion },
485 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_boost_conversion },
486 { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_overload_conversion },
487 { "output.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomFrequency", NULL, "%.0f", 0, NULL },
488 { "ups.test.result", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomTest", NULL, "%s", 0, test_read_info },
489 /* { "UPS.PowerConverter.ShutdownRequested", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomShutdownRequested", NULL, "%.0f", 0, NULL }, */
490 { "ups.delay.shutdown", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "%.0f", 0, NULL },
491 { "ups.delay.start", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeStartup", NULL, "%.0f", 0, NULL },
492 { "load.off", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL },
493
494 /* end of structure. */
495 { NULL, 0, 0, NULL, NULL, NULL, 0, NULL }
496 };
497
powercom_format_model(HIDDevice_t * hd)498 static const char *powercom_format_model(HIDDevice_t *hd) {
499 return hd->Product;
500 }
501
powercom_format_mfr(HIDDevice_t * hd)502 static const char *powercom_format_mfr(HIDDevice_t *hd) {
503 return hd->Vendor ? hd->Vendor : "PowerCOM";
504 }
505
powercom_format_serial(HIDDevice_t * hd)506 static const char *powercom_format_serial(HIDDevice_t *hd) {
507 return hd->Serial;
508 }
509
510 /* this function allows the subdriver to "claim" a device: return 1 if
511 * the device is supported by this subdriver, else 0. */
powercom_claim(HIDDevice_t * hd)512 static int powercom_claim(HIDDevice_t *hd)
513 {
514 int status = is_usb_device_supported(powercom_usb_device_table, hd);
515
516 switch (status)
517 {
518 case POSSIBLY_SUPPORTED:
519 if (hd->ProductID == 0x0002) {
520 upsdebugx(0,
521 "This Powercom device (%04x/%04x) is not supported by usbhid-ups.\n"
522 "Please use the 'powercom' driver instead.\n", hd->VendorID, hd->ProductID);
523 return 0;
524 }
525 /* by default, reject, unless the productid option is given */
526 if (getval("productid")) {
527 return 1;
528 }
529 possibly_supported("PowerCOM", hd);
530 return 0;
531
532 case SUPPORTED:
533 if (hd->ProductID == 0x0001) {
534 interrupt_only = 1;
535 interrupt_size = 8;
536 }
537 return 1;
538
539 case NOT_SUPPORTED:
540 default:
541 return 0;
542 }
543 }
544
545 subdriver_t powercom_subdriver = {
546 POWERCOM_HID_VERSION,
547 powercom_claim,
548 powercom_utab,
549 powercom_hid2nut,
550 powercom_format_model,
551 powercom_format_mfr,
552 powercom_format_serial,
553 };
554