Vent-axia Svara (App controlled extractor fan)

I was able to decompile/read the source code and found a pin number 10135097 … however… my Extractor is now installed and too far from my HA/Pi to try pairing… and to be pair, the mobile app confirms how terrible the BT range is (its about 1m :confounded:)

And yeah, I’m trying to use an ESP32 on the Digital/analog inputs instead…

Hi how did you add the pure sense to home assistant. I cant find my MAC address of the fan

Thanks

I managed to do this quite easily today for my Pure Sense using the BLE Device Tracker Bluetooth LE Tracker - Home Assistant it creates the file known_devices and it will find Pure Sense and give you the MAC ID. I just used 0000 as password and it connected, well… did it? It says it’s discovered and it comes up with the device and a load of entities, but most of them are “unknown”. I took the fan apart and cannot see MAC or PIN. So I think I have it integrated, but I’m unsure whether I can do anything at all with it.

@Paul_Crouch if you pull the fan out you’ll see a sticker on the control board with the MAC address

This is a great integration!

It’s worth noting older Svara’s might not work. I had a Svara that I bought back in 2017, and it would not connect, even when entering the MAC and PIN manually.

It died last week, so I connected Vent-Axia customer services. They sent me a new unit under warranty even though I bought it nearly 7.5 years ago. Big up Vent-Axia customer services!

The new unit wasn’t recognised automatically, but connected straight away when entering the MAC and PIN manually.

We are interested in the scripts… please share!

Apologies for the delay, I had spent a while writing out a tutorial on how to get going with ESPHome etc. but was unable to finish it. When I came back to it my draft message was gone so I gave up.

I’ll make this one brief.

I’m using an M5 Stack Atom Lite and here is my ESPHome yaml for it with MACs, PINs and keys partially redacted using “XX”:

# Define reusable constants here
substitutions:
  name: "iot-atom-ble-proxy-30XXXX"
  friendly_name: Bluetooth Proxy 30XXXX
  tv_mount_mac_address: 34:15:13:DA:XX:XX
  kitchen_extractor_mac_address: 4C:96:2D:12:XX:XX
  bathroom_extractor_mac_address: 58:2b:db:01:XX:XX
  shower_extractor_mac_address: 58:2b:db:01:XX:XX

# The core firmware to build on
packages:
  esphome.bluetooth-proxy: github://esphome/firmware/bluetooth-proxy/m5stack-atom-lite.yaml@main

# Name this node/Atom Lite
esphome:
  name: ${name}
  name_add_mac_suffix: false
  friendly_name: ${friendly_name}

# The encryption key to authenticate this node
api:
  encryption:
    key: XXXX/LOlXzS+XXXXXXXXXXXXXXXXX=

# Wifi credentials
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# Maximum 3 active BLE clients on the Atom Lite (it struggles with 4+)
ble_client:
  # Vent Axia PureAir Sense
  - mac_address: ${kitchen_extractor_mac_address}
    id: KITCHEN_EXTRACTOR
    auto_connect: false
    # Debug logging, shown in the node logs
    on_connect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Connected to Kitchen Extractor");
    # Debug logging, shown in the node logs
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Disconnected from Kitchen Extractor");
  # Vent Axia Svara
  - mac_address: ${bathroom_extractor_mac_address}
    id: BATHROOM_EXTRACTOR
    auto_connect: false
    on_connect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Connected to Bathroom Extractor");
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Disconnected from Bathroom Extractor");
  # Vogels Motion Mount (unrelated to extractors)
  - mac_address: ${tv_mount_mac_address}
    id: TV_MOUNT
    auto_connect: false
    on_connect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Connected to TV mount");
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Disconnected from TV mount");
  # Commented out due to 3 client limit, also a Vent Axia Svara
  #- mac_address: ${shower_extractor_mac_address}
  #  id: SHOWER_EXTRACTOR
  #  auto_connect: false
  #  on_connect:
  #    then:
  #      - lambda: |-
  #          ESP_LOGD("ble_client_lambda", "Connected to Shower Extractor");
  #  on_disconnect:
  #    then:
  #      - lambda: |-
  #          ESP_LOGD("ble_client_lambda", "Disconnected from Shower Extractor");

# Add the sensors that will be discovered by Home Assistant
sensor:
  - platform: ble_rssi
    name: kitchen_extractor_rssi
    mac_address: ${kitchen_extractor_mac_address}
  - platform: ble_rssi
    name: bathroom_extractor_rssi
    mac_address: ${bathroom_extractor_mac_address}
  - platform: ble_rssi
    name: tv_mount_rssi
    mac_address: ${tv_mount_mac_address}

# Add buttons to perform simple trigger actions in Home Assistant
button:
  - platform: template
    name: Boost Kitchen Extractor
    on_press:
        then:
        # Stop the background BLE scanning
        - esp32_ble_tracker.stop_scan:
        # Connect to the PureAir Sense
        - ble_client.connect: KITCHEN_EXTRACTOR
        # Authenticate with PIN (change the value to match yours)
        - ble_client.ble_write:
            id: KITCHEN_EXTRACTOR
            service_uuid: e6834e4b-7b3a-48e6-91e4-f1d005f564d3
            characteristic_uuid: 4cad343a-209a-40b7-b911-4d9b3df569b2
            value: [0xXX, 0x58, 0x82, 0x01]
        # Cancel any current boost time remaining
        - ble_client.ble_write:
            id: KITCHEN_EXTRACTOR
            service_uuid: c119e858-0531-4681-9674-5a11f0e53bb4
            characteristic_uuid: 7c4adc07-2f33-11e7-93ae-92361f002671
            value: [0x00, 0x00, 0x00, 0x00, 0x00]
        # Send the boost command
        - ble_client.ble_write:
            id: KITCHEN_EXTRACTOR
            service_uuid: c119e858-0531-4681-9674-5a11f0e53bb4
            characteristic_uuid: 7c4adc07-2f33-11e7-93ae-92361f002671
            value: [0x01, 0xC4, 0x09, 0x58, 0x02]
        # Disconnect from the Pure Sense
        - ble_client.disconnect: KITCHEN_EXTRACTOR
        # Restart the background BLE scanning
        - esp32_ble_tracker.start_scan:
        # Debug logging, shown in the node logs
        - logger.log: Boost Kitchen Extractor button finished
  - platform: template
    name: Boost Bathroom Extractor
    on_press:
        then:
        # Stop the background BLE scanning
        - esp32_ble_tracker.stop_scan:
        # Connect to the Svara
        - ble_client.connect: BATHROOM_EXTRACTOR
        # Authenticate with PIN (change the value to match yours)
        - ble_client.ble_write:
            id: BATHROOM_EXTRACTOR
            service_uuid: e6834e4b-7b3a-48e6-91e4-f1d005f564d3
            characteristic_uuid: 4cad343a-209a-40b7-b911-4d9b3df569b2
            value: [0xXX, 0x14, 0xf7, 0x01]
        # Cancel any current boost time remaining
        - ble_client.ble_write:
            id: BATHROOM_EXTRACTOR
            service_uuid: c119e858-0531-4681-9674-5a11f0e53bb4
            characteristic_uuid: 118c949c-28c8-4139-b0b3-36657fd055a9
            value: [0x00, 0x00, 0x00, 0x00, 0x00]
        # Send the boost command
        - ble_client.ble_write:
            id: BATHROOM_EXTRACTOR
            service_uuid: c119e858-0531-4681-9674-5a11f0e53bb4
            characteristic_uuid: 118c949c-28c8-4139-b0b3-36657fd055a9
            value: [0x01, 0x60, 0x09, 0x2C, 0x01]
        # Disconnect from the Svara
        - ble_client.disconnect: BATHROOM_EXTRACTOR
        # Restart the background BLE scanning
        - esp32_ble_tracker.start_scan:
        # Debug logging, shown in the node logs
        - logger.log: Boost Bathroom Extractor button finished
  - platform: template
    name: Extend TV Mount
    on_press:
        then:
        - esp32_ble_tracker.stop_scan:
        - ble_client.connect: TV_MOUNT
        - ble_client.ble_write:
            id: TV_MOUNT
            service_uuid: 3e6fe65d-ed78-11e4-895e-00026fd5c52c
            characteristic_uuid: c005fa00-0651-4800-b000-000000000000
            value: [0x00, 0x64]
        - ble_client.disconnect: TV_MOUNT
        - esp32_ble_tracker.start_scan:
        - logger.log: Extend TV Mount button finished
  - platform: template
    name: Retract TV Mount
    on_press:
        then:
        - esp32_ble_tracker.stop_scan:
        - ble_client.connect: TV_MOUNT
        - ble_client.ble_write:
            id: TV_MOUNT
            service_uuid: 3e6fe65d-ed78-11e4-895e-00026fd5c52c
            characteristic_uuid: c005fa00-0651-4800-b000-000000000000
            value: [0x00, 0x00]
        - ble_client.disconnect: TV_MOUNT
        - esp32_ble_tracker.start_scan:
        - logger.log: Retract TV Mount button finished
2 Likes

To achieve this, I use the History Stats integration:

In my configuration.yaml I added the sensor:

sensor:
  - platform: history_stats
    name: Bathroom Light ON 10 mins
    entity_id: light.bathroom_main
    state: "on"
    type: ratio
    end: "{{ now() }}"
    duration:
      minutes: 10

And the automation looks like this:

alias: "Fan: Boost the bathroom extractor"
description: ""
mode: single
triggers:
  - entity_id: sensor.bathroom_light_on_10_mins
    above: "70"
    trigger: numeric_state
conditions: []
actions:
  - repeat:
      while:
        - condition: numeric_state
          entity_id: sensor.bathroom_light_on_10_mins
          above: "70"
      sequence:
        - metadata: {}
          data: {}
          target:
            entity_id: button.iot_atom_ble_proxy_30XXXX_boost_bathroom_extractor
          action: button.press
        - delay:
            hours: 0
            minutes: 1
            seconds: 0
            milliseconds: 0
1 Like

That’s a great code. Thanks!

Do you know how to find out the BLE pin code of the Svensa (new name: pure sense)? It is not displayed anywhere on the device or the chip inside. Reading the uuid does not give any information.

I’ve just checked, it’s not anywhere on the device like the Svara. Instead it’s sent to the app when pairing.

You’ll need a BLE inspector app to read the HEX values. I used GATTBrowser for Android.

Scan for BLE devices, connect to your extractor, mine was listed as “Pure Sense”.

It should present you with a list of services for the device:

Select the one with the matching characteristic ID from the YAML, ending in 569b2.

Read the value as HEX, it should return 00 00 00 00.

Hold the WiFi symbol on the fan until the power LED turns blue.

Read the value again and it should now contain the HEX code of your pin to send as the value in the ESPHome yaml script.

To get the value for a Svara, enter the PIN from the sticker in a Decimal to Hex converter. It will convert the 8 digit number into 3 hex pairs:

Reverse the pairs and add 01 as the last pair.

For example, BC 61 4E would become 4E 61 BC 01.

1 Like

Thanks again! I did already read the value in the correct characteristic ID with a similar app, but I didn’t know I had to do that in Bluetooth connect mode.

Thanks @Fillius - I’ve got the boost button working via my ESPHome, but the PAX integration commands are all unintialised? What format do I need to put the PIN in to that? I’m getting a 9-digit code if I convert my hex to decimal using the format A1B2C3D4, is that correct?

I haven’t used the Pax integration in quite a while, I only needed the boost function and I used to do it with a shell script.

To get the 8 digit pin, take your 4 HEX pairs from ESPhome (A1 B2 C3 D4)

  1. Drop the last pair (should be 01) = A1 B2 C3
  2. Swap the 1st pair and the 3rd pair = C3 B2 A1
  3. Convert to decimal (as one 6 digit hex value) = 12825249

It should give you your 8 digit pin.

My last pair is a 03 :face_with_peeking_eye:

I tried doing the switcharoo anyway and although it gave me an 8-digit code, it still didn’t work.

Interestingly, your ESPHome example does work to enable the boost by using all four parts of the hex code in the order they appear in GATTBrowser.

Is this a Svara or a PureSense you’re trying to connect?

If it’s a Svara then can’t you just get the pin from the sticker on the fan assembly?

If it’s a PureSense then it probably won’t work with the Pax Calima integration as the service/characteristic UUIDs are different.

It’s the Pure Sense. I’ve seen a request on the git page so will follow up there add Svensa fan to supported devices · Issue #51 · eriknn/ha-pax_ble · GitHub

Installed and works great! Question; why did you use the RSSI sensor? Does it have a purpose? Doesn’t it use unnecessary capacity of the esp32/atom lite?

Good point, I had forgotten about that. I initially added it to assist in placement. I had previously had difficulty reaching one of the fans from my Home Assistant server and I needed to know the best place to put the Atom Lite.

@RobPope, did you manage to connect it eventually? I’ve a Svensa fan and I am struggling with finding the PIN code.

Just succeeded: How to connect a Svensa fan · Issue #55 · eriknn/ha-pax_ble · GitHub. Reverse the 4 bytes of the PIN and convert to decimal.