Engelmann Sensostar Heat Meter Integration with ESPHome

I’d like to invite everyone to vote/comment on this Home Assistant feature request:

Checking if it’s compatible.
You can find the Sensostar U/E in several different versions in shops. The only versions that work are the ones with the “Modul” string on the top edge of the front cover.

Wouldnt it make sense to build hourly averages and calculate an hourly COP. I dont think minute COPs make any sense. COP granularity per day should be good enough. Hourly is already quite nerdy :smile:

I do only calculate daily, monthly and yearly COP values. The diagram was just a visualization of the issue.

@brambo123 created a new branch, in which the energy is calculated on the ESP with a higher resolution now: GitHub - brambo123/esphome-sensostar at sub-integer-energy

Thus, the issue is solved for me. :slight_smile:

The second batch of PCBs arrived today. Shipping will begin after the New Year.

I would like to wish you all a happy New Year!

Good news! I can’t wait to test it next month! Michael

What type of JST connector is required for power supply?

Housing
JST PH2P BU

Crimp Contact
JST PH CKB

2mm pitch.

So well just wait for your email with the payment info right? Cant wait to test it :slight_smile:

Yes, I’m currently crimping the cables and spot welding the batteries, etc.
I will send out a bunch of emails every time I have a sufficient amount of accessories. I hope that by the end of January, everything will have been shipped.

Dirk.

Hi Dirk,

I am happy to share that I have received my package (PCB, PowerDongle, Battery and USB-Cable) in good condition.
Have fitted it to my Sensostar yesterday and it is working like a charm.

Thank you very much for all your effort and your excellent work!!!

Ole

The second batch of orders has now been fully processed. Anyone who ordered an adapter should have received an email containing payment information.

All orders paid for by today have already been shipped.

Any orders that have not been paid for within 10 days will be automatically cancelled. There are some people on the waiting list. I will offer these adapters to them.

Regards,

Dirk

The PCB has arrived today and I installed it instantly.
It works like a charm!

Many Thanks to @STB3 for the good work.

I have an Brummerhoop F90U3 Sensor which comes from the same manufacturer as the Sensostar.

Home Assistant integration also worked immediately.

Thanks for the fast delivery. The PCB arrived today. Installation was easy. Your documentation is perfect! I can confirm: it works also well with “Wassergeräte GmbH - Volumess VI-E”. they seem to use the same chinese manufacturer as Sensostar and Brummerhoop. :slight_smile:

Thanks for the board. Easy to install with your instruction, took just 10 minutes. Most struggle was about opening the plastic case :wink:
I can confirm that it works with a “Molline Wingman C3” which is obviously a rebranded Engelmann Sensostar.
Thanks a lot for this fantastic project !
Uwe

Dirk, thank You very much. Installation worked fine and I was very much surprised to see the values immediately in my HomeAssistant System! Very nice and many thanks!!
Roland

Hello Dirk,

I converted the Sensostar today. The ESP does not seem to be receiving any communication. I previously had the MBUS module and everything was fine. Do you have any ideas? I am using sensostart_red.yaml in ESPHOME.

Michael

Yes, it looks like there is no data coming from the heat meter as is.
Did you flash modifications to the board or is it the stock firmware it came with?
Could you post a picture of the MBUS module? I’d like to compare it with mine to see if there are any differences.

Is the red LED blinking?

Regards,

Dirk

Hi Dirk,
No, it’s not the firmware you played. I compiled it with the following yaml using esphome 2025.12.6. The red LED is not flashing.

# Flash using --device COMXX otherwise it is difficult to flash using USB
esphome:
  name: sensostar-v2
  name_add_mac_suffix: false
  comment: SensoStar Energy Meter
  platformio_options:
    board_build.arduino.memory_type: qio_opi
  on_boot:
    then:
      - mqtt.disable:
      - output.turn_off: adc_control  # IO14 LOW at boot
      - script.execute: read_adc_script  # Immediately read ADC once at boot
      - lambda: |-
          if (!id(wifi_connected)) {
            id(ap_mode_blink).execute();
          }
#          id(api_key_text).publish_state("${api_key_display}");    # uncommet if you want to use an api key   
    
esp32:
  variant: esp32c6
  framework:
    type: esp-idf
    
wifi:
# Uncomment this if you have ented your credentials in the secrets.yaml
# anyway at least the api_encryption_key: section must be present in the secrets.yaml
  use_address: esphome-sensostar-xxxx.fritz.box
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: NONE
  ap:
    ssid: "Sensostar"
  reboot_timeout: 0s 
  
  on_connect:
    - script.stop: ap_mode_blink
    - output.turn_on: wifi_led
    - lambda: |-
       if (id(mqtt_enabled).state) {
         ESP_LOGI("wifi", "Wi-Fi connected and MQTT switch is ON — enabling MQTT");
         id(mqtt_broker).enable();
       }
       
  on_disconnect:
    - script.execute: ap_mode_blink
    
captive_portal:

logger:
  level: info
#  hardware_uart: UART0
#  baud_rate: 115200

api:    
  encryption:                                                              # uncommet if you want to use an api key  
    key: "GzGxxxxxxxxxxxxxxxLJZkW40+Qok=" # create use openssl rand -base64 32   # uncommet if you want to use an api key  
  reboot_timeout: 600min
  
ota:
  platform: esphome
  password: "8xxxxxxxxxxxx5d5a"
    
web_server:
  version: 3
  port: 80

    
external_components:
#  - source: github://pr#8103
#    components: [uart]
  - source:
      #type: local
      #path: !secret local_path
      type: git                                         # Be careful ESPhome is caching the repository. Do the following to update the cache
      url: https://github.com/STB3/esphome-sensostar    # Deleting the .esphome/external_components/ cache, or 
      ref: main  # or a specific commit SHA             # Using a ref: to pin to a specific commit or branch.
    components: [ SensoStar_MBus ]

time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/Amsterdam
    servers: ["0.nl.pool.ntp.org", "1.nl.pool.ntp.org", "2.nl.pool.ntp.org"]

uart:
  - id: uart_mbus
    tx_pin: GPIO6
    rx_pin: GPIO7
    baud_rate: 2400
    parity: EVEN
    
SensoStar_MBus:
  id: sensostar_id
  uart_id: uart_mbus
  update_interval: 10s
  data_led: mbus_data_led

sensor:
  - platform: SensoStar_MBus
    energy:
      name: "SensoStar Energy"
    volume:
      name: "SensoStar Volume"
    power:
      name: "SensoStar Power"
#      on_value:
#        then:
#          - output.turn_on: mbus_yellow_led
#          - delay: 200ms
#          - output.turn_off: mbus_yellow_led
    flow:
      name: "SensoStar Flow"
    temperature_flow:
      name: "SensoStar Flow Temperature"
    temperature_return:
      name: "SensoStar Return Temperature"
    temperature_diff:
      name: "SensoStar Temperature Difference"
    calculated_power:
      name: "SensoStar Calculated Power"
      
  - platform: adc
    pin: GPIO1
    id: adc_input_sensor
    name: "SensoStar Battery Level"
    update_interval: never  # done by interval:
    filters:
      - lambda: return x * 3.6; # Vbat is measured via a resistive divider 39k--o--15k

  - platform: wifi_signal
    name: "SensoStar WiFi Signal"
    id: wifi_signal_sensor
    update_interval: 60s      

substitutions:
  api_key_display: "GzGXiZFbY8xslEQVoSnEZdwT7Fcwh80bLJZkW40+Qok=" #!secret api_encryption_key

text_sensor:
  - platform: SensoStar_MBus
    status:
      name: "SensoStar Status"
      
# Show API encryption key in the web interface    
#  - platform: template                         # uncommet if you want to use an api key  
#    name: "API Encryption Key"                 # uncommet if you want to use an api key  
#    id: api_key_text                           # uncommet if you want to use an api key  
#    entity_category: config                    # uncommet if you want to use an api key  
#    lambda: |-                                 # uncommet if you want to use an api key  
#      return std::string("${api_key_display}");# uncommet if you want to use an api key  
#    update_interval: never                     # uncommet if you want to use an api key  

output:
  - platform: gpio
    pin: GPIO14
    id: adc_control
  - platform: gpio
    pin: GPIO4
    id: heartbeat_output
  - platform: gpio
    pin: GPIO3
    id: wifi_led
  - platform: gpio
    pin: GPIO2
    id: mbus_data_led   
  - platform: gpio          
    pin: GPIO5
    id: mbus_yellow_led    
 
script:
  - id: read_adc_script
    then:
      - output.turn_on: adc_control     # Set IO14 HIGH
      - delay: 8ms  # wait until the capacitor on the resistive divider is charged
      - component.update: adc_input_sensor  # Trigger ADC read
      - output.turn_off: adc_control      # Set IO14 LOW

  - id: ap_mode_blink
    mode: restart
    then:
#      - logger.log: "AP mode blinking started"
      - while:
          condition:
            lambda: 'return !id(wifi_connected);'
          then:
            - output.turn_on: wifi_led
            - delay: 200ms
            - output.turn_off: wifi_led
            - delay: 1000ms  

interval:
  - interval: 600s
    then:
      - script.execute: read_adc_script
  - interval: 5s
    then:
      - light.turn_on: heartbeat_led
      - delay: 500ms
      - light.turn_off: heartbeat_led      

button:
  - platform: template
    name: "Read battery level now"
    on_press:
      then:
        - script.execute: read_adc_script
  - platform: template
    name: "Read WiFi Signal now"
    on_press:
      then:
        - component.update: wifi_signal_sensor        
        
light:
  - platform: binary
    id: heartbeat_led
    output: heartbeat_output
    internal: true  # hide from Home Assistant unless needed
    
binary_sensor:
  - platform: status
    name: "ESP Status"
    id: esp_status
    internal: true  # hide from Home Assistant unless needed    

globals:
  - id: wifi_connected
    type: bool
    restore_value: no
    initial_value: 'false'
    
mqtt:
   enable_on_boot: false
   broker: ""
   id: mqtt_broker
   username: ""
   password: ""
   
text:
 - platform: template
   mode: text
   name: "1. MQTT address"
   id: "broker_address"
   icon: "mdi:text"
   optimistic: true
   internal: false
   entity_category: config
   initial_value: ""
   restore_value: true
   on_value:
      then:
        - logger.log:
            format: "%s"
            args: ["x.c_str()"]
        - lambda: |-
            id(mqtt_broker).set_broker_address(x.c_str());
            ESP_LOGI("mqtt", "MQTT broker set to %s", x.c_str()); 
        
 - platform: template
   mode: text
   name: "3. MQTT Username"
   id: "mqtt_user"
   icon: "mdi:text"
   optimistic: true
   internal: false
   entity_category: config
   initial_value: ""
   restore_value: true
   on_value:
      then:
        - logger.log:
            format: "%s"
            args: ["x.c_str()"]  
        - lambda: |-
            id(mqtt_broker).set_username(x.c_str());  
            ESP_LOGI("mqtt", "MQTT user set to %s", x.c_str());              
            
 - platform: template
   mode: password
   name: "4. MQTT Password"
   id: "mqtt_pwd"
   icon: "mdi:text"
   optimistic: true
   internal: false
   entity_category: config
   initial_value: ""
   restore_value: true
   on_value:
      then:
        - logger.log:
            format: "%s"
            args: ["x.c_str()"]   
        - lambda: |-
            id(mqtt_broker).set_password(x.c_str());
            ESP_LOGI("mqtt", "MQTT password set to %s", x.c_str());            

 - platform: template
   mode: text
   name: "3. MQTT Port"
   id: "mqtt_port"
   icon: "mdi:text"
   optimistic: true
   internal: false
   entity_category: config
   initial_value: "1883"
   restore_value: true
   on_value:
      then:
        - logger.log:
            format: "%s"
            args: ["x.c_str()"]  
        - lambda: |-
            // Convert string to integer
            uint16_t port = (uint16_t) atoi(x.c_str());
            // Safety check (valid range for MQTT ports)
            if (port < 1 || port > 65535) {
              ESP_LOGW("mqtt", "Invalid port %d, ignoring", port);
            } else {
              id(mqtt_broker).set_broker_port(port);
              ESP_LOGI("mqtt", "MQTT Port set to %d", port);
            }

switch:
  - platform: template
    name: "MQTT Enabled"
    id: mqtt_enabled
    optimistic: true
    restore_mode: "RESTORE_DEFAULT_OFF"
    entity_category: config
    
    turn_off_action:
      - logger.log: "MQTT disabled"
      - mqtt.disable:
      - lambda: |-
            ESP_LOGI("mqtt", "MQTT: disabled.");
      
    turn_on_action:
      - lambda: |-
          // Get values from text/number inputs
          std::string addr = id(broker_address).state;
          uint16_t port = (uint16_t)atoi(id(mqtt_port).state.c_str());

          // Validate broker address
          if (addr.empty()) {
            ESP_LOGW("mqtt", "Cannot enable MQTT: broker address is empty");
            id(mqtt_enabled).turn_off();
            return;
          }

          // Validate port
          if (port < 1 || port > 65535) {
            ESP_LOGW("mqtt", "Cannot enable MQTT: invalid port %d", port);
            id(mqtt_enabled).turn_off();
            return;
          }

          // All checks passed, enable MQTT
          ESP_LOGI("mqtt", "MQTT enabled on %s:%d", addr.c_str(), port);
          id(mqtt_broker).set_broker_address(addr.c_str());
          id(mqtt_broker).set_broker_port(port);
          id(mqtt_broker).set_username(id(mqtt_user).state.c_str());
          id(mqtt_broker).set_password(id(mqtt_pwd).state.c_str());
          // Non-blocking delay of 1 second before enabling MQTT
          App.scheduler.set_timeout(nullptr, "mqtt_enable", 1000, []() {
          id(mqtt_broker).enable(); });
          return;

Michael