APsystems APS ECU R local inverters data pull

Niiice
… didn’t have good ideas for layout yet, could you show bit yaml for these bars ?

Got the other card also working today btw

Here’s the yaml for the card that uses the ‘custom:bar-card’. I used a custom “helper” called solar_blank to be able to put a blank bar where there was no panel. Obviously this depends on your layout. Hope this helps.

type: horizontal-stack
cards:
  - type: 'custom:bar-card'
    entities:
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: sensor.inverter_802000110269_power_ch_4
        color: '#8A2BE2'
      - entity: sensor.inverter_802000110269_power_ch_1
        color: '#8A2BE2'
      - entity: sensor.inverter_802000112342_power_ch_4
        color: '#DC143C'
      - entity: sensor.inverter_802000112342_power_ch_1
        color: '#DC143C'
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
    columns: '1'
    min: '0'
    max: '350'
    unit_of_measurement: W
    direction: left
    positions:
      icon: 'off'
      indicator: 'off'
      name: 'off'
      value: inside
    color: var(--accent-color)
    height: 40px
  - type: 'custom:bar-card'
    entities:
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: sensor.inverter_802000104413_power_ch_1
        color: '#A9A9A9'
      - entity: sensor.inverter_802000110549_power_ch_1
        color: '#008080'
      - entity: sensor.inverter_802000110269_power_ch_3
        color: '#8A2BE2'
      - entity: sensor.inverter_802000110269_power_ch_2
        color: '#8A2BE2'
      - entity: sensor.inverter_802000112342_power_ch_3
        color: '#DC143C'
      - entity: sensor.inverter_802000112342_power_ch_2
        color: '#DC143C'
      - entity: sensor.inverter_802000113304_power_ch_1
        color: '#32CD30'
      - entity: sensor.inverter_802000113523_power_ch_4
        color: '#FF00FF'
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
      - entity: input_number.solar_blank
        color: black
        positions:
          icon: 'off'
          indicator: 'off'
          name: 'off'
          value: 'off'
        tap_action:
          action: none
    columns: '1'
    min: '0'
    max: '350'
    unit_of_measurement: W
    direction: left
    positions:
      icon: 'off'
      indicator: inside
      name: 'off'
    color: var(--accent-color)
    height: 40px
  - type: 'custom:bar-card'
    entities:
      - entity: sensor.inverter_802000104413_power_ch_4
        color: '#A9A9A9'
      - entity: sensor.inverter_802000104413_power_ch_3
        color: '#A9A9A9'
      - entity: sensor.inverter_802000104413_power_ch_2
        color: '#A9A9A9'
      - entity: sensor.inverter_802000110549_power_ch_4
        color: '#008080'
      - entity: sensor.inverter_802000110549_power_ch_3
        color: '#008080'
      - entity: sensor.inverter_802000110549_power_ch_2
        color: '#008080'
      - entity: sensor.inverter_802000111314_power_ch_4
        color: '#87CEFA'
      - entity: sensor.inverter_802000111314_power_ch_3
        color: '#87CEFA'
      - entity: sensor.inverter_802000111314_power_ch_2
        color: '#87CEFA'
      - entity: sensor.inverter_802000111314_power_ch_1
        color: '#87CEFA'
      - entity: sensor.inverter_802000113523_power_ch_3
        color: '#FF00FF'
      - entity: sensor.inverter_802000113523_power_ch_2
        color: '#FF00FF'
      - entity: sensor.inverter_802000113523_power_ch_1
        color: '#FF00FF'
    columns: '1'
    min: '0'
    max: '350'
    unit_of_measurement: W
    direction: left
    positions:
      icon: 'off'
      indicator: inside
      name: 'off'
    color: var(--accent-color)
    height: 40px

how’s the bug in debugging going Kevin?
I still have some weird stuff in the morning with stability with the literals and keeps going and entities get stale data in HA.Would love to debug a bit here too if you can use the help, just need a little nudge where to do what for logging.

I’ve been busy the last two days, so haven’t had a lot of time to dedicate to it. I do have some debug entries to go on, and will be able to dedicate time this weekend. I should have a new update then, and I’ll leave some of the debugging in, so if you continue to have issues after the update I can pull from your errors too.

This is the one that I got twice last night:

Using cached data from last successful communication from ECU. Error: Unable to convert binary to int location=17 data=b'4150533131303031353030303230320a'

So I’m not sure why I’m getting this seemingly incomplete response. It’s certainly not bigger than my recv buffer, the is supposed to be the response to my APS110028000[ECU-ID]END command and location=17 is trying to determine the number of inverters. (fyi my locations variable starts at 0 vs your table which starts at 1).

The returned data has a checksum is 15 and that’s how many bytes that there are here… so it’s a bit odd… and not exactly sure what I’m supposed to do with that.

I have done some reading and experimenting but my Python knowledge is subzero. I managed to alter some things and started off with using sendall instead of send because the data to be send is known to us and fixed length. I hope this results in complete command transfers (and therefor responses). This weekend I will also spent time trying to restrain that Python. My first focus would be to make the receive section more reliable and use something like a generetic def (taken from pulsaudio):

def _get_full_response(self, sock):
        result = ""
        rcv_buffer = sock.recv(self._buffer_size)
        result += rcv_buffer.decode('utf-8')
        while len(rcv_buffer) == self._buffer_size:
            rcv_buffer = sock.recv(self._buffer_size)
            result += rcv_buffer.decode('utf-8')
        return result

Very much afraid this is the ‘robustness’ from the device it self playing up.
Probably also the reason they use a pushing process to EMA instead of pulling.

I’m very curious how the device itself works. Does it retreive data from inverters or is it waiting for messages being delivered and can only report when it got data from all inverters.

maybe we should ignore responses that do not have END and make a retry?

Yes, we could but still I would like to know why errors occur and/or try to minimize them.

Your code snippet is only helpful when the recv buffer is smaller than the amount of data sent. My recv buffer is currently at 2KB. There is never a response we are expecting that’s anywhere near 2KB. As you see in my debug message it’s only a few bytes long.

It’s getting the entire recv buffer which only contains

b'4150533131303031353030303230320a'

This code snippet would have returned exactly the same thing, since line 3 rcv_buffer would contain my data above, and then the while loop would return false, because len(recv_buffer) is not equal to 2048.

Now I could do full non-blocking I/O and just poll the socket in a loop and wait until we get an END, but my concern with that is what if the device is terrible (which it kinda seems to be) and sometimes doesn’t send an END? I’d need to put a timeout in place and we’d still end up with the same incomplete data.

I’ll try a couple of different approaches this weekend, but I don’t think we are ever gonna get perfect data from this device - since it’s not really designed to do what we are asking of it.

totally agree with that. I think its also related to the WIFI connection. If they really made a bad wifi client implementation, a short data stream that gets interrupted in air and no resend is done, you’re out of options anyway.
I would love to see this damn port also work on ethernet :slight_smile:
for now, i turn off the device at night. Have a sensor that shows me the ‘data age’ from ecu and from there i might do some automation if data age gets older then lets say 45 minutes. (power cycle ecu orso).
Hope to have a few more days like today, pretty good day without clouds, and then see what stability the damn thing can acheive (i’m now convinced it’s not the python part that is the culprit here).

I’m not saying my code is perfect - I know it’s not - and it certainly could be made better. I’m just not sure putting a lot more effort into it will gain us much more results.

I’m currently not seeing any major stability issues, just some bad data that’s unable to parse at various times. Yesterday it was 13 times total, the day before only about 5. I’m doing an ECU query every 1 minute, so with 1440 queries of the ECU in a day, I’m willing to live with 13 occurrences where it will use the cached data instead.

Most of these errors also seem to occur at night. My device is still connected to the internet, so I don’t know if APsystems doesn’t push down some command set during this time period to do something on the device. Maybe this causes increased load and then the device can’t handle that task, and my queries?

Since the device is a blackbox it’s hard to know.

btw kevin, on totally other topic. Do you have the zwave energy device, with one sensor or 3?
have been looking around and the 3 sensor version seems very interesting

code is good enough, if the interface isnt adhered to by device or whatever, doenst mean code is wrong :slight_smile:
in the end i do seem to have stability issues when device is getting weary in responses. like this morning, it didnt recover and kept sending shitty data. HAd to powercycle to get around it.

Yes, it’s a matter of experimenting and learning. We know responses must end with END and we know incorrect commands given leads to responses without. But after all it isn’t a real disaster if we don’t get it 100% right. The results of getting ECU data into HA is a very rewarding thing!

My aeotec zwave device has 2 CTs attached. We have split phase dual 120V power in the US so I needed one on each leg of the incoming power.

I dumps a like 30+ sensors into HA one for set for each CT, and then a combined one.


Freeze temp seems ok… Did well kevin :+1:

Yes, I can confirm that :+1:

I pushed a new version to github that does some additional error checking yesterday, and ran it all night long.

2021-01-10 02:33:09 WARNING (MainThread) [custom_components.apsystems_ecur] Using cached data from last successful communication from ECU. Error: Result on 'Inverter data' incorrect end signature '202' != END data=b'4150533131303031353030303230320a'
2021-01-10 02:34:09 WARNING (MainThread) [custom_components.apsystems_ecur] Using cached data from last successful communication from ECU. Error: Result on 'Inverter data' incorrect end signature '202' != END data=b'4150533131303031353030303230320a'

I got those 2 last night. I don’t think I’ve ever seen any AT command responses. The new version when validating the checksum response also looks for ‘APS’ at the start and ‘END’ at the end. All errors that it catches will log like above, so you can see the response it tried to parse.

I also just pushed a very simple test program called test.py if you put that in the same directory as APSystemsECUR.py it will use my library to query the ECU at the interval you choose. Change the ecu_ip and sleep variable.

You can then run this at 1 sec intervals, or whatever you wish to really test the library w/o having to deal with the home assistant piece.

Maybe this way we can narrow down some of the particular issues a little eaiser.

Let me know what you find.

One interesting difference with your program and my python library is that you leave the socket open. I open it every time you call the query_ecu command, and it closes at the end. I did this so HA wouldn’t leave a socket open for 60 seconds - I could leave it open, and make sure it’s alive each interval (and re-open if not) if we think that might be a better approach. Will the device allow the socket to stay open and idle for 60 seconds and not disconnect it?

I’m certainly willing to go for a more passive approach and if the ECU does indeed print the updates to EMA via the socket. Let me know if that seems to be the case after more testing.

I’d be happy to alter the code for that approach, as it seems like it would be safer and more stable.

weird stuff… alsmost going down a rabbit hole.
My ecu is now off at night for few reasons, one is also have it near my bedroom now and the powersuppply is bit noisy …
Got it running for whole weekend without any measurement gaps. Will try your newer version tomorrow, more stability wont harm.
The other responses values that edwin sees, are really confusing. Cant make any of it, will have a read through https://www.espressif.com/sites/default/files/documentation/4b-esp8266_at_command_examples_en.pdf
maybe that give some clarity