Sterowanie webastem Vevor przez BT

Może komuś się przyda. Projekt nie jest dokończony. Można włączyć/wyłączyć piec, zmienić mode level/automatic. Nie wiem czemu dane które są odczytywane ze sterownika to jakaś porażka. Albo ten piec tak ma albo coś źle skonfigurowane mam w esphome. Dane są poprostu nieprawdziwe. Niemniej jednak w połączeniu z HA da się to ogarnąć.
Oto kod:

substitutions:
  heater_mac: "3C:AB:72:79:77:07"
  service_uuid: "0000FFE0-0000-1000-8000-00805F9B34FB"
  characteristic_uuid: "0000FFE1-0000-1000-8000-00805F9B34FB"

esphome:
  name: vevor
  friendly_name: Vevor
  on_boot:
    priority: -10
    then:
      - delay: 1s
      - ble_client.connect: heater_ble
      - delay: 800ms
      # pierwszy poll po połączeniu
      - ble_client.ble_write:
          service_uuid: ${service_uuid}
          characteristic_uuid: ${characteristic_uuid}
          value: [0xAA, 0x55, 0x0C, 0x22, 0x01, 0x00, 0x00, 0x2F]

esp32:
  board: esp32dev
  framework:
    type: esp-idf

logger:
  level: DEBUG

improv_serial:

api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxx"

ota:
  - platform: esphome
    password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

web_server:
  port: 80
  version: 3

wifi:
  networks:
    - ssid: !secret wifi_ssid
      password: !secret wifi_password
    - ssid: "xxxxxxxxxxxxx"
      password: !secret wifi_password
  ap:
    ssid: "VevorBLE Hotspot"
    password: "12345678"

captive_portal:

esp32_ble_tracker:

ble_client:
  - mac_address: ${heater_mac}
    id: heater_ble
    on_connect:
      then:
        - logger.log: "BLE connected – sending poll"
        - delay: 400ms
        - ble_client.ble_write:
            service_uuid: ${service_uuid}
            characteristic_uuid: ${characteristic_uuid}
            value: [0xAA, 0x55, 0x0C, 0x22, 0x01, 0x00, 0x00, 0x2F]
    on_disconnect:
      then:
        - delay: 2s
        - ble_client.connect: heater_ble

# cykliczny poll
interval:
  - interval: 10s
    then:
      - ble_client.ble_write:
          service_uuid: ${service_uuid}
          characteristic_uuid: ${characteristic_uuid}
          value: [0xAA, 0x55, 0x0C, 0x22, 0x01, 0x00, 0x00, 0x2F]

globals:
  - id: current_mode
    type: int
    initial_value: '1'

text_sensor:
  - platform: template
    name: "Glow Plug Status"
    id: Glow_Plug_Status
  - platform: template
    name: "Heater Mode"
    id: Mode_Burner

binary_sensor:
  - platform: template
    name: "Heater Status"
    id: Burner_Status
    device_class: power

sensor:
  - platform: uptime
    name: Uptime

  - platform: template
    name: "Error Code"
    id: Error_Code
    accuracy_decimals: 0

  - platform: template
    name: "Battery Voltage"
    id: battery_voltage
    unit_of_measurement: "V"
    accuracy_decimals: 1
    device_class: voltage
    icon: "mdi:car-battery"

  - platform: template
    name: "Altitude"
    id: altitude
    unit_of_measurement: "m"
    accuracy_decimals: 0
    icon: "mdi:summit"

  - platform: template
    name: "Power Level"
    id: stato_Potenza

  - platform: template
    name: "Fan Speed"
    id: fan_speed
    icon: "mdi:fan"

  - platform: template
    name: "Room Temperature"
    id: room_temperature
    unit_of_measurement: "°C"

  - platform: template
    name: "Heating Temperature"
    id: heating_temperature
    unit_of_measurement: "°C"

  # BLE NOTIFY parser – DA i AA55
  - platform: ble_client
    type: characteristic
    id: my_ble_sensor
    ble_client_id: heater_ble
    service_uuid: ${service_uuid}
    characteristic_uuid: ${characteristic_uuid}
    name: "BLE Raw Data"
    notify: true
    lambda: |-
      ESP_LOGD("BLE","RX(%d): %s", x.size(), format_hex_pretty(x).c_str());

      // DA format (Twoje nowsze ramki)
      if (x.size() >= 20 && (uint8_t)x[0] == 0xDA) {
        id(Mode_Burner).publish_state(((uint8_t)x[12]) == 0x02 ? "Automatic" : "Level");

        // NIE publikujemy Burner_Status z DA – eliminujemy "odbijanie"
        id(fan_speed).publish_state((int)((uint8_t)x[10]));
        id(room_temperature).publish_state((int)((uint8_t)x[16]));
        id(heating_temperature).publish_state((int)((uint8_t)x[14]));
        id(battery_voltage).publish_state(((uint8_t)x[11]) / 10.0f);
        id(Error_Code).publish_state(0);
        id(altitude).publish_state(0);
        id(Glow_Plug_Status).publish_state("Unknown");
        return 0;
      }

      // AA 55 format (legacy – tu kiedyś działało poprawnie)
      if (x.size() >= 20 && (uint8_t)x[0] == 0xAA) {
        static const char* const states[] = {"Warmup","Self test running","Ignition","Heating","Shutting down"};
        id(heating_temperature).publish_state((int16_t)(((uint8_t)x[14])<<8)|((uint8_t)x[13]));
        id(Mode_Burner).publish_state(x[8] == 2 ? "Automatic" : "Level");

        // Tu wolno publikować Burner_Status (sprawdzone kiedyś)
        id(Burner_Status).publish_state(x[3] > 0);

        if (x[5] < (sizeof(states)/sizeof(states[0]))) {
          id(Glow_Plug_Status).publish_state(states[x[5]]);
        }
        id(stato_Potenza).publish_state(x[9]);
        id(Error_Code).publish_state(x[17]);
        id(altitude).publish_state(((x[7])<<8)|x[6]);
        id(room_temperature).publish_state((int16_t)(((uint8_t)x[16])<<8)|((uint8_t)x[15]));
        id(battery_voltage).publish_state((((x[12])<<8)|x[11])/10.0);
        if (id(Glow_Plug_Status).state == "Warmup" && id(Burner_Status).state == 0) {
          id(fan_speed).publish_state(0);
        } else {
          id(fan_speed).publish_state(x[10] + 1);
        }
        return 0;
      }
      return 0;

switch:
  - platform: template
    name: "Heater Power"
    id: heater_power
    icon: "mdi:radiator"
    optimistic: true
    turn_on_action:
      - logger.log: "Power ON (FFE1)"
      # komenda ON dokładnie jak wcześniej
      - ble_client.ble_write:
          service_uuid: ${service_uuid}
          characteristic_uuid: ${characteristic_uuid}
          value: [0xAA, 0x55, 0x0C, 0x22, 0x03, 0x01, 0x00, 0x32]
      # Lokalnie ustawiamy stan na ON, żeby UI nie odbijało
      - lambda: |-
          id(Burner_Status).publish_state(true);
      # Opóźnij poll o 2 s, by nie nadpisać stanu natychmiast po ON
      - delay: 2s
      - ble_client.ble_write:
          service_uuid: ${service_uuid}
          characteristic_uuid: ${characteristic_uuid}
          value: [0xAA, 0x55, 0x0C, 0x22, 0x01, 0x00, 0x00, 0x2F]

    turn_off_action:
      - logger.log: "Power OFF (FFE1)"
      - ble_client.ble_write:
          service_uuid: ${service_uuid}
          characteristic_uuid: ${characteristic_uuid}
          value: [0xAA, 0x55, 0x0C, 0x22, 0x03, 0x00, 0x00, 0x31]
      # Lokalnie OFF
      - lambda: |-
          id(Burner_Status).publish_state(false);

    lambda: |-
      return id(Burner_Status).state;

select:
  - platform: template
    name: "Heater Mode"
    id: heater_mode
    options:
      - "Level"
      - "Automatic"
    initial_option: "Level"
    optimistic: true
    on_value:
      then:
        - if:
            condition:
              lambda: |-
                return id(heater_mode).state == "Level";
            then:
              - lambda: |-
                  id(current_mode) = 1;
              - ble_client.ble_write:
                  service_uuid: ${service_uuid}
                  characteristic_uuid: ${characteristic_uuid}
                  value: [0xAA, 0x55, 0x0C, 0x22, 0x02, 0x01, 0x00, 0x31]
            else:
              - lambda: |-
                  id(current_mode) = 2;
              - ble_client.ble_write:
                  service_uuid: ${service_uuid}
                  characteristic_uuid: ${characteristic_uuid}
                  value: [0xAA, 0x55, 0x0C, 0x22, 0x02, 0x02, 0x00, 0x32]

number:
  - platform: template
    name: "Heater Temperature"
    id: heater_temperature
    min_value: 8
    max_value: 36
    step: 1
    unit_of_measurement: "°C"
    optimistic: true
    set_action:
      then:
        # tryb auto, potem temperatura
        - ble_client.ble_write:
            service_uuid: ${service_uuid}
            characteristic_uuid: ${characteristic_uuid}
            value: [0xAA, 0x55, 0x0C, 0x22, 0x02, 0x02, 0x00, 0x32]
        - delay: 1s
        - ble_client.ble_write:
            service_uuid: ${service_uuid}
            characteristic_uuid: ${characteristic_uuid}
            value: !lambda |-
              uint8_t t = (uint8_t) id(heater_temperature).state;
              uint16_t sum = 0xAA + 0x55 + 0x0C + 0x22 + 0x04 + t + 0x00;
              uint8_t checksum = (sum + 1) & 0xFF;
              return std::vector<uint8_t>{0xAA, 0x55, 0x0C, 0x22, 0x04, t, 0x00, checksum};

  - platform: template
    name: "Heater Level"
    id: heater_level
    min_value: 0
    max_value: 10
    step: 1
    unit_of_measurement: "Level"
    optimistic: true
    set_action:
      then:
        # tryb level, potem poziom
        - ble_client.ble_write:
            service_uuid: ${service_uuid}
            characteristic_uuid: ${characteristic_uuid}
            value: [0xAA, 0x55, 0x0C, 0x22, 0x02, 0x01, 0x00, 0x31]
        - delay: 1s
        - ble_client.ble_write:
            service_uuid: ${service_uuid}
            characteristic_uuid: ${characteristic_uuid}
            value: !lambda |-
              uint8_t lvl = (uint8_t) id(heater_level).state;
              uint16_t sum = 0xAA + 0x55 + 0x0C + 0x22 + 0x04 + lvl + 0x00;
              uint8_t checksum = (sum + 1) & 0xFF;
              return std::vector<uint8_t>{0xAA, 0x55, 0x0C, 0x22, 0x04, lvl, 0x00, checksum};

Testowane na sterowniku za zdjęcia
Źródło: https://github.com/espnomad/ESPHome-BLE-Dieselheater/blob/main/dieselheater-ble.yaml
Może ktoś to dopracuje :slight_smile:


4 polubienia

Dosłownie przed chwilą pojawił się nowy projekt sterowania webastem Vevor. Z opisu wynika że został właśnie rozpracowany nowy protokół komunikacji BT Vevora. Jeszcze nie testowałem.