Author Topic: APC UPS Backup - Now just needs a plugin  (Read 23510 times)

Offline Ap15e

  • Beta Testers
  • Sr. Hero Member
  • *****
  • Posts: 2000
  • Karma: +11/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #15 on: September 23, 2011, 05:54:41 pm »
IMHO you should create a new service (e.g., urn:kristopher-com:serviceId:UPS1) for all UPS variables that don't match the predefined variables (http://wiki.micasaverde.com/index.php/Luup_UPNP_Files) for a light sensor.

Your UPS device should consist of two devices: one predefined device (for compatibility with non-UIx user interfaces) and a specialized UPS device.

GUI presentation (quick and dirty):

Install WAI (http://forum.micasaverde.com/index.php?topic=6441.0)

Code: [Select]
local message = "APC #" .. currentStatus .. " " .. currentLinev .. " " .. currentLoadpct .. " " .. currentBcharge .. " " .. currentBattv .. " " .. currentTimeleft
luup.variable_set( 'urn:upnp-ap15e-com:serviceId:WAI1', 'Location', message, WAI_dev_ID )

Derive your device files from WAI and GWC. Copy with pride. :)

GWC uses HTML injection to display icons and formatted text in the GUI. I don't know whether there is a way without using HTML injection which - of course - isn't politically correct (but doesn't break in UI5 ...).

No idea where to get the icons (they must be freely usable).
Consult the dataMine plugin for information about where to put the icons.
« Last Edit: September 23, 2011, 06:14:16 pm by Ap15e »

Offline futzle

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3224
  • Karma: +180/-8
Re: APC UPS Backup - Now just needs a plugin
« Reply #16 on: September 23, 2011, 07:46:27 pm »
I really don't know best practices for setting up devices and what the json should look like.  I have, however, completely the polling of the apcaccess device.  Here is the snippet:
[...]
I wrote some stuff into a light sensor as per Futzle's suggestion.  I have no idea how to make those cool battery icons. [...] If someone at a high level can sketch out the proper way to do the device let me know.

That's a pretty good start.  Here's a few recommendations:

It's allowed (I'd even say it's desirable) to use multiple service IDs in your device.  Pick and choose from among the standard services for things that match up.  For stuff that doesn't match any existing service, invent your own service ID.  For stuff that does match an existing service (LightSensor has CurrentLevel, for example), set the variable twice: once with your service ID and once with the out-of-the-box service ID.  Examples:

Code: [Select]
-- Set battery level.  First is for my code to use, second is for battery icon in dashboard.
-- (The string urn:micasaverde-com:serviceId:HaDevice1 is magical.)
luup.variable_set("urn:kristopher:serviceId:UPS1", "BatteryLevel",
      currentBcharge, CURRENT_APC_DEVICE)
luup.variable_set("urn:micasaverde-com:serviceId:HaDevice1", "BatteryLevel",
      currentBcharge, CURRENT_APC_DEVICE)

Code: [Select]
-- Set load.  First is for my code to use, second is to masquerade as a light sensor.
luup.variable_set("urn:kristopher:serviceId:UPS1", "CurrentLevel",
      currentLoadpct, CURRENT_APC_DEVICE)
luup.variable_set("urn:micasaverde-com:serviceId:LightSensor1", "CurrentLevel",
      currentLoadpct, CURRENT_APC_DEVICE)

Code: [Select]
-- Set online status.  First is to masquerade as a binary switch, second is for my code to use.
luup.variable_set("urn:upnp-org:serviceId:SwitchPower1", "Status",
      (currentStatus == "ONLINE") and 1 or 0, CURRENT_APC_DEVICE)
luup.variable_set("urn:kristopher:serviceId:UPS1", "Status",
      currentStatus, CURRENT_APC_DEVICE)

Code: [Select]
-- No common service knows of this type.  Just set the variable in my namespace.
luup.variable_set("urn:kristopher:serviceId:UPS1", "CurrentVoltage",
      currentLinev, CURRENT_APC_DEVICE)

As Ap15e says, everyone just copies-and-pastes device files.  My usual sequence when making a brand-new device is:

1. Write a stub D_APCUPSBackup1.xml file.  Copy and paste it from an existing device, changing: deviceType, ImplementationFile.  Empty out the serviceList.  Point staticJson to a not-yet-existing D_APCUPSBackup1.json file.  We'll make that later.

2. Write a simple I_APCUPSBackup1.xml file.  Put your Lua in it.  Leave most of the other sections empty.

(By this point you will be able to create a device from this file, see its log output in the Luup log, and see its variables in the Advanced tab.  You can debug your Lua until you are happy that the variables are getting set properly.  The dashboard will have a default device box, and you won't have any events or notifications.)

3. Write the D_APCUPSBackup1.json Static JSON file.  Again, copy and paste from an existing device, or read my reverse-engineering notes on the wiki.  Leave eventList and sceneList empty for the moment.  While debugging, if you need to make changes to the Static JSON file, you need to restart the Luup engine and clear your browser's cache.

(By this point you will have a customized device appearance in the dashboard.  Whatever status you've opted to include in the dashboard will update every thirty seconds, and if you've defined any tabs in the static JSON file, they'll appear too.)

If all you care about is read-only viewing of the UPS status, you can stop now.

4. Define events in the static Json file.  I haven't written the reverse-engineering notes for this section yet, so copy from something already present, or let us help you write them.  Events are what can trigger scenes and notifications.

(By this point you will have the ability for the plugin to trigger a scene when, say, the UPS goes onto battery, or when the load goes over a certain percentage, or whatever other events you've defined.)

5. Go back to the D_APCUPSBackup1.xml file and fill out the serviceList section.  For each service Id you've ever mentioned, put in a service section.  If you follow my examples above you'll have four.  Three will mention S_*.xml files that already exist.  For the fourth, invent a name (say, S_UPS1.xml) and match it against your urn:kristopher:serviceId:UPS1 service Id.

(You won't notice anything different in the UI, but anyone using "watch variable" or other introspection will be able to act on your status variables changing.)

6. Write the S_UPS1.xml file.  Include any variables that you have mentioned in the urn:kristopher:serviceId:UPS1 service namespace.  If you want to define any actions (for the UPS, acting on an interrupt when the UPS suddenly goes off battery would be a good one), put them in.

(Again, no UI difference, but now you can interact with the device over HTTP, and things like the dataMine plugin will be able to plot your device's variables over time.)

Offline Kristopher

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #17 on: September 23, 2011, 10:38:08 pm »
Quote
LOADPCT and TIMELEFT are useless for status ONLINE.

I suspect TIMELEFT is probably very dynamic based on current load, and also that the battery probably loses TIMELEFT over time.  So it might not be entirely useless on wall power.  Also, LOADPCT is a non-100% value after an outage.  Might be useful for taking action during a brownout or rolling blackout.

Kristopher


Offline Ap15e

  • Beta Testers
  • Sr. Hero Member
  • *****
  • Posts: 2000
  • Karma: +11/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #18 on: September 24, 2011, 07:10:29 am »
I suspect that the UPS doesn't measure LOADPCT if STATUS == ONLINE.

May I suggest using a generic 'apcupsd to UPnP' mapping?

Code: [Select]
local st = 'APC      : 001,035,0895\n'..
'DATE     : Fri Sep 23 23:11:37 CEST 2011\n'..
'HOSTNAME : MiOS_XXXXX\n'..
'VERSION  : 3.14.7 (1 August 2009) unknown\n'..
'UPSNAME  : UPS_ES_700\n'..
'CABLE    : USB Cable\n'..
'MODEL    : Back-UPS ES 700G\n'..
'UPSMODE  : Stand Alone\n'..
'STARTTIME: Fri Sep 23 23:11:35 CEST 2011\n'..
'STATUS   : ONLINE\n'..
'LINEV    : 232.0 Volts\n'..
'LOADPCT  :   0.0 Percent Load Capacity\n'..
'BCHARGE  : 100.0 Percent\n'..
'TIMELEFT :  43.8 Minutes\n'..
'MBATTCHG : 5 Percent\n'..
'MINTIMEL : 3 Minutes\n'..
'MAXTIME  : 0 Seconds\n'..
'SENSE    : Medium\n'..
'LOTRANS  : 180.0 Volts\n'..
'HITRANS  : 266.0 Volts\n'..
'ALARMDEL : Always\n'..
'BATTV    : 13.6 Volts\n'..
'LASTXFER : No transfers since turnon\n'..
'NUMXFERS : 0\n'..
'TONBATT  : 0 seconds\n'..
'CUMONBATT: 0 seconds\n'..
'XOFFBATT : N/A\n'..
'STATFLAG : 0x07000008 Status Flag\n'..
'MANDATE  : 2011-05-18\n'..
'SERIALNO : 5\n'..
'BATTDATE : 2011-05-18\n'..
'NOMINV   : 230 Volts\n'..
'NOMBATTV :  12.0 Volts\n'..
'FIRMWARE : 871.O2 .I USB FW:O2\n'..
'APCMODEL : Back-UPS ES 700G\n'..
'END APC  : Fri Sep 23 23:11:53 CEST 2011\n'

for identifier,cur_val in string.gmatch( st, '(%w+).-:(.-)\n' )
 do

  print( identifier, cur_val )

 local value = string.match( cur_val, '[0-9.,-]+' )

--  luup.variable_set("urn:kristopher-com:serviceId:UPS1", tostring( identifier ), tostring (cur_val), CURRENT_APC_DEVICE )

  if value ~= nil
   then

print( '************ ',value )

--    luup.variable_set("urn:kristopher-com:serviceId:UPS1", tostring( identifier ) .. '_VALUE', tostring ( value ), CURRENT_APC_DEVICE )

   end

 end

Not perfect, but I could live with it ...

Offline Kristopher

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #19 on: September 26, 2011, 02:30:23 pm »
Instructions thus far:

1.) Download archive
2.) Extract images into /www/icons
3.) Upload 4 xml/json files via Luup
4.) Create device D_APC.xml

I would like to figure out some way to script an install file.  The installer would need to:

1.) Download the hid.o device into the kernel module directory
2.) echo "hid" > /etc/modules/33-hid
3.) opkg update; opkg install apcupsd
4.) create init script for apcupsd (/etc/rc.*/)
5.) mkdir /www/icons
6.) Download icons from theme with the new filenames

Give it a shot, let me knwo what you think.  Very basic but very functional.

Kristopher

Offline garrettwp

  • Beta Testers
  • Master Member
  • *****
  • Posts: 6376
  • Karma: +226/-128
  • Vera 3, Lite, ISY994
Re: APC UPS Backup - Now just needs a plugin
« Reply #20 on: September 26, 2011, 02:56:21 pm »
There is no attachment.  ;D

- Garrett

Offline Kristopher

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #21 on: September 26, 2011, 10:19:48 pm »
Quote
There is no attachment.

Everyone's a critic!

Offline garrettwp

  • Beta Testers
  • Master Member
  • *****
  • Posts: 6376
  • Karma: +226/-128
  • Vera 3, Lite, ISY994
Re: APC UPS Backup - Now just needs a plugin
« Reply #22 on: September 27, 2011, 12:55:07 am »
Downloading now. However I will not be able to test until I get home and swap out my ups that vera is on with an apc unit. However, I'll have a peak at the code.  :)

- Garrett

Offline futzle

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3224
  • Karma: +180/-8
Re: APC UPS Backup - Now just needs a plugin
« Reply #23 on: September 27, 2011, 06:50:09 am »
Give it a shot, let me knwo what you think.  Very basic but very functional.

Excellent, congratulations on your plugin.  I think you've got all the necessary bits in place, now it's just a matter of tweaking and adding features.

For the auto-install script that you want to make, how about something like this?
1. Add a function to your Lua code that writes a shell script to /tmp, and then calls os.execute() on that script.
2. Add a button to the Control tab in the D_APC.json file ("Install") and a matching <action> in the I_APC.xml file, so that the above function can be invoked from the Control tab.
3. In the Lua startup code, check if the installation has been done, and if it hasn't been, exit the startup with a message that will display to the user in the top right of the dashboard.

The shell script will need all kinds of paranoia checks.  Let me know if you need help with it.

Offline Kristopher

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #24 on: September 27, 2011, 09:27:50 am »
Hi Futzle,

Can you show me how to do number 3?

Kristopher

Offline Ap15e

  • Beta Testers
  • Sr. Hero Member
  • *****
  • Posts: 2000
  • Karma: +11/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #25 on: September 27, 2011, 04:19:06 pm »
Excellent job! Thanks for sharing your code.

Code: [Select]
local currentMode = 0
  if (currentStatus == "ONLINE") then
if (tonumber(currentLinev) > 0) then
currentMode = 50
else
currentMode = 100
end
  end

I'm not sure that the line

Code: [Select]
currentMode = 100

is reachable. It boils down to the question which values the STATUS variable can contain. Unfortunately, I couldn't find a definitive answer to that question.

Offline futzle

  • Beta Testers
  • Master Member
  • *****
  • Posts: 3224
  • Karma: +180/-8
Re: APC UPS Backup - Now just needs a plugin
« Reply #26 on: September 27, 2011, 05:08:12 pm »
Can you show me how to do number 3?

You can test for the existence of a file (say, your /etc/init.d/apcupsd script, or the Kernel module) with Lua code like this:

Code: [Select]
-- Zero if file exists; nonzero if it doesn't.
local exitStatus = os.execute('sh -c "[ -f /etc/init.d/apcupsd ]"')

The plugin's startup function should end with

Code: [Select]
return true

to indicate success.  To indicate plugin failure, return a list starting with false:

Code: [Select]
return false, "APC files not installed", "APC UPS plugin"

The other two strings find their way to the dashboard.

Offline Ap15e

  • Beta Testers
  • Sr. Hero Member
  • *****
  • Posts: 2000
  • Karma: +11/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #27 on: September 27, 2011, 05:33:45 pm »
Code: [Select]
file, msg = io.open(name, "r")
if not file then print(msg) end

Offline guessed

  • Master Member
  • *******
  • Posts: 5294
  • Karma: +90/-22
  • Release compat is not a bolted-on afterthought
Re: APC UPS Backup - Now just needs a plugin
« Reply #28 on: September 27, 2011, 09:07:44 pm »
If you want to be safe, always "return" all three values, even in the true case.  There were issues with intermediate UI4 builds that would freak out if you returned 1 or 2 values... in some cases.

Also, don't forget to close the file after using the test that @Ap15e put in.  It's the approach I'd take, since it's lighter weight than forking a child process (if you can avoid it)

Offline Kristopher

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Re: APC UPS Backup - Now just needs a plugin
« Reply #29 on: September 27, 2011, 11:26:31 pm »
Quote
is reachable. It boils down to the question which values the STATUS variable can contain. Unfortunately, I couldn't find a definitive answer to that question.

I'll double check.  If you have no line on the system it can still be online, in which case you're running off just battery.  I'll work on this a little though.

My plan was to have it work like this

0 = no battery, no line
50 = line
100 = battery, no line

Kristopher