MODBUS help needed - Ampinvt solar controller to Home Assistant via ESPhome

Using the suggestion, I have managed to get bit values into HA and I’ll tidy up the aesthetics later. Now I have a new challenge…

The UART read is producing this hex output:

01:B3:01:00:07:00:02:43:04:99:00:C0:00:4F:00:00:00:03:00:00:00:00:00:18:00:00:0F:89:00:00:00:00:00:00:00:00:60

The first 3 bytes (position 0, 1, 2) are static and can be dismissed. The bytes I think I am splitting into bits are bytes 3, 4 and 5 - 00:07:00 - but the bits dont match up.

When accurate, there should only be 3 1’s and everything else should be 0’s. Here is what I’m getting though (I have bytes 3 and 5 disabled in the code):

I’m confused about a couple of things;

  1. This statement in the protocol document - "The byte contains: 1 start bit, 8 data bits (least significant bit is sent first), no parity bit, 1 stop bit, 10 bits in total. Each character or byte is sent in order (left to right): Least Significant Bit (LSB)…Most Significant Bit (MSB). According to everything I am reading, left to right would result in MSB to LSB. Right to left would be LSB to MSB. It wouldn’t surprise me to find more errors in this document though.
  2. in the code below (I had already split out the bit and byte processing into different files yesterday) I can’t figure out if I should be numbering high to low or low to high or if I should be numbering 0x0 to 0x7 or 0x1 to 0x8 for bit positions.

I figure something funky is happening as a result but with the bytes either side of 4 being 00, I still think I should only be seeing 3 1’s, even if the position of them is offset.

Any ideas?

include code for bit processing:

// *****************************************************************
// *          ESPHome Custom Component Modbus sniffer for          *
// *              Ampinvt MPPT Solar Controller                    *
// *                  Original code credits:                       *
// *                https://github.com/htvekov/                    *
// *               https://github.com/assembly12/                  *
// *****************************************************************

#include "esphome.h"

class ampinvtbitsensor : public PollingComponent, public Sensor, public UARTDevice {
  public:
    ampinvtbitsensor(UARTComponent *parent) : PollingComponent(600), UARTDevice(parent) {}

  //37 bytes total - 25 bytes used, 12 bytes unused
  Sensor *ampinvt_op_status              = new Sensor(); // 1 bit ~ byte 3 (0=Normal, 1=Abnormal - Battery Automatic Recognition Error)
  Sensor *ampinvt_battery_status         = new Sensor(); // 1 bit ~ byte 3 (0=Normal, 1=Over Discharge Protection)
  Sensor *ampinvt_fan_status             = new Sensor(); // 1 bit ~ byte 3 (0=Normal, 1=Fan Failure)
  Sensor *ampinvt_overheat_status        = new Sensor(); // 1 bit ~ byte 3 (0=Normal, 1=Over Temperature Protection)
  Sensor *ampinvt_dcoutput_status        = new Sensor(); // 1 bit ~ byte 3 (0=Normal, 1=DC Output SHort / Over Current Protection)
  Sensor *ampinvt_inttemp1_status        = new Sensor(); // 1 bit ~ byte 3 (0=Close, 1=Fault)
  Sensor *ampinvt_inttemp2_status        = new Sensor(); // 1 bit ~ byte 3 (0=Close, 1=Fault)
  Sensor *ampinvt_exttemp_status         = new Sensor(); // 1 bit ~ byte 3 (0=Close, 1=Fault)
  Sensor *ampinvt_chg_status             = new Sensor(); // 1 bit ~ byte 4 (0=Not Charging, 1=Charging)
  Sensor *ampinvt_equalchg_status        = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_track_status           = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_floatchg_status        = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_chgcurrentlimit_status = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_chgderating_status     = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_remoteprohibchg_status = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_pvovervolt_status      = new Sensor(); // 1 bit ~ byte 4 (1=True)
  Sensor *ampinvt_chgoutputrelay_status  = new Sensor(); // 1 bit ~ byte 5 (0=Close, 1=Open)
  Sensor *ampinvt_loadoutput_status      = new Sensor(); // 1 bit ~ byte 5 (0=Close, 1=Open)
  Sensor *ampinvt_fanrelay_status        = new Sensor(); // 1 bit ~ byte 5 (0=Close, 1=Open)
  Sensor *ampinvt_overchgprotect_status  = new Sensor(); // 1 bit ~ byte 5 (0=Normal, 1=Overcharge Protection)
  Sensor *ampinvt_overvoltprotect_status = new Sensor(); // 1 bit ~ byte 5 (0=Normal, 1=Overvoltage Protection)
  
  void setup() override {
  }

  std::vector<int> bytes;

  void update() {
    while(available() > 0) {
      bytes.push_back(read());      
      if(bytes.size() < 37){
        continue;  
      }
    
      else {
      }
	    if(bytes.size() == 37) {

        // uint8_t op_status_byte = (uint8_t)(bytes[3]);
        // #define BIT_OPERATING_STATUS       0x8 // 00000001
        // #define BIT_BATTERY_STATUS         0x7 // 00000010
        // #define BIT_FAN_STATUS             0x6 // 00000100
        // #define BIT_TEMPERATURE_STATUS     0x5 // 00001000
        // #define BIT_DCOUTPUT_STATUS        0x4 // 00010000
        // #define BIT_INTTEMP1_STATUS        0x3 // 00100000
        // #define BIT_INTTEMP2_STATUS        0x2 // 01000000
        // #define BIT_EXTTEMP_STATUS         0x1 // 10000000
        // id(ampinvt_op_status).publish_state((bool)(op_status_byte & BIT_OPERATING_STATUS));
        // id(ampinvt_battery_status).publish_state((bool)(op_status_byte & BIT_BATTERY_STATUS));
        // id(ampinvt_fan_status).publish_state((bool)(op_status_byte & BIT_FAN_STATUS));
        // id(ampinvt_overheat_status).publish_state((bool)(op_status_byte & BIT_TEMPERATURE_STATUS));
        // id(ampinvt_dcoutput_status).publish_state((bool)(op_status_byte & BIT_DCOUTPUT_STATUS));
        // id(ampinvt_inttemp1_status).publish_state((bool)(op_status_byte & BIT_INTTEMP1_STATUS));
        // id(ampinvt_inttemp2_status).publish_state((bool)(op_status_byte & BIT_INTTEMP2_STATUS));
        // id(ampinvt_exttemp_status).publish_state((bool)(op_status_byte & BIT_EXTTEMP_STATUS));

        uint8_t chg_status_byte = (uint8_t)(bytes[4]);
        #define BIT_CHARGING_STATUS        0x1 // 00000001
        #define BIT_EQUALCHG_STATUS        0x2 // 00000010
        #define BIT_TRACK_STATUS           0x3 // 00000100
        #define BIT_FLOATCHG_STATUS        0x4 // 00001000
        #define BIT_CHGCURRENTLIMIT_STATUS 0x5 // 00010000
        #define BIT_CHGDERATING_STATUS     0x6 // 00100000
        #define BIT_REMOTEPROHIBCHG_STATUS 0x7 // 01000000
        #define BIT_PVOVERVOLT_STATUS      0x8 // 10000000
        id(ampinvt_chg_status).publish_state((bool)(chg_status_byte & BIT_CHARGING_STATUS));
        id(ampinvt_equalchg_status).publish_state((bool)(chg_status_byte & BIT_EQUALCHG_STATUS));
        id(ampinvt_track_status).publish_state((bool)(chg_status_byte & BIT_TRACK_STATUS));
        id(ampinvt_floatchg_status).publish_state((bool)(chg_status_byte & BIT_FLOATCHG_STATUS));
        id(ampinvt_chgcurrentlimit_status).publish_state((bool)(chg_status_byte & BIT_CHGCURRENTLIMIT_STATUS));
        id(ampinvt_chgderating_status).publish_state((bool)(chg_status_byte & BIT_CHGDERATING_STATUS));
        id(ampinvt_remoteprohibchg_status).publish_state((bool)(chg_status_byte & BIT_REMOTEPROHIBCHG_STATUS));
        id(ampinvt_pvovervolt_status).publish_state((bool)(chg_status_byte & BIT_PVOVERVOLT_STATUS));

        // uint8_t ctl_status_byte = (uint8_t)(bytes[5]);
        // #define BIT_CHGOUTRLY_STATUS       0x8 // 00000001
        // #define BIT_LOADOUTPUT_STATUS      0x7 // 00000010
        // #define BIT_FANRLY_STATUS          0x6 // 00000100
        // #define BIT_SPARE1_STATUS          0x5 // 00001000
        // #define BIT_OVERCHGPROTECT_STATUS  0x4 // 00010000
        // #define BIT_OVERVOLTPROTECT_STATUS 0x3 // 00100000
        // #define BIT_SPARE2_STATUS          0x2 // 01000000
        // #define BIT_SPARE3_STATUS          0x1 // 10000000
        // id(ampinvt_chgoutputrelay_status).publish_state((bool)(ctl_status_byte & BIT_CHGOUTRLY_STATUS));
        // id(ampinvt_loadoutput_status).publish_state((bool)(ctl_status_byte & BIT_LOADOUTPUT_STATUS));
        // id(ampinvt_fanrelay_status).publish_state((bool)(ctl_status_byte & BIT_FANRLY_STATUS));
        // id(ampinvt_overchgprotect_status).publish_state((bool)(ctl_status_byte & BIT_OVERCHGPROTECT_STATUS));
        // id(ampinvt_overvoltprotect_status).publish_state((bool)(ctl_status_byte & BIT_OVERVOLTPROTECT_STATUS));

        bytes.clear();
      }
    }    
  }
  };

I do not know a lot of C++ and don’t really understand how you retrieve the bits (which certainly does not mean its wrong :wink: ).

11111110 means 254 or 0xFE, which doesn’t correspond to any of the bytes
01111111 means 127 or 0x7F, dito.

This leads me to believe that retrieving the bits isn’t done right.

How about not actually retrieving the bits, but rather just using some logic.

if byte >= 128){
  id(ampinvt_chg_status).publish_state(1);
}
else {
  id(ampinvt_chg_status).publish_state(0);
}

if byte-128 >= 64){
  id(ampinvt_equalchg_status).publish_state(1);
}
else {
  id(ampinvt_equalchg_status).publish_state(0);
}

if byte-128-64 >= 32){
  id(ampinvt_track_status).publish_state(1);
}
else {
  id(ampinvt_track_status).publish_state(0);
}

if byte-128-64-32 >= 16){
  id(ampinvt_floatchg_status).publish_state(1);
}
else {
  id(ampinvt_floatchg_status).publish_state(0);
}

if byte-128-64-32-16 >= 8){
  id(ampinvt_chgcurrentlimit_status).publish_state(1);
}
else {
  id(ampinvt_chgcurrentlimit_status).publish_state(0);
}

if byte-128-64-32-16-8 >= 4){
  id(ampinvt_chgderating_status).publish_state(1);
}
else {
  id(ampinvt_chgderating_status).publish_state(0);
}

if byte-128-64-32-16-8-4 >= 2){
  id(ampinvt_remoteprohibchg_status).publish_state(1);
}
else {
  id(ampinvt_remoteprohibchg_status).publish_state(0);
}

if byte-128-64-32-16-8-4-2 >= 1){
  id(ampinvt_pvovervolt_status).publish_state(1);
}
else {
  id(ampinvt_pvovervolt_status).publish_state(0);
}

might not be very clean, but perhaps easier to wrap your head around and to debug.

I agree, I think it is in the bit processing too.

Your alternative solution post made me smile; it seems like the people that can think about alternative solutions or workarounds are getting fewer and I briefly thought about something similar yesterday while reading about bitmasks and bitshifting and bit numbering and location indexes. Its as ugly as a Christmas sweater but it might work for now. Might be fun to try out when I get a moment. I very much appreciate you taking the time to write that out.

Slightly different topic - text sensors. I don’t use them often and when I do, they tend to be different platforms so implementation is easy. I created a bunch yesterday but esphome is throwing a compile error no matter what I do. It requires a platform so I used - platform: template but then it doesn’t like the listed sensors. Any ideas? I’ve tried about every different permutation I can think of.

  text_sensor:
    - platform: template
      text_sensors:
      - name: "Ampinvt Operating Status"
        id: "ampinvt_op_status_text_sensor"
      - name: "Ampinvt Battery Status"
        id: "ampinvt_battery_status_text_sensor"

Remove the line text_sensors: and remove the “-” before name.
See:

Yeah, tried that…

ERROR Error while reading config: Invalid YAML syntax:

Duplicate key "name"
  in "/config/esphome/esp32_barn_controller.yaml", line 436, column 5:
        name: "Ampinvt Battery Status"
        ^
NOTE: Previous declaration here:
  in "/config/esphome/esp32_barn_controller.yaml", line 434, column 5:
        name: "Ampinvt Operating Status"
        ^

Seems the - is there to allow multiple name entries. If I put the - back I get a different error…

ERROR Error while reading config: Invalid YAML syntax:

while parsing a block mapping
  in "/config/esphome/esp32_barn_controller.yaml", line 433, column 5:
      - platform: template
        ^
expected <block end>, but found '-'
  in "/config/esphome/esp32_barn_controller.yaml", line 434, column 5:
        - name: "Ampinvt Operating Status"
        ^

Taking one name: entry is fine. Multiples fail. I wondered if I was using the wrong platform type but I can’t figure out which would be better.

  text_sensor:
    - platform: template
      name: "Ampinvt Operating Status"
      id: "ampinvt_op_status_text_sensor"
    - platform: template
      name: "Ampinvt Battery Status"
      id: "ampinvt_battery_status_text_sensor"

I managed to get the bit values sorted out and I now have regularly updating binary states for the sensors. Now I just need to get them into a human readable form.

As a quick recap, I converged the separate includes into one file again and I have checked the states under dev tools and watched the values change between 1 and 0.

Here is where the fun begins. Esphome wont validate the yaml code under ‘on_value’. I’ve tried numerous was to format the if function and the top one is currently a reflection of what is in the docs for on_value with lambda:

    - name: "Charging Status"
      id: "ampinvt_chg_status"
      icon: mdi:battery-charging
      internal:
      on_value:
        then:
          - if:
              condition:
                lambda: 'return id(ampinvt_chg_status).state < 1;'
              then:  
                - id(ampinvt_chg_status_text_sensor).publish_state("Not Charging");
              else: 
                - id(ampinvt_chg_status_text_sensor).publish_state("Charging");

    - name: "Equal Charging Status"
      id: "ampinvt_equalchg_status"
      # internal:
      # on_value:
      #   then:
      #     - lambda: |-
      #         if(x==0) {
      #           id(ampinvt_equalchg_status_text_sensor).publish_state("Not Equalising");
      #         }
      #         else  {
      #           id(ampinvt_equalchg_status_text_sensor).publish_state("Equalising");
      #         }
    - name: "MPPT Tracking Status"
      id: "ampinvt_track_status"
      internal:
      on_value:
        then:
          - lambda: |-
              if(x == 0) {
                id(ampinvt_track_status_text_sensor).publish_state("Not Tracking");
              }
              else  {
                id(ampinvt_track_status_text_sensor).publish_state("Tracking");
              }
    - name: "Float Charging Status"
      id: "ampinvt_floatchg_status"
      internal:
        on_value:
        then:
          - lambda: |-
              if(x==0) {
                id(ampinvt_floatchg_status_text_sensor).publish_state("Not Float Charging");
              }
              else  {
                id(ampinvt_floatchg_status_text_sensor).publish_state("Float Charging");
              }

The error that keeps coming up is the top one regarding boolean value:

      Expected boolean value, but cannot convert None to a boolean. Please use 'true' or 'false'.
      internal: 
      on_value: 
        then: 
          - lambda: |-
              if(x == 0) {
                id(ampinvt_track_status_text_sensor).publish_state("Not Tracking");
              }
              else  {
                id(ampinvt_track_status_text_sensor).publish_state("Tracking");
              }

I wondered if the issue was coming over from the include file so I tried changing the (bool) function to (int) but that didn’t change anything:

        uint8_t chg_status_byte = (uint8_t)(bytes[4]);
        #define BIT_CHARGING_STATUS        0x1  // 00000001
        #define BIT_EQUALCHG_STATUS        0x2  // 00000010
        #define BIT_TRACK_STATUS           0x4  // 00000100
        #define BIT_FLOATCHG_STATUS        0x8  // 00001000
        #define BIT_CHGCURRENTLIMIT_STATUS 0x10 // 00010000
        #define BIT_CHGDERATING_STATUS     0x20 // 00100000
        #define BIT_REMOTEPROHIBCHG_STATUS 0x40 // 01000000
        #define BIT_PVOVERVOLT_STATUS      0x80 // 10000000
        id(ampinvt_chg_status).publish_state((int)(chg_status_byte & BIT_CHARGING_STATUS));
        id(ampinvt_equalchg_status).publish_state((bool)(chg_status_byte & BIT_EQUALCHG_STATUS));
        id(ampinvt_track_status).publish_state((int)(chg_status_byte & BIT_TRACK_STATUS));
        id(ampinvt_floatchg_status).publish_state((int)(chg_status_byte & BIT_FLOATCHG_STATUS));
        id(ampinvt_chgcurrentlimit_status).publish_state((bool)(chg_status_byte & BIT_CHGCURRENTLIMIT_STATUS));
        id(ampinvt_chgderating_status).publish_state((bool)(chg_status_byte & BIT_CHGDERATING_STATUS));
        id(ampinvt_remoteprohibchg_status).publish_state((bool)(chg_status_byte & BIT_REMOTEPROHIBCHG_STATUS));
        id(ampinvt_pvovervolt_status).publish_state((bool)(chg_status_byte & BIT_PVOVERVOLT_STATUS));   

Any thoughts? It seems esphome is expecting ‘true’ or ‘false’ but what is coming in from the include is 1 and 0. I figure that because esphome doesn’t recognise 1 or 0 it is ignoring the value to achieve ‘none’.

LOL! Not sure what I did to fix it but its working now!

Edit:
It is the internal: on the custom sensors that sets off the error for boolean. If I disable the internal: setting then it works. I found it by accident it seems, when I inadvertantly selected that while block-disabling code.

Perhaps try this:

              if(x) {
                id(ampinvt_track_status_text_sensor).publish_state("Not Tracking");
              }
              else  {
                id(ampinvt_track_status_text_sensor).publish_state("Tracking");
              }

@assembly - see edit above.

Ah. I see now. Internal should be either true or false. In your example it has no value.

Yes, that was it. Just tidying everything up then I need to figure out how to publish something on Github. I’m sure there is a Youtube video for that somewhere.

1 Like

The project is now up on Github here. I have gained much from others over the years and it feels pretty damn good to give back. I’ve learned a lot through this project and once again, I am grateful to those that helped so much. It’s my first repo on Github too!

When I get a moment, I’ll use what I learned to create a similar solution for the Renogy Rover controller as I have one sat here.

All done I guess - Great work :muscle::sunglasses:

hello i am trying to implement the project as well. currently my configuration looks like this. Is this correct because the wifi settings should not run manually but automatically via DHCP. This is how the configuration looks like:

esphome:

    name: "laderegler"

    comment: "mppt auslesen"

    includes:

      - ampinvt.h

 

  esp32:

    board: esp32dev

    framework:

      version: recommended

      type: arduino

 

  logger:

    level: DEBUG

 

  wifi:

    ap:

      ssid: "Fallback_SolarMonitor"

      password: "xxxxxxxx"

      ap_timeout: 1min

    domain: .local

    reboot_timeout: 15min

    power_save_mode: NONE

    fast_connect: false

    output_power: 20.0

    networks:

    - ssid: xxxxxxxxxx

      password: xxxxxxxx

      priority: 0.0

 

  captive_portal:

   

  uart:

    id: uart_bus

    tx_pin: 1 # Yellow Wire - MPPT UART Transmit

    rx_pin: 3 # Green Wire - MPPT UART Receive

    rx_buffer_size: 2048

    baud_rate: 9600

    parity: NONE

    stop_bits: 1

    debug:

 

  time:

    - platform: homeassistant

      id: esptime

      on_time:

        - seconds: 0,30

          then:

            - uart.write: [ 0x01, 0xB3, 0x01, 0x00, 0x00, 0x00, 0x00, 0xB5 ] # Reads only real-time data

  #          - uart.write: [ 0x01, 0xB2, 0x01, 0x00, 0x00, 0x00, 0x00, 0xB4 ] # Reads only parameter settings

 

  sensor:

  - platform: custom

    lambda: |-

      auto ampinvtsensors = new ampinvtsensor(id(uart_bus));

      App.register_component(ampinvtsensors);

      return {\

      ampinvtsensors->ampinvt_op_status, \

      ampinvtsensors->ampinvt_battery_status, \

      ampinvtsensors->ampinvt_fan_status, \

      ampinvtsensors->ampinvt_overheat_status, \

      ampinvtsensors->ampinvt_dcoutput_status, \

      ampinvtsensors->ampinvt_inttemp1_status, \

      ampinvtsensors->ampinvt_inttemp2_status, \

      ampinvtsensors->ampinvt_exttemp_status, \

      ampinvtsensors->ampinvt_chg_status, \

      ampinvtsensors->ampinvt_equalchg_status, \

      ampinvtsensors->ampinvt_track_status, \

      ampinvtsensors->ampinvt_floatchg_status, \

      ampinvtsensors->ampinvt_chgcurrentlimit_status, \

      ampinvtsensors->ampinvt_chgderating_status, \

      ampinvtsensors->ampinvt_remoteprohibchg_status, \

      ampinvtsensors->ampinvt_pvovervolt_status, \

      ampinvtsensors->ampinvt_chgoutputrelay_status, \

      ampinvtsensors->ampinvt_loadoutput_status, \

      ampinvtsensors->ampinvt_fanrelay_status, \

      ampinvtsensors->ampinvt_overchgprotect_status, \

      ampinvtsensors->ampinvt_overvoltprotect_status, \

      ampinvtsensors->ampinvt_pv_voltage, \

      ampinvtsensors->ampinvt_battery_voltage, \

      ampinvtsensors->ampinvt_charge_current, \

      ampinvtsensors->ampinvt_mppt_temperature, \

      ampinvtsensors->ampinvt_battery_temperature, \

      ampinvtsensors->ampinvt_today_yield, \

      ampinvtsensors->ampinvt_generation_total, \

      };

    sensors:

    - name: "Operating Status"

      id: ampinvt_op_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_op_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_op_status_text_sensor).publish_state("Abnormal");

              }

    - name: "Battery Status"

      id: ampinvt_battery_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_battery_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_battery_status_text_sensor).publish_state("OD Protection On");

              }

    - name: "Fan Status"

      id: ampinvt_fan_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_fan_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_fan_status_text_sensor).publish_state("Fan Failure");

              }

    - name: "Overheat Status"

      id: ampinvt_overheat_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_overheat_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_overheat_status_text_sensor).publish_state("OT Protection On");

              }

    - name: "DC Output Status"

      id: ampinvt_dcoutput_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_dcoutput_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_dcoutput_status_text_sensor).publish_state("DC Output Short");

              }

    - name: "Internal Temperature 1 Status"

      id: ampinvt_inttemp1_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_inttemp1_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_inttemp1_status_text_sensor).publish_state("Fault");

              }

    - name: "Internal Temperature 2 Status"

      id: ampinvt_inttemp2_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_inttemp2_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_inttemp2_status_text_sensor).publish_state("Fault");

              }

    - name: "External Temperature Status"

      id: ampinvt_exttemp_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_exttemp_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_exttemp_status_text_sensor).publish_state("Fault");

              }

    - name: "Charging Status"

      id: ampinvt_chg_status

      icon: mdi:battery-charging

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_chg_status_text_sensor).publish_state("Not Charging");

              }

              else  {

                id(ampinvt_chg_status_text_sensor).publish_state("Charging");

              }

    - name: "Equal Charging Status"

      id: ampinvt_equalchg_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_equalchg_status_text_sensor).publish_state("Not Equalising");

              }

              else  {

                id(ampinvt_equalchg_status_text_sensor).publish_state("Equalising");

              }

    - name: "MPPT Tracking Status"

      id: ampinvt_track_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_track_status_text_sensor).publish_state("Not Tracking");

              }

              else  {

                id(ampinvt_track_status_text_sensor).publish_state("Tracking");

              }

    - name: "Float Charging Status"

      id: ampinvt_floatchg_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_floatchg_status_text_sensor).publish_state("Not Float Charging");

              }

              else  {

                id(ampinvt_floatchg_status_text_sensor).publish_state("Float Charging");

              }

    - name: "Charge Current Limit Status"

      id: ampinvt_chgcurrentlimit_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_chgcurrentlimit_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_chgcurrentlimit_status_text_sensor).publish_state("Charge Current Limit");

              }

    - name: "Charge Derating Status"

      id: ampinvt_chgderating_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_chgderating_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_chgderating_status_text_sensor).publish_state("Charge Derated");

              }

    - name: "Remote Prohibit Charging Status"

      id: ampinvt_remoteprohibchg_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_remoteprohibchg_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_remoteprohibchg_status_text_sensor).publish_state("Remote denial");

              }

    - name: "Panel Overvoltage Status"

      id: ampinvt_pvovervolt_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_pvovervolt_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_pvovervolt_status_text_sensor).publish_state("PV Overvoltage");

              }

    - name: "Charging Output Relay Status"

      id: ampinvt_chgoutputrelay_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_chgoutputrelay_status_text_sensor).publish_state("Closed");

              }

              else  {

                id(ampinvt_chgoutputrelay_status_text_sensor).publish_state("Open");

              }

    - name: "Load Output Status"

      id: ampinvt_loadoutput_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_loadoutput_status_text_sensor).publish_state("Closed");

              }

              else  {

                id(ampinvt_loadoutput_status_text_sensor).publish_state("Open");

              }

    - name: "Fan Relay Status"

      id: ampinvt_fanrelay_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_fanrelay_status_text_sensor).publish_state("Closed");

              }

              else  {

                id(ampinvt_fanrelay_status_text_sensor).publish_state("Open");

              }

    - name: "Overcharge Protection Status"

      id: ampinvt_overchgprotect_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_overchgprotect_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_overchgprotect_status_text_sensor).publish_state("Overcharge Protection On");

              }

    - name: "Overvoltage Protection Status"

      id: ampinvt_overvoltprotect_status

      on_value:

        then:

          - lambda: |-

              if(x==0) {

                id(ampinvt_overvoltprotect_status_text_sensor).publish_state("Normal");

              }

              else  {

                id(ampinvt_overvoltprotect_status_text_sensor).publish_state("Overvoltage Protection On");

              }

    - name: "Solar Panel Voltage"

      id: ampinvt_pv_voltage

      unit_of_measurement: V

      accuracy_decimals: 1

      icon: mdi:flash

      filters:

        - multiply: 0.1

    - name: "Solar Battery Voltage"

      id: ampinvt_battery_voltage

      unit_of_measurement: V

      accuracy_decimals: 2

      icon: mdi:flash

      filters:

        - multiply: 0.01

    - name: "Solar Charge Current"

      id: ampinvt_charge_current

      unit_of_measurement: A

      accuracy_decimals: 2

      icon: mdi:current-dc

      filters:

      - multiply: 0.01

    - name: "Solar Controller Internal Temperature"

      id: ampinvt_mppt_temperature

      unit_of_measurement: °C

      accuracy_decimals: 1

      filters:

      - multiply: 0.1

    - name: "Solar Battery Temperature"

      id: ampinvt_battery_temperature

      unit_of_measurement: °C

      accuracy_decimals: 1

      filters:

        - multiply: 0.1

    - name: "Solar Daily Yield"

      id: ampinvt_today_yield

      unit_of_measurement: W

      accuracy_decimals: 1

      icon: mdi:solar-power

    - name: "Solar Total Yield"

      id: ampinvt_generation_total

      unit_of_measurement: KW

      device_class: energy #for use in energy dashboard

      state_class: total_increasing #for use in energy dashboard

      accuracy_decimals: 1

      icon: mdi:solar-power

      filters:

        - multiply: 0.001

  text_sensor:

  - platform: template

    name: "Ampinvt Operating Status"

    id: ampinvt_op_status_text_sensor

  - platform: template

    name: "Ampinvt Battery Status"

    id: ampinvt_battery_status_text_sensor

  - platform: template

    name: "Ampinvt Fan Status"

    id: ampinvt_fan_status_text_sensor

  - platform: template

    name: "Ampinvt Overheat Status"

    id: ampinvt_overheat_status_text_sensor

  - platform: template

    name: "Ampinvt DC Output Status"

    id: ampinvt_dcoutput_status_text_sensor

  - platform: template

    name: "Ampinvt Internal Temp #1 Status"

    id: ampinvt_inttemp1_status_text_sensor

  - platform: template

    name: "Ampinvt Internal Temp #2 Status"

    id: ampinvt_inttemp2_status_text_sensor

  - platform: template

    name: "Ampinvt External Temperature Status"

    id: ampinvt_exttemp_status_text_sensor

  - platform: template

    name: "Ampinvt Charging Status"

    id: ampinvt_chg_status_text_sensor

  - platform: template

    name: "Ampinvt Equalising Status"

    id: ampinvt_equalchg_status_text_sensor

  - platform: template

    name: "Ampinvt MPPT Tracking Status"

    id: ampinvt_track_status_text_sensor

  - platform: template

    name: "Ampinvt Float Charging Status"

    id: ampinvt_floatchg_status_text_sensor

  - platform: template

    name: "Ampinvt Charge Current Limit Status"

    id: ampinvt_chgcurrentlimit_status_text_sensor

  - platform: template

    name: "Ampinvt Charge Derating Status"

    id: ampinvt_chgderating_status_text_sensor

  - platform: template

    name: "Ampinvt Remote Prohib Status"

    id: ampinvt_remoteprohibchg_status_text_sensor

  - platform: template

    name: "Ampinvt Panel Overvoltage Status"

    id: ampinvt_pvovervolt_status_text_sensor

  - platform: template

    name: "Ampinvt Charging Output Relay Status"

    id: ampinvt_chgoutputrelay_status_text_sensor

  - platform: template

    name: "Ampinvt Load Output Status"

    id: ampinvt_loadoutput_status_text_sensor

  - platform: template

    name: "Ampinvt Fan Relay Status"

    id: ampinvt_fanrelay_status_text_sensor

  - platform: template

    name: "Ampinvt Overcharge Protection Status"

    id: ampinvt_overchgprotect_status_text_sensor

  - platform: template

    name: "Ampinvt Overvoltage Protection Status"

    id: ampinvt_overvoltprotect_status_text_sensor

  switch:

    - platform: restart

      name: "Solar Controller Restart"

      id: solar_controller_restart

In addition, I could not start with the command “!secret” before SSID, password and api key therefore removed.

when compiling I get only one following error:

INFO Reading configuration /config/esphome/ampinvt.yaml...
Failed config

time.homeassistant: [source /config/esphome/ampinvt.yaml:44]
  
  Component time.homeassistant requires component api.
  platform: homeassistant
  id: esptime
  on_time: 
    - seconds: 0,30
      then: 
        - uart.write: 
            - 1
            - 179
            - 1
            - 0
            - 0
            - 0
            - 0
            - 181

How can I fix this?
Thanks for the great project.

Resolved on Github.

is tomeone can help me on this topic ?

I have no comprehension of what you are asking. Did you post this in the correct thread?

Any idea if this would work with Renogy? I have a 48V 3500W 80A unit I sure would like to integrate into HA.

It is unlikely that this code would work as-is but the principle is the same. All I did for this project was take the work of others and adapt it for the Ampinvt units so you could do the same for Renogy.

Those Renogy units are popular though, I’d be surprised if someone hadn’t already done it in HACS or somewhere on Github.