Marstek Venus E with esphome SML Reader as Virtual Shelly 3 Pro EM

Got an Update to v153. It broke down :). Here´s the new config.
It even gives better values.

sml:
  id: mysml
  uart_id: uart_bus

sensor:
  - platform: sml
    name: "Meter Reading"
    sml_id: mysml
    obis_code: "1-0:1.8.0"
    unit_of_measurement: kWh
    accuracy_decimals: 4
    device_class: energy
    state_class: total_increasing
    filters:
      - multiply: 0.0000001

  - platform: total_daily_energy
    name: "Daily Export"
    power_id: export_power
    filters:
      - multiply: 0.001
    unit_of_measurement: kWh

  - platform: sml
    name: "Total Power Consumption"
    id: verbrauch
    sml_id: mysml
    obis_code: "1-0:16.7.0"
    unit_of_measurement: W
    accuracy_decimals: 2
    filters:
      - multiply: 0.01
    on_value:
      - sensor.template.publish:
          id: export_power
          state: !lambda |-
            if ((id(verbrauch).state) >= 0) {
              return 0; 
            } else {
              return (id(verbrauch).state /-1.0);
            }

  - platform: sml
    name: "Power Consumption L1"
    sml_id: mysml
    id: l1
    obis_code: "1-0:36.7.0"
    unit_of_measurement: W
    accuracy_decimals: 2
    filters:
      - multiply: 0.01

  - platform: sml
    name: "Power Consumption L2"
    sml_id: mysml
    id: l2
    obis_code: "1-0:56.7.0"
    unit_of_measurement: W
    accuracy_decimals: 2
    filters:
      - multiply: 0.01

  - platform: sml
    name: "Power Consumption L3"
    sml_id: mysml
    id: l3
    obis_code: "1-0:76.7.0"
    unit_of_measurement: W
    accuracy_decimals: 2
    filters:
      - multiply: 0.01

  - platform: sml
    name: "Voltage L1"
    sml_id: mysml
    id: u1
    obis_code: "1-0:32.7.0"
    unit_of_measurement: V
    accuracy_decimals: 2
    filters:
      - multiply: 0.1

  - platform: sml
    name: "Voltage L2"
    sml_id: mysml
    id: u2
    obis_code: "1-0:52.7.0"
    unit_of_measurement: V
    accuracy_decimals: 2
    filters:
      - multiply: 0.1

  - platform: sml
    name: "Voltage L3"
    sml_id: mysml
    id: u3
    obis_code: "1-0:72.7.0"
    unit_of_measurement: V
    accuracy_decimals: 2
    filters:
      - multiply: 0.1

  - platform: template
    name: "Current L1"
    id: i1
    unit_of_measurement: "A"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (id(u1).has_state() && id(l1).has_state() && id(u1).state > 0) {
        return fabs(id(l1).state / id(u1).state);
      } else {
        return 0.0;
      }
      
  - platform: template
    name: "Current L2"
    id: i2
    unit_of_measurement: "A"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (id(u2).has_state() && id(l2).has_state() && id(u2).state > 0) {
        return fabs(id(l2).state / id(u2).state);
      } else {
        return 0.0;
      }
      
  - platform: template
    name: "Current L3"
    id: i3
    unit_of_measurement: "A"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (id(u3).has_state() && id(l3).has_state() && id(u3).state > 0) {
        return fabs(id(l3).state / id(u3).state);
      } else {
        return 0.0;
      }
      
  - platform: template
    name: "Total Current"
    id: i_total
    unit_of_measurement: "A"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      return id(i1).state + id(i2).state + id(i3).state;
        
  - platform: template
    name: "Power Factor L1"
    id: pf1
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (!id(l1).has_state()) return 0.95; // Fallback
      
      // Power absolute value for calculation
      float power_abs = fabs(id(l1).state);
      
      // Export (negative power)
      if (id(l1).state < 0) {
        // Inverters typically have better PF (0.95-0.99)
        return 0.95 + (std::rand() % 4) * 0.01;
      }
      // Import (positive power)
      else {
        if (power_abs < 20.0) {
          // Low load: worse PF (0.85-0.90)
          return 0.85 + (std::rand() % 5) * 0.01;
        } else if (power_abs < 100.0) {
          // Medium load: medium PF (0.90-0.94)
          return 0.90 + (std::rand() % 4) * 0.01;
        } else {
          // High load: better PF (0.94-0.98)
          return 0.94 + (std::rand() % 4) * 0.01;
        }
      }

  - platform: template
    name: "Power Factor L2"
    id: pf2
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (!id(l2).has_state()) return 0.95; // Fallback
      
      // Power absolute value for calculation
      float power_abs = fabs(id(l2).state);
      
      // Export (negative power)
      if (id(l2).state < 0) {
        // Inverters typically have better PF (0.95-0.99)
        return 0.95 + (std::rand() % 4) * 0.01;
      }
      // Import (positive power)
      else {
        if (power_abs < 20.0) {
          // Low load: worse PF (0.85-0.90)
          return 0.85 + (std::rand() % 5) * 0.01;
        } else if (power_abs < 100.0) {
          // Medium load: medium PF (0.90-0.94)
          return 0.90 + (std::rand() % 4) * 0.01;
        } else {
          // High load: better PF (0.94-0.98)
          return 0.94 + (std::rand() % 4) * 0.01;
        }
      }

  - platform: template
    name: "Power Factor L3"
    id: pf3
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (!id(l3).has_state()) return 0.95; // Fallback
      
      // Power absolute value for calculation
      float power_abs = fabs(id(l3).state);
      
      // Export (negative power)
      if (id(l3).state < 0) {
        // Inverters typically have better PF (0.95-0.99)
        return 0.95 + (std::rand() % 4) * 0.01;
      }
      // Import (positive power)
      else {
        if (power_abs < 20.0) {
          // Low load: worse PF (0.85-0.90)
          return 0.85 + (std::rand() % 5) * 0.01;
        } else if (power_abs < 100.0) {
          // Medium load: medium PF (0.90-0.94)
          return 0.90 + (std::rand() % 4) * 0.01;
        } else {
          // High load: better PF (0.94-0.98)
          return 0.94 + (std::rand() % 4) * 0.01;
        }
      }

  # Apparent power for phase L1
  - platform: template
    name: "Apparent Power L1"
    id: apparent_power_l1
    unit_of_measurement: "VA"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (id(l1).has_state() && id(pf1).has_state() && id(pf1).state > 0) {
        return fabs(id(l1).state) / id(pf1).state;
      } else {
        return 0.0;
      }

  # Apparent power for phase L2
  - platform: template
    name: "Apparent Power L2"
    id: apparent_power_l2
    unit_of_measurement: "VA"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (id(l2).has_state() && id(pf2).has_state() && id(pf2).state > 0) {
        return fabs(id(l2).state) / id(pf2).state;
      } else {
        return 0.0;
      }

  # Apparent power for phase L3
  - platform: template
    name: "Apparent Power L3"
    id: apparent_power_l3
    unit_of_measurement: "VA"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      if (id(l3).has_state() && id(pf3).has_state() && id(pf3).state > 0) {
        return fabs(id(l3).state) / id(pf3).state;
      } else {
        return 0.0;
      }

  # Total apparent power
  - platform: template
    name: "Total Apparent Power"
    id: apparent_power_total
    unit_of_measurement: "VA"
    accuracy_decimals: 2
    update_interval: 1s
    lambda: |-
      return id(apparent_power_l1).state + id(apparent_power_l2).state + id(apparent_power_l3).state;

  - platform: template
    name: "Grid Frequency"
    id: freq
    unit_of_measurement: "Hz"
    accuracy_decimals: 1
    update_interval: 10s
    lambda: |-
      static float last_freq = 50.0;
      // Simulate realistic, slightly fluctuating grid frequency
      // The frequency changes only slowly, never abruptly
      float new_freq = last_freq + ((float)(rand() % 5 - 2) / 20.0);  // -0.1 to +0.1 Hz change
      
      // Limit to realistic range
      if (new_freq < 49.8) new_freq = 49.8;
      if (new_freq > 50.2) new_freq = 50.2;
      
      last_freq = new_freq;
      return new_freq;

udp:
  - id: udp_sender
    port:
      listen_port: 18511
      broadcast_port: 18512
    addresses:
      - 192.168.0.54

  # 1) Sender: unicast to Venus battery 192.168.0.55:22222
  - id: udp_shelly_sender
    port:
      listen_port: 18001        # any local sending port
      broadcast_port: 22222     # target port of the battery
    addresses:
      - 192.168.0.55           # fixed IP of the battery

  # 2) Server: listens on port 1010 for EM.GetStatus
  - id: udp_server
    port:
      listen_port: 1010
      broadcast_port: 1010
    on_receive:
      then:
      - lambda: |-
          std::string msg(data.begin(), data.end());
          if (msg.find("\"method\":\"EM.GetStatus\"") == std::string::npos) return;
      - udp.write:
          id: udp_shelly_sender
          data: !lambda |-
            char buf[1024];
            
            int len = snprintf(buf, sizeof(buf),
              "{"
                "\"id\":0,"
                "\"src\":\"shellypro3em-e682e89c1724\","
                "\"result\":{"
                  "\"id\":0,"
                  "\"a_current\":%.2f,\"a_voltage\":%.1f,\"a_act_power\":%.2f,"
                  "\"a_aprt_power\":%.2f,\"a_pf\":%.2f,\"a_freq\":%.1f,"
                  "\"b_current\":%.2f,\"b_voltage\":%.1f,\"b_act_power\":%.2f,"
                  "\"b_aprt_power\":%.2f,\"b_pf\":%.2f,\"b_freq\":%.1f,"
                  "\"c_current\":%.2f,\"c_voltage\":%.1f,\"c_act_power\":%.2f,"
                  "\"c_aprt_power\":%.2f,\"c_pf\":%.2f,\"c_freq\":%.1f,"
                  "\"total_current\":%.2f,"
                  "\"total_act_power\":%.2f,"
                  "\"total_aprt_power\":%.2f"
                "}"
              "}",
              id(i1).state, id(u1).state, id(l1).state,
              id(apparent_power_l1).state, id(pf1).state, id(freq).state,
              id(i2).state, id(u2).state, id(l2).state,
              id(apparent_power_l2).state, id(pf2).state, id(freq).state,
              id(i3).state, id(u3).state, id(l3).state,
              id(apparent_power_l3).state, id(pf3).state, id(freq).state,
              id(i_total).state,
              id(verbrauch).state,
              id(apparent_power_total).state
            );
            return std::vector<uint8_t>(buf, buf + len);