Code Monkey home page Code Monkey logo

tripplite's People

Contributors

alexrudd2 avatar cooperlees avatar newmanjoel avatar patrickfuller avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tripplite's Issues

Add Basic Unittests + GitHub Action

Lets get some very basic unittests so we can see that we can install + run said tests in different supported python versions.

I see we have pre-commit in use to run ruff etc. so I'll also add some CI to run that on PRs and merges to main (probably using the pre-commit ci and github actions to run some unittests)

Add warning for 3016 TrippLites

From here, it looks like UPSs with product code 3016 are temperamental. May even have a different API not supported by this library.

We should specify what versions we support in the README and warn/error in the code if used otherwise.

Help with handling USB Device ID changes in Long Running Processes

I run the prometheus exporter 24/7 and it has been running flawlessly until a recent hardware change. On my Protectli FW1 hardware I had no issues with how code is, but I recently upgraded to a Supermicro 1 RU server and the USB device ID seems to periodically change. That is, when I run lsusb the Device int changes:

Bus 003 Device 098: ID 09ae:2012 Tripp Lite Tripp Lite UPS

This can take sometimes minutes, sometimes hours after starting the collector.

  1. Should this change if no USB hardware is changed (it could be virtual hardware maybe?)?
  • Is my hardware doing something it shouldn't?
  1. How should I try handle this in code? Do you think it's possible?

I've made some attempts on a fork to handle this, but my hardware / USB n00b-ness has resulted in a lot of guessing. My attempt can be seen here: cooperlees@master

Any tips would be appreciated. The reason I think this is doable is as soon as I systemctl restart tripplite_exporter everything works again for a random amount of time.

Status fields all false on ECO850LCD

First of all, thanks for this great little project!

I have a Tripp Lite ECO850LCD, and all of the "status" fields show false:

$ tripplite
{
    "config": {
        "frequency": 60,
        "power": 850,
        "voltage": 120
    },
    "health": 90,
    "input": {
        "frequency": 59.9,
        "voltage": 121.8
    },
    "output": {
        "power": 47,
        "voltage": 122.9
    },
    "status": {
        "ac present": false,
        "below remaining capacity": false,
        "charging": false,
        "discharging": false,
        "fully charged": false,
        "fully discharged": false,
        "needs replacement": false,
        "shutdown imminent": false
    },
    "time to empty": 3049
}

I wrote this little tool for debugging:

#!/usr/bin/env python3
from tripplite.driver import Battery
from pprint import pprint

b = Battery()
with b:
    # https://trezor.github.io/cython-hidapi/api.html?highlight=get_feature_report#hid.device.get_feature_report
    # Parameters:
    #   report_num (int)
    #   max_length (int)
    # Returns: Incoming feature report

    def test(report_num, max_length):
        r = b.device.get_feature_report(report_num, max_length+1)
        if r:
            addr = r[0]
            r = r[1:]
            assert addr == report_num
            print(f"get_feature_report(report_num={report_num}, max_length={max_length}+1) => {r}")
        return r

    for rptnum in range(0, 256):
        test(rptnum, 100)

Here's the output (while connected to AC):

get_feature_report(report_num=0, max_length=100+1) => []
get_feature_report(report_num=1, max_length=100+1) => [120]
get_feature_report(report_num=2, max_length=100+1) => [60]
get_feature_report(report_num=3, max_length=100+1) => [82, 3]
get_feature_report(report_num=4, max_length=100+1) => [12, 0]
get_feature_report(report_num=6, max_length=100+1) => [95, 0]
get_feature_report(report_num=9, max_length=100+1) => [145, 0]
get_feature_report(report_num=13, max_length=100+1) => [6]
get_feature_report(report_num=14, max_length=100+1) => [7]
get_feature_report(report_num=15, max_length=100+1) => [177]
get_feature_report(report_num=16, max_length=100+1) => [6]
get_feature_report(report_num=17, max_length=100+1) => [3]
get_feature_report(report_num=21, max_length=100+1) => [255, 255]
get_feature_report(report_num=22, max_length=100+1) => [255, 255]
get_feature_report(report_num=23, max_length=100+1) => [255, 255]
get_feature_report(report_num=24, max_length=100+1) => [205, 4]
get_feature_report(report_num=25, max_length=100+1) => [87, 2]
get_feature_report(report_num=27, max_length=100+1) => [205, 4]
get_feature_report(report_num=28, max_length=100+1) => [87, 2]
get_feature_report(report_num=30, max_length=100+1) => [10]
get_feature_report(report_num=32, max_length=100+1) => [137, 0]
get_feature_report(report_num=33, max_length=100+1) => [90]
get_feature_report(report_num=34, max_length=100+1) => [32, 0]
get_feature_report(report_num=35, max_length=100+1) => [16]
get_feature_report(report_num=40, max_length=100+1) => [1]
get_feature_report(report_num=41, max_length=100+1) => [5]
get_feature_report(report_num=42, max_length=100+1) => [4]
get_feature_report(report_num=43, max_length=100+1) => [3]
get_feature_report(report_num=44, max_length=100+1) => [1]
get_feature_report(report_num=48, max_length=100+1) => [120]
get_feature_report(report_num=49, max_length=100+1) => [205, 4]
get_feature_report(report_num=50, max_length=100+1) => [0, 0, 17]
get_feature_report(report_num=51, max_length=100+1) => [2]
get_feature_report(report_num=52, max_length=100+1) => [90]
get_feature_report(report_num=53, max_length=100+1) => [233, 11]
get_feature_report(report_num=54, max_length=100+1) => [100]
get_feature_report(report_num=55, max_length=100+1) => [100]
get_feature_report(report_num=56, max_length=100+1) => [30]
get_feature_report(report_num=58, max_length=100+1) => [10]
get_feature_report(report_num=65, max_length=100+1) => [0]
get_feature_report(report_num=67, max_length=100+1) => [1]
get_feature_report(report_num=70, max_length=100+1) => [4, 0]
get_feature_report(report_num=71, max_length=100+1) => [42, 0]
get_feature_report(report_num=81, max_length=100+1) => [0]
get_feature_report(report_num=82, max_length=100+1) => [0]
get_feature_report(report_num=85, max_length=100+1) => [2]
get_feature_report(report_num=87, max_length=100+1) => [0]
get_feature_report(report_num=88, max_length=100+1) => [0]
get_feature_report(report_num=93, max_length=100+1) => [1]
get_feature_report(report_num=97, max_length=100+1) => [255, 255]
get_feature_report(report_num=98, max_length=100+1) => [2]
get_feature_report(report_num=108, max_length=100+1) => [36, 48]
get_feature_report(report_num=194, max_length=100+1) => [11, 107, 42, 105]
get_feature_report(report_num=195, max_length=100+1) => [2, 10, 0, 0]
get_feature_report(report_num=227, max_length=100+1) => [0, 0, 0, 0, 0, 0, 0]

And while disconnected:

get_feature_report(report_num=0, max_length=100+1) => []
get_feature_report(report_num=1, max_length=100+1) => [120]
get_feature_report(report_num=2, max_length=100+1) => [60]
get_feature_report(report_num=3, max_length=100+1) => [82, 3]
get_feature_report(report_num=4, max_length=100+1) => [12, 0]
get_feature_report(report_num=6, max_length=100+1) => [95, 0]
get_feature_report(report_num=9, max_length=100+1) => [145, 0]
get_feature_report(report_num=13, max_length=100+1) => [6]
get_feature_report(report_num=14, max_length=100+1) => [7]
get_feature_report(report_num=15, max_length=100+1) => [177]
get_feature_report(report_num=16, max_length=100+1) => [6]
get_feature_report(report_num=17, max_length=100+1) => [3]
get_feature_report(report_num=21, max_length=100+1) => [255, 255]
get_feature_report(report_num=22, max_length=100+1) => [255, 255]
get_feature_report(report_num=23, max_length=100+1) => [255, 255]
get_feature_report(report_num=24, max_length=100+1) => [0, 0]
get_feature_report(report_num=25, max_length=100+1) => [0, 0]
get_feature_report(report_num=27, max_length=100+1) => [194, 4]
get_feature_report(report_num=28, max_length=100+1) => [88, 2]
get_feature_report(report_num=30, max_length=100+1) => [10]
get_feature_report(report_num=32, max_length=100+1) => [125, 0]
get_feature_report(report_num=33, max_length=100+1) => [91]
get_feature_report(report_num=34, max_length=100+1) => [33, 0]
get_feature_report(report_num=35, max_length=100+1) => [32]
get_feature_report(report_num=40, max_length=100+1) => [1]
get_feature_report(report_num=41, max_length=100+1) => [5]
get_feature_report(report_num=42, max_length=100+1) => [4]
get_feature_report(report_num=43, max_length=100+1) => [3]
get_feature_report(report_num=44, max_length=100+1) => [1]
get_feature_report(report_num=48, max_length=100+1) => [120]
get_feature_report(report_num=49, max_length=100+1) => [0, 0]
get_feature_report(report_num=50, max_length=100+1) => [0, 0, 32]
get_feature_report(report_num=51, max_length=100+1) => [2]
get_feature_report(report_num=52, max_length=100+1) => [91]
get_feature_report(report_num=53, max_length=100+1) => [218, 11]
get_feature_report(report_num=54, max_length=100+1) => [100]
get_feature_report(report_num=55, max_length=100+1) => [100]
get_feature_report(report_num=56, max_length=100+1) => [30]
get_feature_report(report_num=58, max_length=100+1) => [10]
get_feature_report(report_num=65, max_length=100+1) => [0]
get_feature_report(report_num=67, max_length=100+1) => [1]
get_feature_report(report_num=70, max_length=100+1) => [4, 0]
get_feature_report(report_num=71, max_length=100+1) => [42, 0]
get_feature_report(report_num=81, max_length=100+1) => [1]
get_feature_report(report_num=82, max_length=100+1) => [0]
get_feature_report(report_num=85, max_length=100+1) => [2]
get_feature_report(report_num=87, max_length=100+1) => [0]
get_feature_report(report_num=88, max_length=100+1) => [0]
get_feature_report(report_num=93, max_length=100+1) => [1]
get_feature_report(report_num=97, max_length=100+1) => [255, 255]
get_feature_report(report_num=98, max_length=100+1) => [2]
get_feature_report(report_num=108, max_length=100+1) => [36, 48]
get_feature_report(report_num=194, max_length=100+1) => [11, 107, 42, 105]
get_feature_report(report_num=195, max_length=100+1) => [2, 10, 0, 0]
get_feature_report(report_num=227, max_length=100+1) => [0, 0, 0, 0, 0, 0, 0]

Note this in the diff:

-get_feature_report(report_num=50, max_length=100+1) => [0, 0, 17]
+get_feature_report(report_num=50, max_length=100+1) => [0, 0, 32]

Add Device_ID to json response

I am not sure how to request this as I am n00b. But in using this WONDERFUL code for my personal use, I needed to include the device_id so I could create a URL parameter when building a python cgi-bin response webserver for the json. This allowed me to query the json regardless of what order the json response was in.

This is the hack I used in the driver.py. I am positive there is a better and more elegant way to do this. I am just sharing as it was really useful to know which device was which as I have 4 devices to query.

Update to driver.py

    def get(self):
        """Return an object containing all available data."""
        output = {}
        for category, data in structure.items():
            if 'address' in data:
                output[category] = self._read(data)
            else:
                output[category] = {}
                for subcategory, options in data.items():
                    output[category][subcategory] = self._read(options)
        output['device_id'] = self.path
        return output

/usr/lib/cgi-bin/api file contents

#!/usr/bin/python3
import json
import cgi
from tripplite import Battery, battery_paths

link = cgi.FieldStorage()
params = link.getvalue('path')
params = params.replace("'", "")

print("Content-Type: text/html")
print()
with Battery(params) as battery:
    print(json.dumps(battery.get(), indent=1))

your instructions added to allow www-data access to HID

addgroup tripplite
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="09ae", GROUP="tripplite"' > /etc/udev/rules.d/tripplite.rules
udevadm control --reload-rules
usermod -a -G tripplite www-data

added entry for www-data in sudoers

%www-data ALL = (root) NOPASSWD: /usr/lib/cgi-bin/api

sample URL because of my limited python and development experience. I just ended up URL encoding the path/device_id

http://localhost/cgi-bin/api?path=3-4%3A1.0

Allow multiple of the same product

Right now, this driver can handle reading multiple tripplites with unique product IDs but not multiple of the same product.

To do this, follow this advice and use hid.enumerate to grab all unique paths.

The path could then be passed to each TrippLite object.

Collection crashes randomly

Sorry, not sure if this project is still maintained, maybe you could just give me some pointers and I could patch it. I'm running into this error pretty frequently which requires a restart to mitigate, but then will happen again.

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/wsgiref/handlers.py", line 137, in run
    self.result = application(self.environ, self.start_response)
  File "/usr/local/lib/python3.9/site-packages/prometheus_client/exposition.py", line 123, in prometheus_app
    status, header, output = _bake_output(registry, accept_header, params)
  File "/usr/local/lib/python3.9/site-packages/prometheus_client/exposition.py", line 105, in _bake_output
    output = encoder(registry)
  File "/usr/local/lib/python3.9/site-packages/prometheus_client/openmetrics/exposition.py", line 14, in generate_latest
    for metric in registry.collect():
  File "/usr/local/lib/python3.9/site-packages/prometheus_client/registry.py", line 83, in collect
    for metric in collector.collect():
  File "/usr/local/lib/python3.9/site-packages/tripplite/prometheus.py", line 57, in collect
    ups_data = self.get_data()
  File "/usr/local/lib/python3.9/site-packages/tripplite/collectors.py", line 38, in get_data
    ups_data = self.battery.get()
  File "/usr/local/lib/python3.9/site-packages/tripplite/driver.py", line 127, in get
    output[category][subcategory] = self._read(options)
  File "/usr/local/lib/python3.9/site-packages/tripplite/driver.py", line 137, in _read
    report = self.device.get_feature_report(options['address'],
  File "hid.pyx", line 191, in hid.device.get_feature_report
ValueError: not open

module 'hid' has no attribute 'device'

HI there,
I am trying to get this to work on a Raspberry Pi... I have followed the instructions to add tripplite to the pi (all done as root) and when I run it, I get the following error:

sudo tripplite
Traceback (most recent call last):
  File "/usr/local/bin/tripplite", line 8, in <module>
    sys.exit(command_line())
  File "/usr/local/lib/python3.7/dist-packages/tripplite/__init__.py", line 20, in command_line
    with Battery(args.product_id) as battery:
  File "/usr/local/lib/python3.7/dist-packages/tripplite/driver.py", line 86, in __init__
    self.device = hid.device()
AttributeError: module 'hid' has no attribute 'device'

Add serial number to tripplite communication

The Communication Protocol allows items I didn't think were useful on the first pass. Now, using one device to read multiple batteries, I've reconsidered.

On init, we could pull the fixed data (serial, model, etc) and store within the object. This could then be used to uniquely ID each connected device without worrying about udev rules or other UNIX config.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.