@juhacz I basically used your script with small changes (and translating it to english) since I can’t read the inverter but instead got a smartplug installed which shows the produced energy.
However, it shows me a wrong energy from grid value. I also ran it through ChatGPT and couldn’t find any issues.
Do you have an idea whats going on?
I think this might also be useful for a lot of other people in here.
The point of the script is to do net metering. If you don’t want net metering, dont use the script and use the actual shelly entities instead of the ones the script creates. Or am I missing something?
Here’s the solution that finally worked for me. It gives me the correct numbers and accurately reflects my solar energy consumption.
sensor:
- platform: template
sensors:
net_power_export_watts:
unique_id: net_power_export_watts
friendly_name: "Power to the grid (only positive, netted)"
device_class: power
unit_of_measurement: 'W'
value_template: >-
{% set a = states('sensor.shellypro3em_XXXX_phase_a_active_power') | float(0) %}
{% set b = states('sensor.shellypro3em_XXXX_phase_b_active_power') | float(0) %}
{% set c = states('sensor.shellypro3em_XXXX_phase_c_active_power') | float(0) %}
{% set total = a + b + c %}
{% if total < 0 %}
{{ total | abs | round(2) }}
{% else %}
0.0
{% endif %}
net_power_import_watts:
unique_id: net_power_import_watts
friendly_name: "Power from grid (only positive, netted)"
device_class: power
unit_of_measurement: "W"
value_template: >-
{% set a = states('sensor.shellypro3em_XXXX_phase_a_active_power') | float(0) %}
{% set b = states('sensor.shellypro3em_XXXX_phase_b_active_power') | float(0) %}
{% set c = states('sensor.shellypro3em_XXXX_phase_c_active_power') | float(0) %}
{% set total = a + b + c %}
{{ total if total > 0 else 0 }}
- platform: integration
name: "Total Energy Consumed From Grid kWh"
unique_id: total_energy_consumed_from_grid_kwh
source: sensor.net_power_import_watts
unit_prefix: k
round: 3
method: left
unit_time: h
- platform: integration
name: "Total Energy Sent To Grid kWh"
unique_id: total_energy_sent_to_grid_kwh
source: sensor.net_power_export_watts
unit_prefix: k
round: 2
method: left
unit_time: h
- platform: integration
name: "Total Solar Energy Produced kWh"
unique_id: total_solar_energy_produced_kwh
source: sensor.solar_production
unit_prefix: k
round: 2
method: left
unit_time: h
After testing a million ways and scripts to do this, I ended up with the following modified version of your initial script. I tried to add anything I change to the changelog so it is easier to follow changes in the future. My changes start with “M1.0”.
To me, this one is the most accurate and fail proof script so far.
Maybe you can give feedback on it?
# 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_1234abcd_phase_a_active_power,
# sensor.shellypro3em_1234abcd_phase_b_active_power,
# sensor.shellypro3em_1234abcd_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.
# 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_Delivred 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
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_1234abcd_phase_a_active_power') | float(0) %}
{% set b = states('sensor.shellypro3em_1234abcd_phase_b_active_power') | float(0) %}
{% set c = states('sensor.shellypro3em_1234abcd_phase_c_active_power') | float(0) %}
{% set total = a + b + c %}
{{ total if total > 0 else 0 }}
availability_template: >-
{% set sensors = [
states('sensor.shellypro3em_1234abcd_phase_a_active_power') | float(none),
states('sensor.shellypro3em_1234abcd_phase_b_active_power') | float(none),
states('sensor.shellypro3em_1234abcd_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_1234abcd_phase_a_active_power') | float(0) %}
{% set b = states('sensor.shellypro3em_1234abcd_phase_b_active_power') | float(0) %}
{% set c = states('sensor.shellypro3em_1234abcd_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_1234abcd_phase_a_active_power') | float(none),
states('sensor.shellypro3em_1234abcd_phase_b_active_power') | float(none),
states('sensor.shellypro3em_1234abcd_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
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_1234abcd_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 }}
{% 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:
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
Thank You!
I had many versions of this script, and each of them wasn’t stable enough with solar production. Your version was almost perfect for me.
I deleted the availability_template because I have microinverters, and sometimes one of the micros doesn’t respond for 30 seconds. With availability_template, I had issues, so I prefer just using float(0).
I also added a 2% threshold filter on import and export.
sensor:
- platform: template
sensors:
# 1) SOLAR GENERATION POWER SUM
power_solargen:
friendly_name: "Solar Generation Power"
unit_of_measurement: 'W'
value_template: >-
# Defining a list of power readings from various solar panels.
{% set readings = [
states('sensor.dom_wschod_1_power') | float(0),
states('sensor.dom_wschod_2_power') | float(0),
states('sensor.dom_wschod_3_power') | float(0),
states('sensor.faplab_wschod_power') | float(0),
states('sensor.faplab_zachod_power') | float(0)
] %}
# Returning the sum of all readings.
{{ readings | sum }}
# 2) TOTAL IMPORT (sum of all three phases, only if > threshold)
power_import:
friendly_name: "Power Import"
unit_of_measurement: 'W'
value_template: >-
# Get the power consumption of each phase.
{% set a = states('sensor.shellyem3_c45bbe6c0c8a_channel_a_power') | float(0) %}
{% set b = states('sensor.shellyem3_c45bbe6c0c8a_channel_b_power') | float(0) %}
{% set c = states('sensor.shellyem3_c45bbe6c0c8a_channel_c_power') | float(0) %}
# Calculate the total power usage across all three phases.
{% set total = a + b + c %}
# Get the current solar generation power.
{% set solar_val = states('sensor.power_solargen') | float(0) %}
# Set the threshold based on solar generation (if less than 100W, threshold is 2W).
{% set threshold = 2.0 if solar_val < 100 else 0 %}
# Only return total power usage if it exceeds the threshold, else return 0.
{% if total > threshold %}
{{ total }}
{% else %}
0
{% endif %}
# 3) TOTAL EXPORT (sum of all three phases, only if < -threshold)
power_export:
friendly_name: "Power Export"
unit_of_measurement: 'W'
value_template: >-
# Get the power consumption of each phase.
{% set a = states('sensor.shellyem3_c45bbe6c0c8a_channel_a_power') | float(0) %}
{% set b = states('sensor.shellyem3_c45bbe6c0c8a_channel_b_power') | float(0) %}
{% set c = states('sensor.shellyem3_c45bbe6c0c8a_channel_c_power') | float(0) %}
# Calculate the total power usage across all three phases.
{% set total = a + b + c %}
# Get the current solar generation power.
{% set solar_val = states('sensor.power_solargen') | float(0) %}
# Set the threshold based on solar generation (if less than 100W, threshold is 2W).
{% set threshold = 2.0 if solar_val < 100 else 0 %}
# Only return total power export if it exceeds the negative threshold, else return 0.
{% if total < -threshold %}
{{ (-total) }}
{% else %}
0
{% endif %}
# 4) NET CONSUMPTION: import + solar – export, with dynamic tolerance buffer
power_consumption:
friendly_name: "Power Consumption"
unit_of_measurement: 'W'
value_template: >-
# Get the total power import.
{% set import_val = states('sensor.power_import') | float(0) %}
# Get the total power export.
{% set export_val = states('sensor.power_export') | float(0) %}
# Get the current solar generation power.
{% set solar_val = states('sensor.power_solargen') | float(0) %}
# Calculate the difference between solar generation and export.
{% set delta = solar_val - export_val %}
# Set the tolerance value based on solar generation (if < 100W, tolerance is 2W).
{% set tol = 2.0 if solar_val < 100 else 0 %}
# Logic for power consumption based on the difference and tolerance.
{% if delta < -tol %}
# If delta is negative and exceeds tolerance, show import value + solar.
{{ import_val + solar_val }}
{% elif delta > tol %}
# If delta is positive and exceeds tolerance, show the delta.
{{ delta }}
{% elif delta | abs <= tol %}
# If delta is within tolerance, return 0.
0
{% else %}
# If no other condition is met, show import value + solar.
{{ import_val + solar_val }}
{% endif %}
#####################################################################
# INTEGRATION SENSORS - total usage/export (Riemann sum)
#####################################################################
- platform: integration
source: sensor.power_import
name: energy_import_sum
unit_prefix: k
round: 2
method: left
- platform: integration
source: sensor.power_export
name: energy_export_sum
unit_prefix: k
round: 2
method: left
- platform: integration
source: sensor.power_consumption
name: energy_consumption_sum
unit_prefix: k
round: 2
method: left
#####################################################################
# UTILITY METER - daily and monthly cycles
#####################################################################
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
To filter out noise Unfortunately, your power consumption logic doesn’t work in my case (see the screenshot), so I’m back to version one million and one of this script
Was using this template sensor for quite a while…now I added a SolarFlow battery system and added the sensors sensor.solarflow_input and sensor.solarflow_output like:
So that the power consumption subtracts the power when the battery is charged, or adds when it is discharged.
Works fine, but at one occasion when the battery is charging and sensor.power_import is > 0 it adds the battery charing power to the consumption, which is really odd as I only subtract it.
Hi community!
Has someone already migrated the V1.4 script into the new template format for 2025.12? I’ve tried but failed because I don’t know how to adjust the rest of my configuration.yml accordingly.