Notes on working with live data from Enphase systems

Starting a new topic with a clean slate because many of the existing ones covering Enphase are hundreds of messages long and this doesn’t seem to fit any of them… Enphase systems are a bit of a pain to work with because to get the data you want there’s a range of different controllers, you need to have all the appropriate CT sensors fitted, and then you need the controller that you’re using to make the sensor data available rather than keep it to itself.

However there’s another source for the information which is straight from the inverters. Unfortunately unlike systems like Fronius and Goodwe there’s no single source of truth present at the controller but just a lot of data from individual inverters.

To deal with this, I created some template sensors that take the necessary data from each subsystem and combine them into composite sensors that report the overall state, e.g. power coming from solar panels or power to/from batteries.

In case this is of use to anyone, here’s the setup, just edit the sensor lists to match your inverters and copy into the packages directory with a name like enphase.yaml.

Some notes, the batteries I have do actually provide a total power reading so there was no need to enumerate inverters on each one. Also the sensors won’t show up in the Energy dashboard because it doesn’t work with state_class: measurement sensors so you’ll need to use something like Power Flow Card Plus to display the flows.

If enough people are interested, I’ll post a slightly fancier version that shows solar flow by array rather than all-in-one so you can see flows from all your north-facing panels, all your east-facing panels, … as well as the overall totals, although I’m still playing with Power Flow Card Plus to see if it can show flows from multiple sets of panels.

# Template sensors to combine all Enphase batteries and all Enphase solar
# panels into one composite value.  These are usable with Power Flow Card
# Plus but not the default Energy dashboard because it doesn't recognise
# 'state_class: measurement' sensors, we'd need to convert them to
# 'state_class: total' sensors for that.

  # Note double indentation step for sensor: -> name:
  - sensor:
      # Power in W, charge = -ve, discharge = +ve.
      - name: "Enphase Battery Total"
        unique_id: uniqueid__enphase_battery_total
        unit_of_measurement: "W"
        device_class: energy
        state_class: measurement
        state: >
          {% set battery1 = states('sensor.encharge_492****_power') | float %}
          {% set battery2 = states('sensor.encharge_492****_power') | float %}
          {{ ( battery1 + battery2 ) | round(0) }}

  - sensor:
      # This is a bit of an odd one because we're taking the average of two
      # technically independent values, however since they're charged and
      # discharged in sync they should always be within 1% of each other.
      # Which means in theory we could just take the state of one battery as
      # being representative of both.
      - name: "Enphase Battery Charge State"
        unique_id: uniqueid__enphase_battery_charge_state
        unit_of_measurement: "%"
        device_class: battery
        state_class: measurement
        state: >
          {% set battery1 = states('sensor.encharge_492****_battery') | float %}
          {% set battery2 = states('sensor.encharge_492****_battery') | float %}
          {{ ( ( battery1 + battery2 ) / 2 ) | round(0) }}

  - sensor:
      - name: "Enphase Solar Total"
        unique_id: uniqueid__enphase_solar_total
        unit_of_measurement: "W"
        device_class: energy
        state_class: measurement
        # In theory we could get fancy here and add a
        # '{% if is_state('sun.sun', 'above_horizon') %}' check if we get
        # glitch readings at night but this doesn't seem necessary at the
        # moment.
        state: >
          {% set solar1 = states('sensor.inverter_1222****') | float %}
          {% set solar2 = states('sensor.inverter_1222****') | float %}
          {% set solar3 = states('sensor.inverter_1222****') | float %}
          {% set solar4 = states('sensor.inverter_1222****') | float %}
          {% set solar5 = states('sensor.inverter_1222****') | float %}
          {% set solar6 = states('sensor.inverter_1222****') | float %}
          {% set solar7 = states('sensor.inverter_1222****') | float %}
          {% set solar8 = states('sensor.inverter_1222****') | float %}
          {% set solar9 = states('sensor.inverter_1222****') | float %}
          {% set solar10 = states('sensor.inverter_1222****') | float %}
          {% set solar11 = states('sensor.inverter_1222****') | float %}
          {% set solar12 = states('sensor.inverter_1222****') | float %}
          {{ ( solar1 + solar2 + solar3 + solar4 + solar5 + solar6 + solar7 + solar8 + solar9 + solar10 + solar11 + solar12 ) | round(0) }}