Hello,
I’m not really good at programming but would like to share my smart meter that I created via Home Assistant.
My setup is a Growatt Inverter 600TL-X with a ShineWifi-X stick running ESPHome.
All energy is monitored by a Shelly3EM and a Shelly1PM (inverter).
The inverter is configured to produce 2000W - yes, it’s a 600W inverter, but who cares?
The steps are 1-100, which corresponds to 20W per step.
The output power of the inverter should always be 60W above the actual consumption.
If the consumption is less than 100W, the inverter should always generate 120W (equivalent to 6).
Over 2000W consumption = set inverter to 100 - maximum power, regardless of whether there is enough sun or not.
Also, the automation only triggers every 10 seconds because the inverter is slow and can’t ramp up and down very quickly.
The ShineWifi-X Stick is flashed with ESPHome, this is the current Yaml code:
ESPHome YAML Code
substitutions:
devicename: growattwr
upper_devicename: growattwr-stick
esphome:
name: $devicename
platform: ESP8266
board: esp07s
#esp8266_restore_from_flash: true
on_boot:
priority: 600
then:
- delay: 10s
- number.set:
id: poweroutput
value: 35
logger:
baud_rate: 0
api:
encryption:
key: ""
ota:
password: ""
wifi:
ssid: "SSID"
password: ""
fast_connect: true
manual_ip:
static_ip: 192.168.178.138
gateway: 192.168.178.1
subnet: 255.255.255.0
dns1: 192.168.178.10
ap:
ssid: "Growatt Fallback Hotspot"
password: ""
captive_portal:
web_server:
port: 80
time:
- platform: homeassistant
id: homeassistant_time
output:
# Blue Led
- id: light_bl
platform: gpio
pin: 16
# Green Led
- id: light_gr
platform: gpio
pin: 0
# Red Led
- id: light_rd
platform: gpio
pin: 2
uart:
id: mod_bus
tx_pin: 1
rx_pin: 3
baud_rate: 115200
modbus:
id: modbus1
uart_id: mod_bus
flow_control_pin: GPIO4
modbus_controller:
- id: growatt
address: 0x1
modbus_id: modbus1
setup_priority: -10
update_interval: 10s
#command_throttle: 1s
text_sensor:
- platform: template
name: "${devicename} Status"
icon: mdi:eye
entity_category: diagnostic
lambda: |-
if (id(status).state == 1) {
return {"Normal"};
} else if (id(status).state == 0) {
return {"Waiting"};
} else {
return {"Fault!"};
}
button:
- platform: restart
name: "Stick Restart"
switch:
- platform: modbus_controller
name: "${devicename} OnOff"
address: 0
register_type: holding
number:
- platform: modbus_controller
name: "${devicename} Ausgangspower"
id: poweroutput
address: 3
value_type: U_WORD
min_value: 0
max_value: 100
entity_category: config
select:
- platform: modbus_controller
id: powermode
name: "${devicename} Power Mode"
address: 121
value_type: U_WORD
optionsmap:
"600": 6
"750": 7
"1000": 10
"1500": 15
"2000": 20
entity_category: config
skip_updates: 60
sensor:
- platform: wifi_signal
name: "${devicename} WiFi Signal"
update_interval: 360s
entity_category: diagnostic
- platform: modbus_controller
address: 0
register_type: "read"
internal: true
id: status
skip_updates: 6
- platform: modbus_controller
name: "${devicename} DC Power"
address: 3005 #5
register_type: "read"
unit_of_measurement: W
device_class: power
icon: mdi:flash
value_type: U_DWORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 3
- platform: modbus_controller
name: "${devicename} DC Volt"
address: 3003 #3
register_type: "read"
unit_of_measurement: V
device_class: voltage
icon: mdi:flash
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} DC Strom"
address: 3004 #4
register_type: "read"
unit_of_measurement: A
device_class: current
icon: mdi:flash
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} AC Power"
address: 3023 #40
register_type: "read"
unit_of_measurement: W
device_class: power
icon: mdi:flash
value_type: U_DWORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} AC Frequenz"
address: 3025 #37
register_type: "read"
unit_of_measurement: Hz
icon: mdi:flash
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.01
skip_updates: 6
- platform: modbus_controller
name: "${devicename} AC Volt"
address: 3026 #38
register_type: "read"
unit_of_measurement: V
device_class: voltage
icon: mdi:flash
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} AC Strom"
address: 3027 #39
register_type: "read"
unit_of_measurement: A
device_class: current
icon: mdi:flash
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} AC Power VA"
address: 3028 #40
register_type: "read"
unit_of_measurement: VA
icon: mdi:flash
value_type: U_DWORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} Energy Heute"
address: 3049 #53
register_type: "read"
unit_of_measurement: kWh
device_class: energy
icon: mdi:flash
value_type: U_DWORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} Energy Total"
address: 3051 #55
register_type: "read"
unit_of_measurement: kWh
state_class: total_increasing
device_class: energy
icon: mdi:flash
value_type: U_DWORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
- platform: modbus_controller
name: "${devicename} Temperatur"
address: 3093
register_type: "read"
unit_of_measurement: °C
device_class: temperature
icon: mdi:thermometer
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.1
skip_updates: 6
What does this Automation do ?
Show me....
action:
- service: number.set_value
target:
entity_id: number.growattwr_ausgangspower
data_template:
value: >
{% set current_consumption = states('sensor.aktuellerverbrauch') | int %}
{% set calculated_value = ((current_consumption + 60)) // 20 %}
{% if calculated_value > 100 %}
100
{% elif current_consumption < 100 %}
6
{% else %}
{{ calculated_value }}
{% endif %}
-
- Service:
number.set_value
indicates that the action is to set the value of a number entity.
- Service:
- Target:
entity_id: number.growattwr_ausgangspower
specifies the entity whose value will be set. In this case, it’s likely a number entity representing the output power of a device. - Data Template: This section allows dynamic data manipulation based on the current state of other entities. It calculates a new value for the
number.growattwr_ausgangspower
entity based on the current consumption (sensor.aktuellerverbrauch
).
- Data Template Explanation: Let’s break down what the data template does:
- Setting Variables: It sets two variables:
current_consumption
: Extracts the current consumption fromsensor.aktuellerverbrauch
and converts it to an integer.calculated_value
: Calculates a new value based on the current consumption.
- Calculating New Value:
calculated_value
is computed by adding 60 to the current consumption and then dividing by 20. This seems to represent some kind of scaling operation.
- Conditional Output:
- If the calculated value exceeds 100, it caps it at 100.
- If the current consumption is less than 100, it sets the value to 6.
- Otherwise, it uses the calculated value.
Now, let’s do some math examples with different power consumptions:
- For 80W:
calculated_value = ((80 + 60) // 20) = 140 // 20 = 7
- Since 7 is less than 100, the output will be 7.
- For 1200W:
calculated_value = ((1200 + 60) // 20) = 1260 // 20 = 63
- Since 63 is less 100, the output will be 63.
- For 2500W:
calculated_value = ((2500 + 60) // 20) = 2560 // 20 = 128
- Since 128 exceeds 100, the output will be capped at 100.
Here is the full YAML:
Full Automation YAML
alias: 00// Test Smart Meter Growatt ESPHome / Shelly3EM
description: ""
trigger:
- platform: time_pattern
seconds: /10
alias: query every 10s
condition:
- condition: and
conditions:
- condition: sun
after: sunrise
before: sunset
after_offset: "+00:30:00"
alias: only trigger 30Min after sunrise and stop at sunset
- condition: template
value_template: "{{ states('number.growattwr_ausgangspower') != 'unavailable' }}"
alias: if Growatt is 'unavailable' dont set - (offline)
- condition: template
value_template: >
{{ states('number.growattwr_ausgangspower') | int !=
((states('sensor.aktuellerverbrauch') | int + 60)) // 20 }}
alias: >-
test whether “number.growattwr_ausgangspower” corresponds to the value
to be set
alias: test if 3 conditions match
action:
- service: number.set_value
target:
entity_id: number.growattwr_ausgangspower
data_template:
value: >
{% set current_consumption = states('sensor.aktuellerverbrauch') | int
%} {% set calculated_value = ((current_consumption + 60)) // 20%} {% if
calculated_value > 100 %}
100
{% elif current_consumption < 100 %}
6
{% else %}
{{ calculated_value }}
{% endif %}
alias: Calculation template for setting number.growattwr_ausgangspower
trace:
stored_traces: 10
Sensors i use, written in the external sensor.yaml
# Sensor for Riemann sum of energy import (W -> Wh)
- platform: integration
source: sensor.aktuellerimport
name: Import
unit_prefix: k
round: 2
method: left
# Sensor for Riemann sum of energy export (W -> Wh)
- platform: integration
source: sensor.aktuellerexport
name: Export
unit_prefix: k
round: 2
method: left
# Sensor for Riemann sum of energy consumption (W -> Wh)
- platform: integration
source: sensor.aktuellerverbrauch
name: Verbrauch
unit_prefix: k
round: 2
method: left
Templates for the Shelly3EM - external template.yaml
- sensor:
#IMPORT SENSOR
- name: aktuellerimport
unit_of_measurement: W
state_class: measurement
device_class: power
unique_id: aktuellerimport
state:
"{{ max(0,states('sensor.shelly3em_channel_a_power')|float +
states('sensor.shelly3em_channel_b_power')|float +
states('sensor.shelly3em_channel_c_power')|float )}}"
availability:
"{{ [ states('sensor.shelly3em_channel_a_power'),
states('sensor.shelly3em_channel_b_power'),
states('sensor.shelly3em_channel_c_power') ] | map('is_number') | min }}"
#EXPORT SENSOR
- name: aktuellerexport
unit_of_measurement: W
state_class: measurement
device_class: power
unique_id: aktuellerexport
state:
"{{ min(0,states('sensor.shelly3em_channel_a_power')|float +
states('sensor.shelly3em_channel_b_power')|float +
states('sensor.shelly3em_channel_c_power')|float)|abs}}"
availability:
"{{ [ states('sensor.shelly3em_channel_a_power'),
states('sensor.shelly3em_channel_b_power'),
states('sensor.shelly3em_channel_c_power') ] | map('is_number') | min }}"
#VERBRAUCH SENSOR
- name: aktuellerverbrauch
unit_of_measurement: W
state_class: measurement
device_class: power
unique_id: aktuellerverbrauch
state: "{{ states('sensor.sum_channel_shelly')|float + states('sensor.shellypv_power')|float}}"
availability:
"{{ [ states('sensor.sum_channel_shelly'), states('sensor.shellypv_power') ]
| map('is_number') | min }}"
If anyone has any ideas to improve the code, please let me know what you think… I’m a beginner and just proud of my first setup
If I’ve forgotten anything, let me know.