Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Status fields all false on ECO850LCD #10

Open
JonathonReinhart opened this issue Apr 22, 2021 · 4 comments
Open

Status fields all false on ECO850LCD #10

JonathonReinhart opened this issue Apr 22, 2021 · 4 comments

Comments

@JonathonReinhart
Copy link

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]
@patrickfuller
Copy link
Member

patrickfuller commented Apr 22, 2021

@JonathonReinhart interesting! I'll do my best to help but it's unfortunately hard to solve without direct access to the device.

To your point, it looks like the proper register is changing when you plug / unplug but the code isn't translating it. To debug, I'd take the relevant driver code and pepper in some print statements:

keys = [
    'shutdown imminent',
    'ac present',
    'charging',
    'discharging',
    'needs replacement',
    'below remaining capacity',
    'fully charged',
    'fully discharged'
]
number = 32  # whatever the output of get_feature_report(50)[-1] is
print(number)
bits = '{:08b}'.format(number)
print(bits)
bits = bits[::-1]
print(bits)
state = {k: bool(int(v)) for k, v in zip(keys, bits)}
print(state)

Something like that. My only guess right now is it has something to do with the python version. Let me know how it goes!

@JonathonReinhart
Copy link
Author

Well the first thing to note is that the current code has 'bytes': 1, for the status field (at addr 50), but in my toy code (which tried reading up to 100 bytes), register 50 always returned 3 bytes.

I put your bit-decoding snippet above into my code, and got this output:

get_feature_report(report_num=50, max_length=3+1) => [0, 0, 9]
9
00001001
{'shutdown imminent': False, 'ac present': False, 'charging': False, 'discharging': False, 'needs replacement': True, 'below remaining capacity': False, 'fully charged': False, 'fully discharged': True}

...which doesn't seem correct.

So the questions for anyone who wants to take this further are:

  • Why does this return 3 bytes for register 50?
  • Why do the bits not seem to map up?

@patrickfuller
Copy link
Member

@JonathonReinhart I sent an email to TrippLite asking for the protocol. For reference, this driver was built off this protocol doc for SMART1500LCD.

The relevant part is here:

Screen Shot 2021-04-23 at 8 43 23 AM

where the supported protocol expects one byte of this pattern. If this changes between batteries, we could support by checking battery type on init and changing the logic.

@patrickfuller
Copy link
Member

@JonathonReinhart if you're interested in chasing this down, you'll need to email techsupport@tripplite.com with your model and serial number and ask for the communication protocol. Sounds like the same model's supported a few slightly different protocols over the years. Sorry I can't help more!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants