esphome:
  name: lumiax-mppt-bt

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

logger:
  level: DEBUG

esp32_ble_tracker:
  scan_parameters:
    active: true

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  ap:
    ssid: "imppt"
    password: "mppt4880"

web_server:
  port: 80

ota:
  - platform: esphome
    password: !secret ota_password

ble_client:
  - mac_address: "60:6E:41:1A:68:DB"
    id: mppt_client
    on_connect:
      then:
        - logger.log: "MPPT connected"

globals:
  - id: rx_buf
    type: std::vector<uint8_t>
    restore_value: no
  - id: rx_expected_len
    type: int
    initial_value: '0'
  - id: config_synced
    type: bool
    restore_value: no
    initial_value: 'false'

# ====================== INTERVALY ======================
interval:
  - interval: 5s
    then:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x04, 0x30, 0x30, 0x00, 0x2B, 0xAB, 0x15]

  - interval: 60s
    then:
      - delay: 2500ms
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x03, 0x90, 0x21, 0x00, 0x0A, 0xAC, 0xC8]

# ====================== SCRIPT: re-read konfiguracji po zapisie ======================
script:
  - id: refresh_config
    then:
      - delay: 500ms
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x03, 0x90, 0x21, 0x00, 0x0A, 0xAC, 0xC8]

# ====================== TEXT SENSORS ======================
text_sensor:
  - platform: template
    name: "MPPT Last Frame Live"
    id: last_frame_hex
    update_interval: never

  - platform: template
    name: "MPPT Last Frame Config"
    id: last_frame_hex_config
    update_interval: never

  - platform: template
    name: "MPPT Charge State"
    id: charge_state_text
    update_interval: never

  - platform: template
    name: "MPPT Load State"
    id: load_state_text
    update_interval: never

  - platform: template
    name: "MPPT System Rated Voltage"
    id: system_rated_voltage_text
    update_interval: never

# ====================== BINARY SENSORS ======================
binary_sensor:
  - platform: template
    name: "MPPT Is Charging"
    id: is_charging

  - platform: template
    name: "MPPT Is Night"
    id: is_night

  - platform: template
    name: "MPPT Charge Fault"
    id: charge_fault
    device_class: problem

  - platform: template
    name: "MPPT Charge Over Temperature"
    id: charge_over_temp
    device_class: problem

  - platform: template
    name: "MPPT Charge Disabled"
    id: charge_disabled

  - platform: template
    name: "MPPT Load Enabled"
    id: load_enabled

  - platform: template
    name: "MPPT Output Fault"
    id: output_fault
    device_class: problem

  - platform: template
    name: "MPPT Output Short Circuit"
    id: output_short_circuit
    device_class: problem

  - platform: template
    name: "MPPT Output Over Temperature"
    id: output_over_temp
    device_class: problem

  - platform: template
    name: "MPPT Output Open Circuit"
    id: output_open_circuit
    device_class: problem

  - platform: template
    name: "MPPT Output Hardware Protection"
    id: output_hw_protection
    device_class: problem

# ====================== SENSORS ======================
sensor:
  # --- RECEIVER ---
  - platform: ble_client
    type: characteristic
    ble_client_id: mppt_client
    id: ble_rx_handler
    internal: true
    service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
    characteristic_uuid: "0000ff01-0000-1000-8000-00805f9b34fb"
    notify: true
    update_interval: never
    lambda: |-
      auto &buf = id(rx_buf);

      if (x.size() >= 3 && x[0] == 0x01 && (x[1] == 0x03 || x[1] == 0x04)) {
        buf.clear();
        id(rx_expected_len) = x[2] + 5;
      }
      for (auto b : x) buf.push_back(b);

      if (id(rx_expected_len) == 0 || (int)buf.size() < id(rx_expected_len)) {
        return {};
      }

      char hex[260];
      char *pp = hex;
      int dump_len = std::min((int)buf.size(), 80);
      for (int i = 0; i < dump_len; i++) {
        pp += sprintf(pp, "%02X ", buf[i]);
      }
      *pp = 0;

      const uint8_t func = buf[1];
      const uint8_t *p = buf.data() + 3;
      const int N = id(rx_expected_len) - 5;

      auto u16 = [&](int off) -> uint16_t {
        return (uint16_t(p[off]) << 8) | p[off+1];
      };
      auto s16 = [&](int off) -> int16_t { return (int16_t)u16(off); };
      auto u32 = [&](int off) -> uint32_t {
        return (uint32_t(u16(off+2)) << 16) | u16(off);
      };

      // =========================================================
      // FUNKCJA 0x04 = READ INPUT (live)
      // =========================================================
      if (func == 0x04 && N >= 0x50) {
        ESP_LOGI("mppt", "RX live %d B: %s", (int)buf.size(), hex);
        id(last_frame_hex).publish_state(hex);

        uint16_t st_charge = u16(0x08);
        uint16_t st_load   = u16(0x0A);

        id(charging_state_raw).publish_state(st_charge);
        id(is_charging).publish_state((st_charge >> 0) & 0x01);
        id(charge_fault).publish_state((st_charge >> 1) & 0x01);
        id(charge_over_temp).publish_state((st_charge >> 4) & 0x01);
        id(is_night).publish_state((st_charge >> 5) & 0x01);
        id(charge_disabled).publish_state((st_charge >> 6) & 0x01);

        const char* cs_names[] = {"Not charging", "Float", "Boost", "Equalize"};
        id(charge_state_text).publish_state(cs_names[(st_charge >> 2) & 0x03]);

        id(load_enabled).publish_state((st_load >> 0) & 0x01);
        id(output_fault).publish_state((st_load >> 1) & 0x01);
        id(output_over_temp).publish_state((st_load >> 2) & 0x01);
        id(output_open_circuit).publish_state((st_load >> 3) & 0x01);
        id(output_hw_protection).publish_state((st_load >> 4) & 0x01);
        id(output_short_circuit).publish_state((st_load >> 11) & 0x01);

        const char* ls_names[] = {"Light", "Moderate", "Rated", "Overload"};
        id(load_state_text).publish_state(ls_names[(st_load >> 12) & 0x03]);

        id(environment_temperature).publish_state(s16(0x0C) / 100.0f);
        id(internal_temperature).publish_state(s16(0x0E) / 100.0f);

        id(battery_soc).publish_state(u16(0x2A));
        id(battery_voltage).publish_state(u16(0x2C) / 100.0f);
        id(battery_current).publish_state(s16(0x2E) / 100.0f);
        id(battery_power).publish_state(s16(0x30) / 100.0f);

        id(load_voltage).publish_state(u16(0x34) / 100.0f);
        id(load_current).publish_state(u16(0x36) / 100.0f);
        id(load_power).publish_state(s16(0x38) / 100.0f);

        id(pv_voltage).publish_state(u16(0x3C) / 100.0f);
        id(pv_current).publish_state(u16(0x3E) / 100.0f);
        id(pv_power).publish_state(s16(0x40) / 100.0f);

        id(pv_daily_energy).publish_state(u16(0x44) / 100.0f);
        id(pv_total_energy).publish_state(u32(0x46) / 100.0f);
        id(load_daily_energy).publish_state(u16(0x4A) / 100.0f);
        id(load_total_energy).publish_state(u32(0x4C) / 100.0f);

        float pv_p  = s16(0x40) / 100.0f;
        float bat_p = s16(0x30) / 100.0f;
        float bat_v = u16(0x2C) / 100.0f;
        float est_load_p = pv_p - bat_p;
        if (est_load_p < 0.0f) est_load_p = 0.0f;
        id(load_power_estimated).publish_state(est_load_p);
        if (bat_v > 1.0f) {
          id(load_current_estimated).publish_state(est_load_p / bat_v);
        }
      }

      // =========================================================
      // FUNKCJA 0x03 = READ HOLDING (config)
      // =========================================================
      else if (func == 0x03 && N >= 20) {
        ESP_LOGI("mppt", "RX config %d B: %s", (int)buf.size(), hex);
        id(last_frame_hex_config).publish_state(hex);

        // Battery type (0x9021) -> select
        const char* bt_names[] = {"Lithium", "Liquid", "GEL", "AGM"};
        uint8_t bt = u16(0x00) & 0x0F;
        if (bt < 4) id(battery_type_select).publish_state(bt_names[bt]);

        // Napięcia (0x9022..0x9026) -> number (publish_state nie wywołuje set_action)
        id(set_lvd_voltage).publish_state(u16(0x02) / 100.0f);
        id(set_lvr_voltage).publish_state(u16(0x04) / 100.0f);
        id(set_boost_voltage).publish_state(u16(0x06) / 100.0f);
        id(set_equalizing_voltage).publish_state(u16(0x08) / 100.0f);
        id(set_float_voltage).publish_state(u16(0x0A) / 100.0f);

        // System rated voltage (0x9027) -> text sensor (read only)
        const char* sv_names[] = {"Auto", "12V", "24V", "36V", "48V", "60V", "110V", "120V", "220V", "240V"};
        uint8_t sv = u16(0x0C);
        if (sv < 10) id(system_rated_voltage_text).publish_state(sv_names[sv]);

        // CVT / CVR Li (0x9028, 0x9029) -> number
        id(set_cvt_voltage).publish_state(u16(0x0E) / 100.0f);
        id(set_cvr_voltage).publish_state(u16(0x10) / 100.0f);

        // 0°C charging mode (0x902A) -> select
        const char* zc_names[] = {"Normal charging", "No charging", "Slow charging"};
        uint8_t zc = u16(0x12) & 0x0F;
        if (zc < 3) id(zero_c_charging_select).publish_state(zc_names[zc]);

        id(config_synced) = true;
      }

      id(rx_expected_len) = 0;
      buf.clear();
      return {};

  # ====================== SENSORY LIVE ======================
  - platform: template
    name: "MPPT Charging State (raw)"
    id: charging_state_raw
    accuracy_decimals: 0

  - platform: template
    name: "MPPT Environment Temperature"
    id: environment_temperature
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: temperature
    state_class: measurement

  - platform: template
    name: "MPPT Internal Temperature"
    id: internal_temperature
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: temperature
    state_class: measurement

  - platform: template
    name: "MPPT Battery SoC"
    id: battery_soc
    unit_of_measurement: "%"
    accuracy_decimals: 0
    device_class: battery
    state_class: measurement

  - platform: template
    name: "MPPT Battery Voltage"
    id: battery_voltage
    unit_of_measurement: "V"
    accuracy_decimals: 2
    device_class: voltage
    state_class: measurement

  - platform: template
    name: "MPPT Battery Current"
    id: battery_current
    unit_of_measurement: "A"
    accuracy_decimals: 2
    device_class: current
    state_class: measurement

  - platform: template
    name: "MPPT Battery Power"
    id: battery_power
    unit_of_measurement: "W"
    accuracy_decimals: 2
    device_class: power
    state_class: measurement

  - platform: template
    name: "MPPT Load Voltage"
    id: load_voltage
    unit_of_measurement: "V"
    accuracy_decimals: 2
    device_class: voltage
    state_class: measurement

  - platform: template
    name: "MPPT Load Current"
    id: load_current
    unit_of_measurement: "A"
    accuracy_decimals: 2
    device_class: current
    state_class: measurement

  - platform: template
    name: "MPPT Load Power"
    id: load_power
    unit_of_measurement: "W"
    accuracy_decimals: 2
    device_class: power
    state_class: measurement

  - platform: template
    name: "MPPT Load Current Estimated"
    id: load_current_estimated
    unit_of_measurement: "A"
    accuracy_decimals: 2
    device_class: current
    state_class: measurement

  - platform: template
    name: "MPPT Load Power Estimated"
    id: load_power_estimated
    unit_of_measurement: "W"
    accuracy_decimals: 2
    device_class: power
    state_class: measurement

  - platform: template
    name: "MPPT PV Voltage"
    id: pv_voltage
    unit_of_measurement: "V"
    accuracy_decimals: 2
    device_class: voltage
    state_class: measurement

  - platform: template
    name: "MPPT PV Current"
    id: pv_current
    unit_of_measurement: "A"
    accuracy_decimals: 2
    device_class: current
    state_class: measurement

  - platform: template
    name: "MPPT PV Power"
    id: pv_power
    unit_of_measurement: "W"
    accuracy_decimals: 2
    device_class: power
    state_class: measurement

  - platform: template
    name: "MPPT PV Daily Energy"
    id: pv_daily_energy
    unit_of_measurement: "kWh"
    accuracy_decimals: 2
    device_class: energy
    state_class: total_increasing

  - platform: template
    name: "MPPT PV Total Energy"
    id: pv_total_energy
    unit_of_measurement: "kWh"
    accuracy_decimals: 2
    device_class: energy
    state_class: total_increasing

  - platform: template
    name: "MPPT Load Daily Energy"
    id: load_daily_energy
    unit_of_measurement: "kWh"
    accuracy_decimals: 2
    device_class: energy
    state_class: total_increasing

  - platform: template
    name: "MPPT Load Total Energy"
    id: load_total_energy
    unit_of_measurement: "kWh"
    accuracy_decimals: 2
    device_class: energy
    state_class: total_increasing

# ====================== SWITCH ======================
switch:
  - platform: template
    name: "MPPT Load Output"
    id: load_output_switch
    icon: mdi:power-plug
    lambda: |-
      if (id(load_enabled).has_state()) {
        return id(load_enabled).state;
      }
      return {};
    turn_on_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x98, 0x35]
      - delay: 500ms
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x04, 0x30, 0x30, 0x00, 0x2B, 0xAB, 0x15]
    turn_off_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x05, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xC5]
      - delay: 500ms
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: [0xFE, 0x04, 0x30, 0x30, 0x00, 0x2B, 0xAB, 0x15]



number:
  - platform: template
    name: "MPPT Set LVD Voltage"
    id: set_lvd_voltage
    optimistic: true
    min_value: 9.0
    max_value: 12.0
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x22,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            ESP_LOGI("mppt_set", "LVD %.2fV -> %02X %02X %02X %02X %02X %02X %02X %02X",
                     x, cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5],cmd[6],cmd[7]);
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set LVR Voltage"
    id: set_lvr_voltage
    optimistic: true
    min_value: 9.6
    max_value: 16.0
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x23,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set Boost Voltage"
    id: set_boost_voltage
    optimistic: true
    min_value: 12.0
    max_value: 17.0
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x24,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set Equalizing Voltage"
    id: set_equalizing_voltage
    optimistic: true
    min_value: 12.0
    max_value: 17.0
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x25,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set Float Voltage"
    id: set_float_voltage
    optimistic: true
    min_value: 12.0
    max_value: 17.0
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x26,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set CVT (Charge Target Li)"
    id: set_cvt_voltage
    optimistic: true
    min_value: 10.0
    max_value: 17.0
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x28,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set CVR (Charge Recovery Li)"
    id: set_cvr_voltage
    optimistic: true
    min_value: 9.2
    max_value: 16.8
    step: 0.1
    unit_of_measurement: "V"
    mode: box
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = (uint16_t)(x * 100.0f + 0.5f);
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x29,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

# ====================== SELECT ======================
select:
  - platform: template
    name: "MPPT Set Battery Type"
    id: battery_type_select
    optimistic: true
    options:
      - "Lithium"
      - "Liquid"
      - "GEL"
      - "AGM"
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = 0;
            if (x == "Lithium") v = 0;
            else if (x == "Liquid") v = 1;
            else if (x == "GEL") v = 2;
            else if (x == "AGM") v = 3;
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x21,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config

  - platform: template
    name: "MPPT Set 0°C Charging Mode"
    id: zero_c_charging_select
    optimistic: true
    options:
      - "Normal charging"
      - "No charging"
      - "Slow charging"
    entity_category: config
    set_action:
      - ble_client.ble_write:
          id: mppt_client
          service_uuid: "0000ff00-0000-1000-8000-00805f9b34fb"
          characteristic_uuid: "0000ff02-0000-1000-8000-00805f9b34fb"
          value: !lambda |-
            uint16_t v = 0;
            if (x == "Normal charging") v = 0;
            else if (x == "No charging") v = 1;
            else if (x == "Slow charging") v = 2;
            std::vector<uint8_t> cmd = {0xFE, 0x06, 0x90, 0x2A,
                                       (uint8_t)(v >> 8), (uint8_t)(v & 0xFF), 0, 0};
            uint16_t crc = 0xFFFF;
            for (int i = 0; i < 6; i++) {
              crc ^= cmd[i];
              for (int j = 0; j < 8; j++)
                crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
            }
            cmd[6] = crc & 0xFF;
            cmd[7] = (crc >> 8) & 0xFF;
            return cmd;
      - script.execute: refresh_config
