dalehumby / ESPHome-Apple-Watch-detection

ESPHome BLE Apple Watch presence detection
MIT License
326 stars 16 forks source link

Detect all watches, or just one watch? #9

Open dcgrove opened 2 years ago

dcgrove commented 2 years ago

Hello, I have been using this successfully to detect my apple watch (series 5) for some time now. I recently tested to see if it picked up my wife's watch (series 3) by walking out of range of the ESP32 while my wife was in range and found that did not detect it. Shouldn't it have detected my wife's watch as well? My config is below.

  scan_parameters:
    interval: 1.2s
    window: 500ms
    active: false
  on_ble_advertise:
    - then:
      # Look for manufacturer data of form: 4c00 10 05 YY 98 XXXXXX
      # Where YY can be 01..0F or 20..2F; and XXXXXX is ignored
      - lambda: |-
          optional<int16_t> best_rssi = nullopt;
          for (auto data : x.get_manufacturer_datas()) {
            // Guard against non-Apple datagrams, or those that are too small.
            if (data.data.size() < 4 || data.uuid.to_string() != "0x004C" || data.data[0] != 0x10 || data.data[1] < 5) {
              continue;
            }
            const int16_t rssi = x.get_rssi();
            const uint8_t status_flags = data.data[2] >> 4;  // High nibble
            const uint8_t data_flags = data.data[3];
            if (data_flags == 0x98) {  // Match unlocked Apple Watches
              if (status_flags == 0 || status_flags == 2) {
                best_rssi = max(rssi, best_rssi.value_or(rssi));
                ESP_LOGD("ble_adv", "Found Apple Watch (mac %s) rssi %i", x.address_str().c_str(), rssi);
              } else {
                ESP_LOGD("ble_adv", "Possible Apple Watch? (mac %s) rssi %i, unrecognised status/action flags %#04x", x.address_str().c_str(), rssi, data.data[2]);
              }
            }
          }
          if (best_rssi) {
            id(apple_watch_rssi).publish_state(*best_rssi);
          }

sensor:
  - platform: template
    id: apple_watch_rssi
    name: "$yourname Apple Watch $roomname RSSI"
    device_class: signal_strength
    unit_of_measurement: dBm
    accuracy_decimals: 0
    filters:
      - exponential_moving_average:
          alpha: 0.3
          send_every: 1
    on_value:
      then:
        - lambda: |-
            if (id(apple_watch_rssi).state > $rssi_present) {
              id(room_presence_debounce).publish_state(1);
            } else if (id(apple_watch_rssi).state < $rssi_not_present) {
              id(room_presence_debounce).publish_state(0);
            }
        - script.execute: presence_timeout  # Publish 0 if no rssi received

  - platform: template
    id: room_presence_debounce
    filters:
      - sliding_window_moving_average:
          window_size: 3
          send_every: 1
  - platform: homeassistant
    name: HA RSSI Present Value
    entity_id: input_number.office_rssi_present_value
    id: harssi_present
  - platform: homeassistant
    name: HA RSSI Not Present Value
    entity_id: input_number.office_rssi_not_present
    id: harssi_not_present
binary_sensor:
  - platform: status
    name: "Office BLE Tracker Status"
  - platform: template
    id: room_presence
    name: "$yourname $roomname presence"
    device_class: occupancy
    lambda: |-
      if (id(room_presence_debounce).state > 0.99) {
        return true;
      } else if (id(room_presence_debounce).state < 0.01) {
        return false;
      } else {
        return id(room_presence).state;
      }

script:
  # Publish event every 30 seconds when no rssi received
  id: presence_timeout
  mode: restart
  then:
    - delay: 30s
    - lambda: |-
        id(room_presence_debounce).publish_state(0);
    - script.execute: presence_timeout
dalehumby commented 2 years ago

Hi. Yes, the code should detect any Apple watch that is in range, whether Series 3 or 5.

Two things to try:

  1. Is the Series 3 watch unlocked?
  2. Could you post some logs from when your Series 5 watch goes out of range and only the Series 3 is in range? Could help surface the issue.
neroxps commented 2 years ago

Very good project, but unfortunately, the AppleWatch firmware sold in China is not universal.

Whether the watch is unlocked or locked, the Data flags are 0x18.

image

dalehumby commented 2 years ago

Hi @neroxps - If you want to detect a Watch where the data flag is 0x18, have a look at line 60 in the yaml file. Change

if (data_flags == 0x98) {

to

if (data_flags == 0x98 || data_flags == 0x18) {

If this doesn't work, please send your full yaml, your watch series, and verbose debug log. Verbose logging can be set

logger:
  level: VERY_VERBOSE
dalehumby commented 2 years ago

Hi @dcgrove - I wanted to check if you still needed help diagnosing this issue?