Hi! For somebody having an interest, there is a driver for APC BH500INET. It uses neon library and based loosely on netxml-ups driver. It have a base
functionality and can not be used to set any ups parameters (althougth in theory it is possible). I use it for about two years and it causes no problems, so I
guess it can be used, on the other hand I already just vaguely can remember how it does work. I have attaching here sources and a patch against
drivers/Makefile.am to build it against version 2.7.4 of Network UPS Tools.
/* Data expiration time in seconds (when to request new data from UPS) */
#define EXPIRATION 60
#define HTTP_TIMEOUT 3
#define DRIVER_NAME "APC BH500INET protocol driver"
#define DRIVER_VERSION "0.1"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ne_session.h"
#include "ne_request.h"
#include "main.h"
#include "bh500.h"
#define BUFSIZEMAX 511
#define ERROR_SIZE 128
int _ups[7];
int item_timeout = HTTP_TIMEOUT;
typedef struct
{
int pos;
char rcvdata[BUFSIZEMAX];
}
rcvbuf;
char *read_ups_data(int * upsdata, int data_type)
{
const char h1[] = "/dynamicdata.cgi";
const char h2[] = "/staticdata.cgi";
const char s0[] = "<TITLE";
const char s2[] = "DYNAMICDATA";
const char s3[] = "<TABLE ";
const char s5[] = "<TR>";
rcvbuf *buf = NULL;
int res = 0;
int state = 0;
ne_session *session;
ne_request *request;
char *error = NULL;
char *errbuf = NULL;
int i, s3pos;
int s6state = 0, s7state = 0;
ne_sock_init();
session = ne_session_create("http", device_path, 80);
ne_set_connect_timeout(session, HTTP_TIMEOUT);
ne_set_read_timeout(session, HTTP_TIMEOUT);
if (data_type)
request = ne_request_create(session, "GET", h1);
else
request = ne_request_create(session, "GET", h2);
if ((res = ne_begin_request(request)) == NE_OK) {
buf = malloc(sizeof(rcvbuf));
if (buf) {
s3pos = 0;
state = 1;
while ((res = ne_read_response_block(request, buf->rcvdata, BUFSIZEMAX)) > 0) {
buf->pos = res;
for (i = 0; i < buf->pos; i++) {
if (data_type) { // 0 - static data 1 - dynamic data
switch (state) {
case 1: /* seeking for '<title' */
if (toupper(buf->rcvdata[i]) == s0[s3pos++]) {
if (s3pos > 5) {
state++;
s3pos = 0;
}
}
else
s3pos = 0;
break;
case 2: /* seeking for closing '>' */
if (buf->rcvdata[i] == '>') {
state++;
s3pos = 0;
}
break;
case 3: /* seeking for title */
if (toupper(buf->rcvdata[i]) == s2[s3pos++]) {
if (s3pos > 10) {
state++;
s3pos = 0;
}
}
else {
goto end; /* title must follows closing '>' */
}
break;
case 4: /* seeking for '<table ' */
if (toupper(buf->rcvdata[i]) == s3[s3pos++]) {
if (s3pos > 6) {
state++;
s3pos = 0;
}
}
else
s3pos = 0;
break;
case 5: /* seeking for closing '>' */
if (buf->rcvdata[i] == '>') {
state++;
s3pos = 0;
}
break;
case 6: /* seeking for '<tr>' */
if (toupper(buf->rcvdata[i]) == s5[s3pos++]) {
if (s3pos > 3) {
state++;
s3pos = 0;
}
}
else
s3pos = 0;
break;
case 7: /* convert to integer */
upsdata[s6state++] = atoi(buf->rcvdata + i);
if (s6state > 6) {
state++;
goto end;
}
state--;
break;
}
}
else {
switch (state) {
case 1: /* seeking for '<title' */
if (toupper(buf->rcvdata[i]) == s0[s3pos++]) {
if (s3pos > 5) {
state++;
s3pos = 0;
}
}
else
s3pos = 0;
break;
case 2: /* seeking for closing '>' */
if (buf->rcvdata[i] == '>') {
state++;
s3pos = 0;
}
break;
case 3: /* seeking for title */
if (toupper(buf->rcvdata[i]) == s2[s3pos++]) {
if (s3pos > 10) {
state++;
s3pos = 0;
}
}
else {
goto end; /* title must follows closing '>' */
}
break;
case 4: /* seeking for '<table ' */
if (toupper(buf->rcvdata[i]) == s3[s3pos++]) {
if (s3pos > 6) {
state++;
s3pos = 0;
}
}
else
s3pos = 0;
break;
case 5: /* seeking for closing '>' */
if (buf->rcvdata[i] == '>') {
state++;
s3pos = 0;
}
break;
case 6: /* seeking for '<tr>' */
if (toupper(buf->rcvdata[i]) == s5[s3pos++]) {
if (s3pos > 3) {
state++;
s3pos = i + 1;
s7state = 0;
}
}
else
s3pos = 0;
break;
case 7: /* seeking for \r or \n */
if (buf->rcvdata[i] == '\r' || buf->rcvdata[i] == '\n') {
int r;
/* right trim */
for (r = i - 1; r > s3pos; r--)
if (isspace(buf->rcvdata[r]))
buf->rcvdata[r] = 0;
else
break;
buf->rcvdata[i] = 0;
switch (s6state++) {
case 2:
dstate_setinfo("ups.model", buf->rcvdata + s3pos);
break;
case 3:
dstate_setinfo("ups.serial", buf->rcvdata + s3pos);
break;
case 4:
dstate_setinfo("ups.firmware", buf->rcvdata + s3pos);
break;
case 6:
dstate_setinfo("ups.mfr.date", buf->rcvdata + s3pos);
break;
case 7:
dstate_setinfo("battery.date", buf->rcvdata + s3pos);
break;
}
if (s6state > 7) {
state++;
goto end;
}
s3pos = 0;
state--;
}
else if (s7state == 0 && !isspace(buf->rcvdata[i])) { /* left trim */
s3pos = i;
s7state = 1;
}
break;
}
}
}
}
}
else {
error = strdup("cannot allocate memory");
}
res = ne_end_request(request);
// if(res == NE_ERROR)
error = strdup(ne_get_error(session));
// else
// if((error = malloc(32)))
// snprintf(error, 32, "ne_end_request returned %d", res);
}
else {
// if(res == NE_ERROR)
error = strdup(ne_get_error(session));
// else
// if((error = malloc(32)))
// snprintf(error, 32, "ne_begin_request returned %d", res);
}
end:
free(buf);
ne_request_destroy(request);
ne_session_destroy(session);
if (state == 8)
return 0;
if (error == NULL)
error = strdup("got malformed http reply");
errbuf = malloc(ERROR_SIZE);
snprintf(errbuf, ERROR_SIZE, "[%s] - %s", device_path, error);
free(error);
return errbuf;
}
/* driver description structure */
upsdrv_info_t upsdrv_info = {
DRIVER_NAME,
DRIVER_VERSION,
"Sukin&syn <[email protected]>",
DRV_EXPERIMENTAL,
{NULL}
};
void upsdrv_help(void)
{
}
void upsdrv_initups(void)
{
memset(_ups, 0, sizeof(_ups));
}
void upsdrv_initinfo(void)
{
char *err=NULL;
int retries=3;
upsdebugx(1, "initinfo");
while((err=read_ups_data(_ups, 0)) && retries--)
{
upslogx(LOG_WARNING, err);
free(err);
}
if(!err)
{
upsdebugx(1, "initinfo OK");
dstate_setinfo("ups.mfr", "APC");
}
else {
fatalx(EXIT_FAILURE, "Cannot connect to ups on %s", device_path);
}
}
void upsdrv_updateinfo(void)
{
char *err=NULL;
err=read_ups_data(_ups, 1);
if(err)
{
upslogx(LOG_WARNING, err);
free(err);
dstate_datastale();
}
else {
upsdebugx(1, "updateinfo OK");
dstate_setinfo("battery.charge", "%d", _ups[1]);
dstate_setinfo("battery.runtime", "%d", _ups[2]);
dstate_setinfo("battery.runtime", "%d", _ups[2]);
dstate_setinfo("ups.load", "%d", _ups[6]);
status_init();
if (_ups[0]==1)
status_set("OL");
else
status_set("OB");
if(_ups[5]==4)
status_set("RB");
status_commit();
dstate_dataok();
}
}
void upsdrv_cleanup(void)
{
}
void upsdrv_shutdown(void)
{
}
void upsdrv_makevartable(void)
{
}
--- drivers/Makefile.am.orig 2016-03-08 15:01:11.000000000 +0300
+++ drivers/Makefile.am 2019-08-19 13:18:26.182840198 +0300
@@ -270,3 +270,8 @@
# corresponding object files. This library is not actually built,
EXTRA_LIBRARIES = libdummy.a
libdummy_a_SOURCES = main.c dstate.c serial.c
+# APC BH500 INET
+NEONXML_DRIVERLIST += bh500
+dist_noinst_HEADERS += bh500.h
+bh500_SOURCES = bh500.c
+bh500_LDADD = $(LDADD_DRIVERS) $(LIBNEON_LIBS)
_______________________________________________
Nut-upsdev mailing list
[email protected]
https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/nut-upsdev