Add support for sonoff s-mate and r5 (ewelink-remote sub-devices)

Here little bit shorter code for Sonoff R5/S-Mate/S-Mate2.
ESPHome will fireHA event with data about device id, button pressed and kind of action.
Events can be seen in:

  • Developer Tools → Events → Event to subscribe to: esphome.sonoff_ble → Start Listening.
globals:
  - id: btdata_uuid4
    type: std::vector<uint32_t>
    restore_value: no
    #button press UUID by order. The order may vary according to device version
    initial_value: '{0x8A6BECDA, 0xD535B385, 0x1FFC794F, 0xE60781B6, 0x2FCF487F, 0xD734B087, 0x02E36652, 0xEB0B8FBB, 0x32D15662,
                     0xFA1B99AA, 0x9575F6C5, 0x9F7CFCCF, 0x29C84B79, 0xBF5FDDEF, 0xA043C2F0, 0xBF5EDAEF, 0xA141C4F1, 0x9D7EF8CD}'
  - id: button_actions
    type: 'std::vector<std::string>'
    restore_value: no
    initial_value: '{"short", "double", "long"}'

esp32_ble_tracker:
  scan_parameters:
    interval: 150ms
    window: 150ms
    active: false

  on_ble_advertise:
    - mac_address: "66:55:44:33:22:11" # Sonoff BLE
      then:
        - lambda: |-
            static std::vector< esphome::esp32_ble::ESPBTUUID > btdata_store;
            auto btdata = x.get_service_uuids();

            if (btdata.size() >= 6 && btdata[1].contains(0x78, 0xF6)) {
              if (!(btdata_store == btdata)) {
                btdata_store = btdata;
                uint32_t btdevice = (btdata[2].get_uuid().uuid.uuid32 & 0xff000000) | 
                                    (btdata[3].get_uuid().uuid.uuid32 & 0x00ffffff);
                uint32_t uuid4u32 = (btdata[4].get_uuid().uuid.uuid32);

                for (size_t i = 0; i < id(btdata_uuid4).size(); i++) {
                  uint32_t stored_value = id(btdata_uuid4)[i];
                  if ((((uuid4u32 >> 24) ^ (stored_value >> 24)) == ((uuid4u32 >> 16 & 0xff) ^ (stored_value >> 16 & 0xff))) && 
                      (((uuid4u32 >> 8 & 0xff) ^ (stored_value >> 8 & 0xff)) == ((uuid4u32 & 0xff) ^ (stored_value & 0xff)))) {
                              
                    int button_number = i / 3 + 1;  // each button is grouped in multiples of 
                    int button_action = i % 3;

                    esphome::api::CustomAPIDevice capi;
                    capi.fire_homeassistant_event("esphome.sonoff_ble", 
                      {{"device", format_hex(btdevice)}, 
                       {"button", to_string(button_number)}, 
                       {"action", id(button_actions)[button_action].c_str()}});
                    ESP_LOGI("Sonoff", "0x%x : %i : %s", btdevice, button_number,id(button_actions)[button_action].c_str());
                    return;
                  }
                }
              }
            }

binary_sensor:
  - platform: template
    id: ble_scan_torun
    internal: true
    entity_category: diagnostic
    device_class: connectivity
    lambda: !lambda |-
      return global_api_server->is_connected();
    on_press:
    - esp32_ble_tracker.start_scan:
        continuous: true
    on_release:
    - esp32_ble_tracker.stop_scan:

Additionally possible to collect list of BLE MAC’s data received from:

globals:
  - id: btdata_id_str
    type: std::vector<std::string>
    restore_value: no
    initial_value: "std::vector<std::string>{}"

switch:
  - platform: template
    name: "BLE List Collect"
    id: ble_list_collect
    restore_mode: RESTORE_DEFAULT_ON
    optimistic: true

button:
  - platform: template
    name: "BLE List Publish"
    on_press:
      - script.execute: ble_list_publish

script:
  - id: ble_list_publish
    then:
      - lambda: |-
          if (id(ble_list_collect).state) {
            ESP_LOGI("BLE", "Address List (%02i)", id(btdata_id_str).size());
            for (auto s: id(btdata_id_str)) {
              ESP_LOGI("BLE", "%s", s.c_str());
            }
          } else {
            ESP_LOGI("BLE", "Address List disabled");
          }


esp32_ble_tracker:
  on_ble_advertise:
    - then:
        - lambda: |-
            if (id(ble_list_collect).state && (x.get_address_type() != BLE_ADDR_TYPE_RANDOM)) {
              std::string address_str = x.address_str();
              for (auto s: id(btdata_id_str)) {
                if (address_str == s) return;
              }
              id(btdata_id_str).push_back(address_str);
              id(ble_list_publish_script).execute();
            }

If want to see all MAC’s in the air - remove (x.get_address_type() != BLE_ADDR_TYPE_RANDOM) condition.

Here is an example of automation:

  automation:
    - id: sonoff_ble_events
      alias: "Sonoff BLE events"
      description: ''
      mode: restart
      trigger:
        - platform: event
          event_type: esphome.sonoff_ble
      condition: []
      variables:
        data: "{{ trigger.event.data }}"
      action:
      - choose:
        - conditions: "{{ data.device == '5a2bcfd9' }}"
          sequence:
            - choose:
              - conditions: "{{ data.button == '1' and data.action == 'short' }}"
                sequence:
                  - action: switch.toggle
                    target:
                      entity_id: 
                        - switch.xxx
1 Like