Shelly 3EM Pro + PV shows wrong grid consumption

I have a Shelly 3EM Pro installed on the 3 main phases (L1, L2 and L3). After that the three phases are connected to my energy suppliers smart meter.

I get daily reports from that smart meter of how much electricity I consumed from the grid. The Shelly 3EM Pro is always off by ~10% or more.

I also have a PV connected which supplies energy over L3 (so only one phase).

Initially I thought I could use the Shelly “Total Active Energy” and “Total Active Energy Returned”. But that seems to be very very wrong and also it only updates once per minute to HA. The update interval shouldn’t matter much because the total number still gets tracked by the Shelly 3EM Pro. It only reports it back to HA every 60 seconds. Still the total numbers are wrong.

I then made a little script which is based on the “Total Active Power” sensor.
Basically I summed it up when its value was positive (meaning nothing gets exported to get grid, only imported from the grid) as well as when it was negative (that means I would currently export energy from my PV).

The values become more reasonable now but are still wrong compared to the smart meter of my energy supplier.

Templates.yaml:

- sensor:

    # Positive-only (import) from total power
    - name: "Shelly Total Active Power Import"
      unique_id: shelly_total_active_power_import
      unit_of_measurement: 'W'
      state_class: measurement
      device_class: power
      state: >
        {% set p = states('sensor.shellypro3em_9454c5b9b340_total_active_power') | float(0) %}
        {{ p if p > 0 else 0 }}
        
        
    - name: "Shelly Total Active Energy Import"
      state: "{{ states('sensor.shelly_total_active_power_import_name') }}"
      unit_of_measurement: "kWh"
      device_class: energy
      state_class: total_increasing
      

    # Negative-only (export) from total power (absolute value)
    - name: "Shelly Total Active Power Export"
      unique_id: shelly_total_active_power_export
      unit_of_measurement: 'W'
      state_class: measurement
      device_class: power
      state: >
        {% set p = states('sensor.shellypro3em_9454c5b9b340_total_active_power') | float(0) %}
        {{ -p if p < 0 else 0 }}
        
    - name: "Shelly Total Active Energy Export"
      state: "{{ states('sensor.shelly_total_active_power_export_name') }}"
      unit_of_measurement: "kWh"
      device_class: energy
      state_class: total_increasing

Does anyone know whats wrong? Do I have to change my script or is something wrong with my shelly?

I also have a smart plug connected to my PV but that doesn’t help for fixing my problem?

any idea here?

@phier this is my final script which is working fine for me.

Put this in configuration.yaml:


########## Sensor ##########
sensor:
# Shelly 3EM templates for 3-phase Net Import, Export and Consumption (if you have solar generation details)
# This uses the Shelly instantaneous power sensors to achieve the best possible accuracy.
# Shelly Sensors are:
#   sensor.shellypro3em_abcd1234_phase_a_active_power,
#   sensor.shellypro3em_abcd1234_phase_b_active_power,
#   sensor.shellypro3em_abcd1234_phase_c_active_power for the three phases.
# Solar generation in W is used to calculate consumption via sensor.power_solargen
# V1.0 Initial release by Uksa007
# V1.1 Add float(0) to Consumption template to stop log warnings.
# V1.2 Add Friendly names to Utility Meter sensors.
# V1.3 Remove negative spikes from power consumption due to different update timing of solar sensor.
# V1.4 Change round: 2 for small value users.
# M Changelog: My own changes
# M1.0 Set round to 4, integrate PV-Summation_Delivered to be used with sensor.power_solargen, add float for each phase (otherwise error).
# M1.1 Introduce performance and accuracy enhancements:
#      - Use of local 'set' variables to minimize repeated state lookups and improve performance.
#      - Accurate 'availability_template' using float(none) and is_number validation.
#      - Introduce a tolerance threshold (3W) in power_consumption logic to suppress jitter/noise when solar ≈ export.
#      - Improve clarity and robustness of export/consumption calculation by avoiding undefined states and false negatives.
#      - Remove early rounding in all sensors; final values are left raw to preserve precision for integration processing.
# M1.2 Remove integration of PV-Summation_Delivered and use shellypmminig3_1234abcd_power instead
# M1.3 Make tolerance dynamic but 1,5W at minimum
# M1.4 Make tolerance less dynamic by applying a fixed 2W tolerance but the tolerance only applies when solar generation is < 100W
#      - Please make sure if solar_val is being reported as positive or negative value and adapt sensor "power consumption" accordingly
  - platform: template
    sensors:

      # Calculates total power import (sum of 3 phases, only if active_power is > 0)
      power_import:
        friendly_name: "Power Import"
        unit_of_measurement: 'W'
        value_template: >-
          {% set a = states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(0) %}
          {% set b = states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(0) %}
          {% set c = states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(0) %}
          {% set total = a + b + c %}
          {{ total if total > 0 else 0 }}
        availability_template: >-
          {% set sensors = [
            states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(none)
          ] %}
          {{ sensors | select('is_number') | list | count == 3 }}

      # Calculates total power export (sum of 3 phases, only if active_power is < 0)
      power_export:
        friendly_name: "Power Export"
        unit_of_measurement: 'W'
        value_template: >-
          {% set a = states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(0) %}
          {% set b = states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(0) %}
          {% set c = states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(0) %}
          {% set total = a + b + c %}
          {{ (total * -1) if total < 0 else 0 }}
        availability_template: >-
          {% set sensors = [
            states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(none)
          ] %}
          {{ sensors | select('is_number') | list | count == 3 }}

      # Calculates real power consumption including solar generation and export
      # Sets a tolerance to try and mitgitate any sensor noise / lags / jitters
      # Make solar_val a positive number (the shelly will report the PV power as negative value)
      power_consumption:
        friendly_name: "Power Consumption"
        unit_of_measurement: 'W'
        value_template: >-
          {% set import_val = states('sensor.power_import') | float(0) %}
          {% set export_val = states('sensor.power_export') | float(0) %}
          {% set solar_val = (states('sensor.shellypmminig3_5432046f3b24_power') | float(0)) * -1 %}
          {% set delta = solar_val - export_val %}
          {% set tolerance = 2.0 if solar_val < 100 else 0 %}
          {% if delta < -tolerance %}
            {{ import_val + solar_val }}
          {% elif delta > tolerance %}
            {{ delta }}
          {% elif abs(delta) <= tolerance %}
            {{ 0 }}
          {% else %}
            {{ import_val + solar_val }}
          {% endif %}
  # Sensor for Riemann sum of energy import (W -> Wh)
  - platform: integration
    source: sensor.power_import
    name: energy_import_sum
    unit_prefix: k
    round: 4
    method: left
  # Sensor for Riemann sum of energy export (W -> Wh)
  - platform: integration
    source: sensor.power_export
    name: energy_export_sum
    unit_prefix: k
    round: 4
    method: left
  # Sensor for Riemann sum of energy consumption (W -> Wh)
  - platform: integration
    source: sensor.power_consumption
    name: energy_consumption_sum
    unit_prefix: k
    round: 4
    method: left
    
########## Utility Meter ##########
utility_meter:
  energy_import_daily:
    source: sensor.energy_import_sum
    name: Energy Import Daily
    cycle: daily
  energy_import_monthly:
    source: sensor.energy_import_sum
    name: Energy Import Monthly
    cycle: monthly
  energy_export_daily:
    source: sensor.energy_export_sum
    name: Energy Export Daily
    cycle: daily
  energy_export_monthly:
    source: sensor.energy_export_sum
    name: Energy Export Monthly
    cycle: monthly
  energy_consumption_daily:
    source: sensor.energy_consumption_sum
    name: Energy Consumption Daily
    cycle: daily
  energy_consumption_monthly:
    source: sensor.energy_consumption_sum
    name: Energy Consumption Monthly
    cycle: monthly

Use sensor.energy_import_daily and sensor.energy_export_daily together with your PV sensor for the energy dashboard.

Hello @zakazak,

thank you for your work on this script. It mainly works, but I stumbled upon a problem or two… maybe you are kind enough to help me out?

First of all, I don’t use a shelly to measure my PV production but I have a Hoymiles microinverter, and I use this custom integration GitHub - suaveolent/ha-hoymiles-wifi: Home Assistant custom component for Hoymiles DTUs and the HMS-XXXXW-2T microinverters to get the value via sensor “sensor.inverter_ac_power”.

Then I had to change the “power consumption” sensor, because most of the time it didn’t include import in the calculations.

So basically that part of the script:

      power_consumption:
        friendly_name: "Power Consumption"
        unit_of_measurement: 'W'
        value_template: >-
          {% set import_val = states('sensor.power_import') | float(0) %}
          {% set export_val = states('sensor.power_export') | float(0) %}
          {% set solar_val = (states('sensor.shellypmminig3_5432046f3b24_power') | float(0)) * -1 %}
          {% set delta = solar_val - export_val %}
          {% set tolerance = 2.0 if solar_val < 100 else 0 %}
          {% if delta < -tolerance %}
            {{ import_val + solar_val }}
          {% elif delta > tolerance %}
            {{ delta }}
          {% elif abs(delta) <= tolerance %}
            {{ 0 }}
          {% else %}
            {{ import_val + solar_val }}
          {% endif %}

now looks like this:

      power_consumption:
        friendly_name: "Power Consumption"
        unit_of_measurement: 'W'
        value_template: >-
          {% set import_val = states('sensor.power_import') | float(0) %}
          {% set export_val = states('sensor.power_export') | float(0) %}
          {% set solar_val = states('sensor.inverter_ac_power') | float(0) %}
          {% set delta = solar_val - export_val %}
          {% set tolerance = 2.0 if solar_val < 100 else 0 %}
          {% if delta < -tolerance %}
            {{ import_val + solar_val }}
          {% elif delta > tolerance %}
            {{ delta + import_val }}
          {% elif abs(delta) <= tolerance %}
            {{ delta + import_val }}
          {% else %}
            {{ import_val + solar_val }}
          {% endif %}

But now I have the problem that as soon as my microinverter stops producing power, all the consumption sensors become “Unavailable”.

Any help is appreciated.

Just for reference, this is the complete script:

########## Sensor ##########
# Shelly 3EM templates for 3-phase Net Import, Export and Consumption (if you have solar generation details)
# This uses the Shelly instantaneous power sensors to achieve the best possible accuracy.
# Shelly Sensors are:
#   sensor.shellypro3em_abcd1234_phase_a_active_power,
#   sensor.shellypro3em_abcd1234_phase_b_active_power,
#   sensor.shellypro3em_abcd1234_phase_c_active_power for the three phases.
# Solar generation in W is used to calculate consumption via sensor.power_solargen
# V1.0 Initial release by Uksa007
# V1.1 Add float(0) to Consumption template to stop log warnings.
# V1.2 Add Friendly names to Utility Meter sensors.
# V1.3 Remove negative spikes from power consumption due to different update timing of solar sensor.
# V1.4 Change round: 2 for small value users.
# M Changelog: My own changes
# M1.0 Set round to 4, integrate PV-Summation_Delivered to be used with sensor.power_solargen, add float for each phase (otherwise error).
# M1.1 Introduce performance and accuracy enhancements:
#      - Use of local 'set' variables to minimize repeated state lookups and improve performance.
#      - Accurate 'availability_template' using float(none) and is_number validation.
#      - Introduce a tolerance threshold (3W) in power_consumption logic to suppress jitter/noise when solar ≈ export.
#      - Improve clarity and robustness of export/consumption calculation by avoiding undefined states and false negatives.
#      - Remove early rounding in all sensors; final values are left raw to preserve precision for integration processing.
# M1.2 Remove integration of PV-Summation_Delivered and use shellypmminig3_1234abcd_power instead
# M1.3 Make tolerance dynamic but 1,5W at minimum
# M1.4 Make tolerance less dynamic by applying a fixed 2W tolerance but the tolerance only applies when solar generation is < 100W
#      - Please make sure if solar_val is being reported as positive or negative value and adapt sensor "power consumption" accordingly
sensor:
  - platform: template
    sensors:

      # Calculates total power import (sum of 3 phases, only if active_power is > 0)
      power_import:
        friendly_name: "Power Import"
        unit_of_measurement: 'W'
        value_template: >-
          {% set a = states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(0) %}
          {% set b = states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(0) %}
          {% set c = states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(0) %}
          {% set total = a + b + c %}
          {{ total if total > 0 else 0 }}
        availability_template: >-
          {% set sensors = [
            states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(none)
          ] %}
          {{ sensors | select('is_number') | list | count == 3 }}

      # Calculates total power export (sum of 3 phases, only if active_power is < 0)
      power_export:
        friendly_name: "Power Export"
        unit_of_measurement: 'W'
        value_template: >-
          {% set a = states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(0) %}
          {% set b = states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(0) %}
          {% set c = states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(0) %}
          {% set total = a + b + c %}
          {{ (total * -1) if total < 0 else 0 }}
        availability_template: >-
          {% set sensors = [
            states('sensor.shellypro3em_abcd1234_phase_a_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_b_active_power') | float(none),
            states('sensor.shellypro3em_abcd1234_phase_c_active_power') | float(none)
          ] %}
          {{ sensors | select('is_number') | list | count == 3 }}

      # Calculates real power consumption including solar generation and export
      # Sets a tolerance to try and mitgitate any sensor noise / lags / jitters
      # Make solar_val a positive number (the shelly will report the PV power as negative value)
      power_consumption:
        friendly_name: "Power Consumption"
        unit_of_measurement: 'W'
        value_template: >-
          {% set import_val = states('sensor.power_import') | float(0) %}
          {% set export_val = states('sensor.power_export') | float(0) %}
          {% set solar_val = states('sensor.inverter_ac_power') | float(0) %}
          {% set delta = solar_val - export_val %}
          {% set tolerance = 2.0 if solar_val < 100 else 0 %}
          {% if delta < -tolerance %}
            {{ import_val + solar_val }}
          {% elif delta > tolerance %}
            {{ delta + import_val }}
          {% elif abs(delta) <= tolerance %}
            {{ delta + import_val }}
          {% else %}
            {{ import_val + solar_val }}
          {% endif %}
  # Sensor for Riemann sum of energy import (W -> Wh)
  - platform: integration
    source: sensor.power_import
    name: energy_import_sum
    unit_prefix: k
    round: 4
    method: left
  # Sensor for Riemann sum of energy export (W -> Wh)
  - platform: integration
    source: sensor.power_export
    name: energy_export_sum
    unit_prefix: k
    round: 4
    method: left
  # Sensor for Riemann sum of energy consumption (W -> Wh)
  - platform: integration
    source: sensor.power_consumption
    name: energy_consumption_sum
    unit_prefix: k
    round: 4
    method: left
    
########## Utility Meter ##########
utility_meter:
  energy_import_daily:
    source: sensor.energy_import_sum
    name: Energy Import Daily
    cycle: daily
  energy_import_monthly:
    source: sensor.energy_import_sum
    name: Energy Import Monthly
    cycle: monthly
  energy_export_daily:
    source: sensor.energy_export_sum
    name: Energy Export Daily
    cycle: daily
  energy_export_monthly:
    source: sensor.energy_export_sum
    name: Energy Export Monthly
    cycle: monthly
  energy_consumption_daily:
    source: sensor.energy_consumption_sum
    name: Energy Consumption Daily
    cycle: daily
  energy_consumption_monthly:
    source: sensor.energy_consumption_sum
    name: Energy Consumption Monthly
    cycle: monthly

so is that device still kind of buggy and such a custom script is required? thanks