GitHub issues can also be used to report HCL updates
[networkupstools/kirr.git] / drivers / victronups.c
blob5b6cf6d406d008d22ff7b7dc05f93b94f54a3643
1 /* victronups.c - Model specific routines for GE/IMV/Victron units
2 * Match, Match Lite, NetUps
4 * Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
5 * Copyright (C) 2000 Radek Benedikt <benedikt@lphard.cz>
6 * old style "victronups"
7 * Copyright (C) 2001 Daniel.Prynych <Daniel.Prynych@hornet.cz>
8 * porting to now style "newvictron"
9 * Copyright (C) 2003 Gert Lynge <gert@lynge.org>
10 * Porting to new serial functions. Now removes \n from data (was causing
11 * periodic misreadings of temperature and voltage levels)
12 * Copyright (C) 2004 Gert Lynge <gert@lynge.org>
13 * Implemented some Instant Commands.
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "main.h"
32 #include "serial.h"
34 #define DRIVER_NAME "GE/IMV/Victron UPS driver"
35 #define DRIVER_VERSION "0.20"
37 /* driver description structure */
38 upsdrv_info_t upsdrv_info = {
39 DRIVER_NAME,
40 DRIVER_VERSION,
41 "Russell Kroll <rkroll@exploits.org>\n" \
42 "Radek Benedikt <benedikt@lphard.cz>\n" \
43 "Daniel Prynych <Daniel.Prynych@hornet.cz>\n" \
44 "Gert Lynge <gert@lynge.org>",
45 DRV_STABLE,
46 { NULL }
49 #define ENDCHAR '\r'
50 #define IGNCHARS "\n"
52 #define UPS_DELAY 150000
53 #define UPS_LONG_DELAY 450000
55 #define VICTRON_OVER 128
56 #define VICTRON_RB 1
57 #define VICTRON_OB 2
58 #define VICTRON_LB 4
60 #define VICTRON_NO_TEST 1
61 #define VICTRON_ABORT_TEST 2
62 #define VICTRON_SYSTEM_TEST 3
63 #define VICTRON_BATTERY_TEST 4
64 #define VICTRON_CALIBRATION 5
65 #define VICTRON_BYPASS_TEST 101
67 #define LENGTH_TEMP 256
69 int sdwdelay = 0; /* shutdown after 0 second */
71 char *model_name;
73 static int start_is_datastale = 1;
75 static int exist_ups_serial = 0;
76 static int exist_ups_temperature = 0;
77 static int exist_output_current = 0;
78 static int exist_battery_charge = 0;
79 static int exist_battery_current = 0;
80 static int exist_battery_temperature = 0;
81 static int exist_battery_runtime = 0;
83 static int test_in_progress = VICTRON_NO_TEST;
85 static int get_data (const char *out_string, char *in_string)
87 int ret_code;
88 ser_send(upsfd, "%s%c", out_string, ENDCHAR);
89 usleep (UPS_DELAY);
90 ret_code = ser_get_line(upsfd, in_string, LENGTH_TEMP, ENDCHAR,
91 IGNCHARS, 3, 0);
92 if (ret_code < 1) {
93 dstate_datastale();
94 return -1;
96 return 0;
99 static int instcmd(const char *cmdname, const char *extra)
101 char temp[ LENGTH_TEMP ];
103 if(!strcasecmp(cmdname, "calibrate.start"))
105 if(get_data("vTi5!",temp))
107 upsdebugx(1, "instcmd: ser_send calibrate.start failed");
108 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
110 else
112 upsdebugx(1, "instcmd: calibrate.start returned: %s", temp);
113 test_in_progress = VICTRON_CALIBRATION;
114 return STAT_INSTCMD_HANDLED;
117 else if(!strcasecmp(cmdname, "calibrate.stop"))
119 if(get_data("vTi2!",temp))
121 upsdebugx(1, "instcmd: ser_send calibrate.stop failed");
122 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
124 else
126 upsdebugx(1, "instcmd: calibrate.stop returned: %s", temp);
127 return STAT_INSTCMD_HANDLED;
130 else if(!strcasecmp(cmdname, "test.battery.stop"))
132 if(get_data("vTi2!",temp))
134 upsdebugx(1, "instcmd: ser_send test.battery.stop failed");
135 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
137 else
139 upsdebugx(1, "instcmd: test.battery.stop returned: %s", temp);
140 return STAT_INSTCMD_HANDLED;
143 else if(!strcasecmp(cmdname, "test.battery.start"))
145 if(get_data("vTi4!",temp))
147 upsdebugx(1, "instcmd: ser_send test.battery.start failed");
148 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
150 else
152 upsdebugx(1, "instcmd: test.battery.start returned: %s", temp);
153 test_in_progress = VICTRON_BATTERY_TEST;
154 return STAT_INSTCMD_HANDLED;
157 else if(!strcasecmp(cmdname, "test.panel.stop"))
159 if(get_data("vTi2!",temp))
161 upsdebugx(1, "instcmd: ser_send test.panel.stop failed");
162 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
164 else
166 upsdebugx(1, "instcmd: test.panel.stop returned: %s", temp);
167 return STAT_INSTCMD_HANDLED;
170 else if(!strcasecmp(cmdname, "test.panel.start"))
172 if(get_data("vTi3!",temp))
174 upsdebugx(1, "instcmd: ser_send test.panel.start failed");
175 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
177 else
179 upsdebugx(1, "instcmd: test.panel.start returned: %s", temp);
180 test_in_progress = VICTRON_SYSTEM_TEST;
181 return STAT_INSTCMD_HANDLED;
184 else if(!strcasecmp(cmdname, "bypass.stop"))
186 if(get_data("vTi2!",temp))
188 upsdebugx(1, "instcmd: ser_send bypass.stop failed");
189 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
191 else
193 upsdebugx(1, "instcmd: bypass.stop returned: %s", temp);
194 return STAT_INSTCMD_HANDLED;
197 else if(!strcasecmp(cmdname, "bypass.start"))
199 if(get_data("vTi101!",temp))
201 upsdebugx(1, "instcmd: ser_send bypass.start failed");
202 return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */
204 else
206 upsdebugx(1, "instcmd: bypass.start returned: %s", temp);
207 test_in_progress = VICTRON_BYPASS_TEST;
208 return STAT_INSTCMD_HANDLED;
211 else
213 upsdebugx(1, "instcmd: unknown command: %s", cmdname);
214 return STAT_INSTCMD_UNKNOWN;
218 void upsdrv_initinfo(void)
221 if (model_name)
222 dstate_setinfo("ups.model", "%s", model_name);
224 upsh.instcmd = instcmd;
226 dstate_addcmd("test.battery.start");
227 dstate_addcmd("test.battery.stop");
228 dstate_addcmd("calibrate.start");
229 dstate_addcmd("calibrate.stop");
230 dstate_addcmd("test.panel.start"); /* We need a GeneralSystemTest, but use this one instead */
231 dstate_addcmd("test.panel.stop"); /* We need a GeneralSystemTest, but use this one instead */
232 dstate_addcmd("bypass.start");
233 dstate_addcmd("bypass.stop");
237 void upsdrv_updateinfo(void)
239 int flags;
240 char temp[ LENGTH_TEMP ];
241 char test_result[ LENGTH_TEMP ];
242 int runtime_sec = -1;
244 if (start_is_datastale)
246 if (get_data("vDS?",temp)) return;
247 if (strcmp(temp+3,"NA"))
248 exist_ups_serial=1;
250 if (get_data("vBT?",temp)) return;
251 if (strcmp(temp+3,"NA"))
252 exist_ups_temperature =1;
254 if (get_data("vO0I?",temp)) return;
255 if (strcmp(temp+4,"NA"))
256 exist_output_current =1;
258 if (get_data("vBC?",temp)) return;
259 if (strcmp(temp+3,"NA"))
260 exist_battery_charge = 1;
262 if (get_data("vBI?",temp)) return;
263 if (strcmp(temp+3,"NA"))
264 exist_battery_charge = 1;
266 if (get_data("vBT?",temp)) return;
267 if (strcmp(temp+3,"NA"))
268 exist_battery_temperature = 1;
270 if (get_data("vBt?",temp)) return;
271 if (strcmp(temp+3,"NA"))
272 exist_battery_runtime = 1;
274 start_is_datastale = 0;
280 /* ups.status */
281 if (get_data("vAa?",temp)) return;
282 flags = atoi (temp+3);
284 status_init();
286 if (flags & VICTRON_OVER)
287 status_set("OVER");
289 if (flags & VICTRON_RB)
290 status_set("RB");
292 if (flags & VICTRON_LB)
293 status_set("LB");
295 if (flags & VICTRON_OB)
296 status_set("OB");
297 else
298 status_set("OL");
300 /* Get UPS test results */
301 if (get_data("vTr?",temp)) return;
302 if (get_data("vTd?",test_result)) return;
304 switch(atoi(temp+3))
306 case 1:
307 upsdebugx(1, "upsdrv_updateinfo: test %i result = Done, Passed: %s",test_in_progress,test_result+3);
308 test_in_progress = VICTRON_NO_TEST;
309 break;
311 case 2:
312 upsdebugx(1, "upsdrv_updateinfo: test %i result = Done, Warning: %s",test_in_progress,test_result+3);
313 test_in_progress = VICTRON_NO_TEST;
314 break;
316 case 3:
317 upsdebugx(1, "upsdrv_updateinfo: test %i result = Done, Error: %s",test_in_progress,test_result+3);
318 test_in_progress = VICTRON_NO_TEST;
319 break;
321 case 4:
322 upsdebugx(1, "upsdrv_updateinfo: test %i result = Aborted: %s",test_in_progress,test_result+3);
323 test_in_progress = VICTRON_NO_TEST;
324 break;
326 case 5:
327 if(test_in_progress==VICTRON_CALIBRATION)
328 status_set("CAL"); /* calibration in progress*/
329 upsdebugx(1, "upsdrv_updateinfo: test %i result = In Progress: %s",
330 test_in_progress,test_result+3);
331 break;
333 case 6:
334 upsdebugx(1, "upsdrv_updateinfo: test result = No test initiated: %s",
335 test_result+3);
336 break;
338 default:
339 upsdebugx(1, "upsdrv_updateinfo: unknown test result: %s / %s",temp+3,test_result+3);
340 break;
343 status_commit();
344 /* dstate_dataok(); */
346 upsdebugx(1, "upsdrv_updateinfo: ups.status = %s\n", dstate_getinfo("ups.status"));
349 /************** ups.x ***************************/
351 /* ups model */
352 if (!model_name)
354 if (get_data("vDM?",temp)) return;
355 dstate_setinfo("ups.model", "%s", temp+3);
356 upsdebugx(1, "ups.model >%s<>%s<\n",temp,temp+3);
361 /* ups.mfr */
362 if (get_data("vDm?",temp)) return;
363 dstate_setinfo("ups.mfr", "%s", temp+3);
364 upsdebugx(1, "ups.mfr >%s<>%s<\n",temp,temp+3);
367 /* ups.serial */
368 if (exist_ups_serial)
370 if (get_data("vDS?",temp)) return;
371 dstate_setinfo("ups.serial", "%s", temp+3);
373 upsdebugx(1, "ups.serial >%s<>%s<\n",temp,temp+3);
375 /* ups.firmware */
376 if (get_data("vDV?",temp)) return;
377 dstate_setinfo("ups.firmware", "%s", temp+3);
378 upsdebugx(1, "ups.firmware >%s<>%s<\n",temp,temp+3);
380 /* ups.temperature */
381 if (exist_ups_temperature)
383 if (get_data("vBT?",temp)) return;
384 dstate_setinfo("ups.temperature", "%s", temp+3);
386 upsdebugx(1, "ups.temperature >%s<>%s<\n",temp,temp+3);
388 /* ups.load */
389 if (get_data("vO0L?",temp)) return;
390 dstate_setinfo("ups.load", "%s", temp+4);
391 upsdebugx(1, "ups.load >%s<>%s<\n",temp,temp+4);
395 /* ups protocol */
396 /*if (get_data("vDC?",temp)) return;
397 dstate_setinfo("ups.protocol", "%s", temp+3;
398 upsdebugx(1, "ups.protocol >%s<>%s<\n",temp,temp+3;
402 /************** input.x *****************/
404 /* input.voltage */
405 if (get_data("vI0U?",temp)) return;
406 dstate_setinfo("input.voltage", "%s", temp+4);
407 upsdebugx(1, "input.voltage >%s<>%s<\n",temp,temp+4);
410 /* input.transfer.low */
411 if (get_data("vFi?",temp)) return;
412 dstate_setinfo("input.transfer.low", "%s", temp+3);
413 upsdebugx(1, "input.transfer.low >%s<>%s<\n",temp,temp+3);
416 /* input.transfer.high */
417 if (get_data("vFj?",temp)) return;
418 dstate_setinfo("input.transfer.high", "%s", temp+3);
419 upsdebugx(1, "input.transfer.high >%s<>%s<\n",temp,temp+3);
422 /* input.frequency */
423 if (get_data("vI0f?",temp)) return;
424 dstate_setinfo("input.frequency", "%2.1f", atof(temp+4) / 10.0);
425 upsdebugx(1, "input.frequency >%s<>%s<\n",temp,temp+4);
428 /*************** output.x ********************************/
431 /* output.voltage */
432 if (get_data("vO0U?",temp)) return;
433 dstate_setinfo("output.voltage", "%s", temp+4);
434 upsdebugx(1, "output.voltage >%s<>%s<\n",temp,temp+4);
437 /* output.frequency */
438 if (get_data("vOf?",temp)) return;
439 dstate_setinfo("output.frequency", "%2.1f", atof(temp+3) / 10.0);
440 upsdebugx(1, "output.frequency >%s<>%s<\n",temp,temp+3);
443 /* output.current */
444 if (exist_output_current)
446 if (get_data("vO0I?",temp)) return;
447 dstate_setinfo("output.current", "%2.1f", atof(temp+4) / 10.0);
449 upsdebugx(1, "output.current >%s<>%s<\n",temp,temp+4);
452 /*************** battery.x *******************************/
454 /* battery charge */
455 if (exist_battery_charge)
457 if (get_data("vBC?",temp)) return;
458 dstate_setinfo("battery.charge", "%s", temp+3);
460 upsdebugx(1, "battery.charge >%s<>%s<\n",temp,temp+3);
462 /* battery.voltage */
463 if (get_data("vBU?",temp)) return;
464 dstate_setinfo("battery.voltage", "%2.1f", atof(temp+3) / 10.0);
465 upsdebugx(1, "battery.voltage >%s<>%s<\n",temp,temp+3);
468 /* battery.current */
469 if (exist_battery_current)
471 if (get_data("vBI?",temp)) return;
472 dstate_setinfo("battery.current", "%2.1f", atof(temp+3) / 10.0);
474 upsdebugx(1, "battery.current >%s<>%s<\n",temp,temp+3);
476 /* battery.temperature */
477 if (exist_battery_temperature)
479 if (get_data("vBT?",temp)) return;
480 dstate_setinfo("battery.temperature", "%s", temp+3);
482 upsdebugx(1, "battery.temperature >%s<>%s<\n",temp,temp+3);
485 /* battery.runtime */
486 if (exist_battery_runtime)
488 if (get_data("vBt?",temp)) return;
489 runtime_sec = strtol(temp+3, NULL, 10)*60;
490 snprintf(temp, sizeof(temp), "%d", runtime_sec);
491 dstate_setinfo("battery.runtime", "%s", temp);
493 upsdebugx(1, "battery.runtime >%s<>%d<\n",temp,runtime_sec);
495 dstate_dataok();
498 void upsdrv_shutdown(void)
500 ser_send(upsfd, "vCc0!%c", ENDCHAR);
501 usleep(UPS_DELAY);
503 ser_send(upsfd, "vCb%i!%c", sdwdelay, ENDCHAR);
506 void upsdrv_help(void)
510 /* list flags and values that you want to receive via -x */
511 void upsdrv_makevartable(void)
513 addvar(VAR_VALUE, "usd", "Seting delay before shutdown");
514 addvar(VAR_VALUE, "modelname", "Seting model name");
517 void upsdrv_initups(void)
519 char temp[ LENGTH_TEMP ], *usd = NULL; /* = NULL je dulezite jen pro prekladac */
522 upsfd = ser_open(device_path);
523 ser_set_speed(upsfd, device_path, B1200);
526 if ((usd = getval("usd"))) {
527 sdwdelay=atoi(usd);
528 upsdebugx(1, "(-x) Delay before shutdown %i",sdwdelay);
531 if ((model_name = getval("modelname"))) {
532 /* kdyz modelname nebylo zadano je vraceno NULL*/
533 upsdebugx(1, "(-x) UPS Name %s",model_name);
536 /* inicializace a synchronizace UPS */
538 ser_send_char(upsfd, ENDCHAR);
539 usleep (UPS_LONG_DELAY);
540 ser_send(upsfd, "?%c", ENDCHAR);
541 usleep (UPS_LONG_DELAY);
542 ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 3, 0);
543 ser_send(upsfd, "?%c", ENDCHAR);
544 usleep (UPS_LONG_DELAY);
545 ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 3, 0);
546 ser_send(upsfd, "?%c", ENDCHAR);
547 usleep (UPS_DELAY);
548 ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 3, 0);
551 /* the upsh handlers can't be done here, as they get initialized
552 * shortly after upsdrv_initups returns to main.
556 void upsdrv_cleanup(void)
558 ser_close(upsfd, device_path);