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

Reply via email to