Due to the big rise of power prices in germany, I now changed my meter setup to self-use solar energy (which is extra funded by german government), and got here 2 brand new DZG DWSB10.2 bidirectional powermeters with IR based SML interface from our power supplier, hope my config helps someone else.
Though these meters have a pulsed LED, I discovered it’s worthless, because it cannot tell if the power is entering or leaving the house, so my old pulsecounter is not usable anymore.
To get the meters talking more frequently, they can be set to report all measurements every second, I needed a pin to unlock the meters menu, my very friendly power company gave them to me together with the meters manual.
To get the serial data, I use 2 IR heads from Ebay
3D printed cases found at Thingiverse, modified a bit to match the meters and my magnets, and a ESP32 dev board.
Had an unused I2C display from AZdelivery, which looks pretty good in the cabinet of the meters.
Used an ESP32, it has 2 usable hardware serial ports, here’s the esphome config to read out 2 meters, TX and RX are incorrectly printed on the IR heads, so had to swap the pins in software:
substitutions:
name: smlreader
esphome:
name: ${name}
platform: ESP32
board: nodemcu-32s
<<: !include _common/global.yaml
time:
- platform: sntp
id: sntp_time
servers: timeserver.in.edevau.net
external_components:
- source: github://alengwenus/esphome_components@main
refresh: 0s
components: [sml]
i2c:
sda: GPIO21
scl: GPIO22
font:
- file: "fonts/hd44780.ttf"
id: font1
size: 8
display:
- platform: ssd1306_i2c
model: "SH1106 128x64"
# no model: "SH1107 128x64"
# no model: "SH1106 128x32"
# no model: "SSD1306 128x64"
#reset_pin: D0
address: 0x3C
lambda: |-
auto red = Color(255, 0, 0);
it.printf( 64, 0, id(font1), TextAlign::TOP_CENTER, "${name}");
it.line(0, 9, 128, 9, red);
it.printf( 5, 12, id(font1), "Import %8.2f kWh", id(evu_import).state);
it.printf( 5, 21, id(font1), "Export %8.2f kWh", id(evu_export).state);
it.line(0, 30, 128, 30, red);
it.printf( 2, 33, id(font1), "PV:");
it.printf( 5, 42, id(font1), "Import %8.2f kWh", id(pv_import).state);
it.line(0, 52, 128, 52, red);
it.line(42, 9, 42, 52, red);
it.strftime(64, 56, id(font1), TextAlign::TOP_CENTER, "%Y-%m-%d %H:%M", id(sntp_time).now());
uart:
- id: uart_bus0
# TX: 1, RX: 3 (reversed)
tx_pin: GPIO3
rx_pin: GPIO1
baud_rate: 9600
rx_buffer_size: 2048
data_bits: 8
stop_bits: 1
parity: NONE
- id: uart_bus2
# TX:17, RX: 16 (reversed)
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 9600
rx_buffer_size: 2048
data_bits: 8
stop_bits: 1
parity: NONE
sml:
- id: sml_pv
uart_id: uart_bus0
- id: sml_evu
uart_id: uart_bus2
sensor:
- platform: sml
name: "evu_import"
id: "evu_import"
sml_id: sml_evu
obis_code: "1-0:1.8.0"
unit_of_measurement: kWh
state_class: total_increasing
device_class: energy
filters:
- multiply: 0.0001
accuracy_decimals: 4
force_update: true
- platform: sml
name: "evu_WhImported"
id: "evu_WhImported"
sml_id: sml_evu
obis_code: "1-0:1.8.0"
unit_of_measurement: Wh
state_class: total_increasing
device_class: energy
filters:
- multiply: 0.1
accuracy_decimals: 1
force_update: true
- platform: sml
name: "evu_export"
id: "evu_export"
sml_id: sml_evu
obis_code: "1-0:2.8.0"
unit_of_measurement: kWh
state_class: total_increasing
device_class: energy
filters:
- multiply: 0.0001
accuracy_decimals: 4
force_update: true
- platform: sml
name: "evu_WhExported"
id: "evu_WhExported"
sml_id: sml_evu
obis_code: "1-0:2.8.0"
unit_of_measurement: Wh
state_class: total_increasing
device_class: energy
filters:
- multiply: 0.1
accuracy_decimals: 1
force_update: true
- platform: sml
name: "pv_import"
id: "pv_import"
sml_id: sml_pv
obis_code: "1-0:2.8.0"
unit_of_measurement: kWh
state_class: total_increasing
device_class: energy
accuracy_decimals: 4
filters:
- multiply: 0.0001
force_update: true
- platform: sml
name: "pv_WhImported"
id: "pv_WhImported"
sml_id: sml_pv
obis_code: "1-0:2.8.0"
unit_of_measurement: Wh
state_class: total_increasing
device_class: energy
filters:
- multiply: 0.1
accuracy_decimals: 1
force_update: true
# - platform: sml
# name: "evu_leistung"
# id: "evu_leistung"
# sml_id: sml_evu
# obis_code: "1-0:16.7.0"
# filters:
# - multiply: 0.001
# unit_of_measurement: W
# state_class: measurement
# device_class: power
# - platform: sml
# name: "pv_leistung"
# id: "pv_leistung"
# sml_id: sml_pv
# obis_code: "1-0:16.7.0"
# filters:
# - multiply: 0.001
# unit_of_measurement: W
# state_class: measurement
# device_class: power
# - platform: sml
# name: "pv_p_eff"
# id: "pv_p_eff"
# sml_id: sml_pv
# obis_code: "1-0:16.7.0"
# filters:
# - multiply: 0.01
# unit_of_measurement: W
# state_class: measurement
# device_class: power
- platform: sml
name: "evu_p_eff"
id: "evu_p_eff"
sml_id: sml_evu
obis_code: "1-0:16.7.0"
filters:
- multiply: 0.01
unit_of_measurement: W
state_class: measurement
device_class: power
One meter counts import solar energy, the second meter measures import and export from the power company. state_class
and device_class
are required to make the entities show up in Home Assistants Energy Dashboard.
The values are also published on MQTT (from Home Assistant) to tell the Wallbox if we have unused energy (to charge the car). These values for the MQTT bridge to Openwb are mostly calculated by template sensors, at this point, force_update: true
is very important, to avoid the template sensors get stuck at the last old measurement if one entity is not updated for longer, e.g. the evu import counter during sunshine.
Getting the power from the absolute counters is easy then using derivative
platform:
sensor:
#openWB/set/evu/W Bezugsleistung in Watt, int, positiv Bezug, negativ Einspeisung
- platform: derivative
name: bezugsleistung
source: sensor.evu_whimported
round: 0
unit: W
time_window: 00:00:10
unit_time: h
- platform: derivative
name: einspeiseleistung
source: sensor.evu_whexported
round: 0
unit: W
time_window: 00:00:10
unit_time: h
- platform: template
sensors:
#openWB/set/evu/W Bezugsleistung in Watt, int, positiv Bezug, negativ Einspeisung
bezugsleistung4openwb:
friendly_name: 'W'
value_template: "{{ (states('sensor.bezugsleistung')) | int(0) - (states('sensor.einspeiseleistung')) | int(0) }}"
Here I have the one automation to update the MQTT from my template sensors, some values from a SDM630 modbus meter:
automation:
- alias: 'openWB Publish calculated sensor values'
mode: parallel
trigger:
platform: state
entity_id: sensor.aphase1, sensor.aphase2, sensor.aphase3, sensor.bezugsleistung4openwb, sensor.evu_whimported, sensor.smlwhexported4openwb, sensor.smll1volts4openwb, sensor.smll2volts4openwb, sensor.smll3volts4openwb, sensor.smlfrequency4openwb
action:
service: mqtt.publish
data_template:
payload: "{{trigger.to_state.state}}"
topic: "openWB/openWB/set/evu/{{trigger.to_state.name}}"
I (mis-)use the ‘name’ attribute to address the correct target MQTT topic, because it is case sensitive, and the wallbox expects the values exactly there.
As soon as the sun comes out, the cars starts charging with the unused current, unfortunately in this example the dishwasher is running in parallel, the blue line (car) tries to keep the EVU current in balance:
For better long term analysis, I also store all measurements in InfluxDB: