Blue Connect pool measurements

I fiddle with them and found that they where pretty close too, please let me know when you get them closer. I need to add the battery suggestion from fhempy

I use an automation to enable the ‘switch.prefix_sensor_enable’ every 2 hours. Once the reading is done BLE sends a notify that is picked up by the text_sensor. I would add a date function in the lamda part to that and add that to a new template sensor to show it. Besides that the last_updated info from the text_sensor can be shown as secondary info. I think this could be useful: Time Component — ESPHome

@JosePortillo amazing reverse engineering work. regarding the salt and conductivity doubts, I found this in the manual:


@Peerke Peter, great job implementing Jose findings in ESPHome. If I understand correctly (never used ESPHome before) once having the device configured with that code, it would automatically create the entities in HA, right? I hope it won’t be too difficult to add the conductivity/salt sensors once it will be clearer how to interpret them from raw data.

Thanks again to both of you.

I’m on Android, latest version of the app, and I only see Temp, pH, ORP and Salinity (g/l).

It automatically creates the (template) sensors and button when uploading the code to your ESPHome device. The esp part is specific for mine wt32eth01, if you have another device you should adjust that to match the hardware. I used a manual IP address to make sure it get the same address all the time. This is because I use it outside on a isolated network segment that causes the multicast-discovery of the unit to fail. That results in an off-line state. With the manual IP (bit more work) makes sure my device is reachable and the ethernet connection is safe guarded if someone would put the cable in a laptop

After the activation of your ESP32 device, you can create an automation to trigger the reading (or do that in the UI with the button)

If you can share your raw _hex (found in the console logs) and do a manual reading in the app just before or after the reading from HA we can see if that matches the findings earlier in the post.
( I don’t have such a device, I only have one without salt)
→ these 2 lines print the raw reading in the console:

std::string rawhex = format_hex_pretty((uint8_t *) x.c_str(), x.size()).c_str();
ESP_LOGD(“raw_hex”, “%s”, rawhex.c_str());

First of all, thanks for the great work in reverse engineering the BLE communications. After setting up the pool last week, I’ve been playing around with the ESPHome code of @Peerke. For some reason, I can’t get it to connect to the Blueconnect.

When using a raspberry pi, I can get the raw data using gatttool:

root@raspberrypi:~/ble-scanner# gatttool -b 00:A0:50:44:XX:XX --char-write-req --handle=0x0014 --value=0100 --char-write-req --handle=0x0012 --value=01 --listen
Characteristic value was written successfully
Notification handle = 0x0014 value: 33 15 09 35 07 58 09 00 00 15 0e 1a

I’m expecting the esphome to connect to the sensor when it starts, and update the values. However, I don’t see any values (in the very_verbose logging output). How should I trigger the esphome to connect and get values from the sensor?

Hi Nick,

What i found out during testing is the following:

  • The device can only connect to 1 app (either the app on your phone or the BLE hardware)
  • when connecting using ESPHOME it stays connected and no more reading can be done (at least I couldn’t get that to work. How it fixed that is by enabling and disabling BLE client on ESP32 (using the internal ESPHOME button) and the switch turns the button on/off from HA

When you need a reading:

  • enable BLE, wait 2 sec to settle the hardware
  • register with – register service_uuid: ‘F3300001-F0A2-9B06-0C59-1BC4763B5C00’ and char ‘F3300002-F0A2-9B06-0C59-1BC4763B5C00’
    This takes a few seconds to do anything, the device is slow

The device will return data (once the reading is complete) at:
service_uuid: ‘F3300001-F0A2-9B06-0C59-1BC4763B5C00’ at char ‘F3300003-F0A2-9B06-0C59-1BC4763B5C00’
I catch that using the text_sensor

You need to get the connect-disconnect sequence right and use the correct uuid’s (in esphome i had to string-ify them (put quotes around the uuid)
I tried to use the app after a reading and if that failed my esp32 was still connected… then i filled with the uuid’s and got the communication flowing.

I have HA on a RaspPi and use the HA ESPHome interface and console to debut thing. didn’t try the commandline.

Hopes this helps, keep me updates on your findings.
(ps took me a while to get this figured out)

And in your ESPhome secrets, there’s something like this?

blueriiot_mac: "00:A0:50:44:XX:XX"
blueriiot_nameprefix: "blueriiot"
blueriiot_idprefix: "blueriiot"

When I start my ESPhome, I get these loglines (related to BLE):

[13:13:57][C][esp32_ble:238]: ESP32 BLE:
[13:13:57][C][esp32_ble:240]:   MAC address: 40:22:D8:78:YY:YY
[13:13:57][C][esp32_ble:241]:   IO Capability: none
[13:13:57][C][esp32_ble_tracker:591]: BLE Tracker:
[13:13:57][C][esp32_ble_tracker:592]:   Scan Duration: 300 s
[13:13:57][C][esp32_ble_tracker:593]:   Scan Interval: 320.0 ms
[13:13:57][C][esp32_ble_tracker:594]:   Scan Window: 30.0 ms
[13:13:57][C][esp32_ble_tracker:595]:   Scan Type: ACTIVE
[13:13:57][C][ble_client:027]: BLE Client:
[13:13:57][C][ble_client:028]:   Address: 00:A0:50:44:XX:XX

In HA, the “Blueriiot enable” switch is set to on. I tried turning that on and off several times, but all I get in the logging of ESPhome is:

[13:15:00][D][switch:016]: 'blueriiot Enable' Turning OFF.
[13:15:00][D][switch:055]: 'blueriiot Enable': Sending state OFF

(And the same for ON).

I will play around with this a bit more. I’m sure it’s something small I missed.

In my secrets there is something simular.
nameprefix: BlueConnect Pool Sensor
for the idprefix I used the ID of the device.

Looks from your logs that the switch doesn’t press the internal button.
Just a thought that it might be because the id and name are the same?

This is my log (not at Debug level so its missing the conversion messages.
Checked the sensors and they were updated at the time of disconnect.

Enabled the switch in HA

[21:21:38][I][app:102]: ESPHome version 2023.5.5 compiled on Jun  2 2023, 12:07:37
[21:21:52][I][esp32_ble_client:064]: [0] [00:A0:50:XX:XX:XX] 0x00 Attempting BLE connection
[21:21:53][I][ble_text_sensor:034]: [BlueConnect Pool Sensor reading data] Connected successfully!
[21:21:54][I][esp32_ble_client:196]: [0] [00:A0:50::XX:XX:XX] Connected
--> took the reading and got the reply
[21:22:07][I][ble_client:041]: [00:A0:50::XX:XX:XX] Disabling BLE client.
[21:22:07][W][ble_text_sensor:040]: [BlueConnect Pool Sensor reading data] Disconnected!

Thanks for everyone contributing here. The reverse engineering work and ESPHome implementation is really useful and saves a lot of work!

I was wondering if I could use the code from @Peerke to poll two sensors by simply modifying the code a little bit:

substitutions:
  # 1st Blueconnect
  blueriiot1_mac: 'xx:yy'
  blueriiot1_name_prefix: 'pool'
  blueriiot1_id_prefix: 'pl'

  # 2nd Blueconnect
  blueriiot2_mac: 'zz:zz'
  blueriiot2_name_prefix: 'spa'
  blueriiot2_id_prefix: 'sp'
  
  # send true 0x01 to this service ID
  blueriiot_send_service_uuid: 'F3300001-F0A2-9B06-0C59-1BC4763B5C00'
  blueriiot_send_characteristic_uuid: 'F3300002-F0A2-9B06-0C59-1BC4763B5C00'
  
  # notification is recieved on this Service ID                  
  blueriiot_recieve_service_uuid: 'F3300001-F0A2-9B06-0C59-1BC4763B5C00'
  blueriiot_recieve_characteristic_uuid: 'F3300003-F0A2-9B06-0C59-1BC4763B5C00'

esphome:
  name: "poolsensors"
  friendly_name: Poolsensors

esp32:
  board: m5stack-atom
  framework:
    type: arduino

# Enable logging
logger:
  #level: VERY_VERBOSE
  level: INFO

# Enable Home Assistant API
api:
  password: "1234"
 
ota:
  password: "1234"

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


esp32_ble_tracker:
  scan_parameters:
    active: false
    interval: 1000ms
    window: 700ms

  on_ble_advertise:
    - mac_address: ${blueriiot1_mac}
      then:
        - logger.log: 
            format: "Found 1st sensor"
            level: "INFO"

    - mac_address: ${blueriiot2_mac}
      then:
        - logger.log: 
            format: "Found 2nd sensor"
            level: "INFO"

ble_client:
  # 1st Sensor
  - mac_address: ${blueriiot1_mac}
    id: ble_client_${blueriiot1_id_prefix}
    on_connect: 
      then:
        - logger.log: 
            format: "Connected to 1st sensor"
            level: "INFO"
        - lambda: "id(binary_sensor_${blueriiot1_id_prefix}_connected).publish_state(true);"
        - delay: 2s
        - button.press: button_${blueriiot1_id_prefix}_doreading
    on_disconnect: [lambda: "id(binary_sensor_${blueriiot1_id_prefix}_connected).publish_state(false);"]

  # 2nd Sensor
  - mac_address: ${blueriiot2_mac}
    id: ble_client_${blueriiot2_id_prefix}
    on_connect: 
      then:
        - logger.log: 
            format: "Connected to 2nd sensor"
            level: "INFO"
        - lambda: "id(binary_sensor_${blueriiot2_id_prefix}_connected).publish_state(true);"
        - delay: 2s
        - button.press: button_${blueriiot2_id_prefix}_doreading
    on_disconnect: [lambda: "id(binary_sensor_${blueriiot2_id_prefix}_connected).publish_state(false);"]
    

binary_sensor:
# 1st Sensor  
  - platform: template
    id: binary_sensor_${blueriiot1_id_prefix}_connected
    name: ${blueriiot1_name_prefix} Status
    device_class: connectivity
    entity_category: diagnostic

# 2nd Sensor (optional)
  - platform: template
    id: binary_sensor_${blueriiot2_id_prefix}_connected
    name: ${blueriiot2_name_prefix} Status
    device_class: connectivity
    entity_category: diagnostic

switch:
  # 1st Sensor
  - platform: ble_client
    ble_client_id: ble_client_${blueriiot1_id_prefix}
    name: "${blueriiot1_name_prefix} Enable"
    id: switch_${blueriiot1_id_prefix}_enable
  
  # 2nd Sensor (optional)
  - platform: ble_client
    ble_client_id: ble_client_${blueriiot2_id_prefix}
    name: "${blueriiot2_name_prefix} Enable"
    id: switch_${blueriiot2_id_prefix}_enable

sensor:
  # 1st Sensor
  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_temperature
    name: ${blueriiot1_name_prefix} Temperature
    # Optional variables:
    unit_of_measurement: "°C"
    icon: "mdi:water-percent"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
  
  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_ph
    name: ${blueriiot1_name_prefix} PH
    # Optional variables:
    #unit_of_measurement: "°C"
    #icon: "mdi:water-percent"
    #device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1

  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_orp
    name: ${blueriiot1_name_prefix} Chlor
    # Optional variables:
    #unit_of_measurement: "°C"
    #icon: "mdi:water-percent"
    device_class: "voltage"
    state_class: "measurement"
    accuracy_decimals: 1
    
  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_bat
    name: ${blueriiot1_name_prefix} Battery
    # Optional variables:
    #unit_of_measurement: "°C"
    #icon: "mdi:water-percent"
    device_class: "battery"
    state_class: "measurement"
    accuracy_decimals: 1
  # END 1st Sensor

  # 2nd Sensor (optional)
  - platform: template 
    id: sensor_${blueriiot2_id_prefix}_temperature
    name: ${blueriiot2_name_prefix} Temperature
    # Optional variables:
    unit_of_measurement: "°C"
    icon: "mdi:water-percent"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
  
  - platform: template 
    id: sensor_${blueriiot2_id_prefix}_ph
    name: ${blueriiot2_name_prefix} PH
    # Optional variables:
    #unit_of_measurement: "°C"
    #icon: "mdi:water-percent"
    #device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1

  - platform: template 
    id: sensor_${blueriiot2_id_prefix}_orp
    name: ${blueriiot2_name_prefix} Chlor
    # Optional variables:
    #unit_of_measurement: "°C"
    #icon: "mdi:water-percent"
    device_class: "voltage"
    state_class: "measurement"
    accuracy_decimals: 1
    
  - platform: template 
    id: sensor_${blueriiot2_id_prefix}_bat
    name: ${blueriiot2_name_prefix} Battery
    # Optional variables:
    #unit_of_measurement: "°C"
    #icon: "mdi:water-percent"
    device_class: "battery"
    state_class: "measurement"
    accuracy_decimals: 1
  # ENDE 2nd

    
##
## Blue Connect
# 1st Sensor
button:
  - platform: template
    id: button_${blueriiot1_id_prefix}_doreading
    name: ${blueriiot1_name_prefix} do reading
    internal: true
    on_press:
      then:
        - logger.log: 
            format: "Writing to 1st sensor..."
            level: "INFO"
        - ble_client.ble_write:
            id: ble_client_${blueriiot1_id_prefix}
            service_uuid: ${blueriiot_send_service_uuid}
            characteristic_uuid: ${blueriiot_send_characteristic_uuid}
            # A lambda returning an std::vector<uint8_t>.
            value: !lambda |-
              return {0x01};

# 2nd Sensor
  - platform: template
    id: button_${blueriiot2_id_prefix}_doreading
    name: ${blueriiot2_name_prefix} do reading
    internal: true
    on_press:
      then:
        - logger.log: 
            format: "Writing to 2nd sensor..."
            level: "INFO"
        - ble_client.ble_write:
            id: ble_client_${blueriiot2_id_prefix}
            service_uuid: ${blueriiot_send_service_uuid}
            characteristic_uuid: ${blueriiot_send_characteristic_uuid}
            # A lambda returning an std::vector<uint8_t>.
            value: !lambda |-
              return {0x01};

text_sensor:
  # 1st Sensor
  - platform: ble_client
    id: ${blueriiot1_id_prefix}_reading_data
    name: ${blueriiot1_name_prefix} reading data
    internal: true
    ble_client_id: ble_client_${blueriiot1_id_prefix}
    service_uuid: ${blueriiot_recieve_service_uuid}
    characteristic_uuid: ${blueriiot_recieve_characteristic_uuid}
    notify: true
    update_interval: never
    on_notify:
      then:
       lambda: |-
          std::string rawhex = format_hex_pretty((uint8_t *) x.c_str(), x.size()).c_str();
          ESP_LOGD("raw_hex", "%s", rawhex.c_str());

          float temperature = (float)((int16_t)(x[2]<< 8) + x[1])/100;
          ESP_LOGD("temp", "%f", temperature);
          id(sensor_${blueriiot1_id_prefix}_temperature).publish_state(temperature);
              
          float raw_ph = (float)( (int16_t) (x[4]<< 8) + x[3]) ;
          float ph = (float)( (int16_t) (2048 - raw_ph)) / 232  + 7 ; 
          ESP_LOGD("ph", "%f", ph);
          id(sensor_${blueriiot1_id_prefix}_ph).publish_state(ph);
          
          float orp = (float)( (int16_t) (x[6]<< 8) + x[5]) / 3.86 ;
          ESP_LOGD("orp", "%f", orp);
          id(sensor_${blueriiot1_id_prefix}_orp).publish_state(orp);
          
          float bat = (float)( (int16_t) x[11]) ;
          ESP_LOGD("bat", "%f", bat);
          id(sensor_${blueriiot1_id_prefix}_bat).publish_state(bat);
          
          
          
          id(switch_${blueriiot1_id_prefix}_enable).turn_off();
    # END 1st Sensor

    # 2nd Sensor
  - platform: ble_client
    id: ${blueriiot2_id_prefix}_reading_data
    name: ${blueriiot2_name_prefix} reading data
    internal: true
    ble_client_id: ble_client_${blueriiot2_id_prefix}
    service_uuid: ${blueriiot_recieve_service_uuid}
    characteristic_uuid: ${blueriiot_recieve_characteristic_uuid}
    notify: true
    update_interval: never
    on_notify:
      then:
       lambda: |-
          std::string rawhex = format_hex_pretty((uint8_t *) x.c_str(), x.size()).c_str();
          ESP_LOGD("raw_hex", "%s", rawhex.c_str());

          float temperature = (float)((int16_t)(x[2]<< 8) + x[1])/100;
          ESP_LOGD("temp", "%f", temperature);
          id(sensor_${blueriiot2_id_prefix}_temperature).publish_state(temperature);
              
          float raw_ph = (float)( (int16_t) (x[4]<< 8) + x[3]) ;
          float ph = (float)( (int16_t) (2048 - raw_ph)) / 232  + 7 ; 
          ESP_LOGD("ph", "%f", ph);
          id(sensor_${blueriiot2_id_prefix}_ph).publish_state(ph);
          
          float orp = (float)( (int16_t) (x[6]<< 8) + x[5]) / 3.86 ;
          ESP_LOGD("orp", "%f", orp);
          id(sensor_${blueriiot2_id_prefix}_orp).publish_state(orp);
          
          float bat = (float)( (int16_t) x[11]) ;
          ESP_LOGD("bat", "%f", bat);
          id(sensor_${blueriiot2_id_prefix}_bat).publish_state(bat);
          
          
          
          id(switch_${blueriiot2_id_prefix}_enable).turn_off();
    # END 2nd Sensor

At the moment, it doesn’t work reliably for me because one of the sensors won’t give me any results. Not sure if it’s because of a problem in the code (my 1st yaml :wink: ) or maybe a bad reception.

Would you mind telling us how you’d find the bluetooth connection information / payload in the app?
I also use BlueConnect devices and due to the hard work in this thread, I’m able to read them cloudless now :wink:

But I also own a few other devices which only communicate with their vendor’s apps and I would really like to control them using ESPHome.

Which tools did you use to reverse the BlueConnect app? Or did you manage to “sniff” the outgoing BLE traffic from your tablet/phone?

I was just about to change the id_prefix so it’s not the same as the name_prefix, when I noticed the ESP device got one measurement to HA. However, it was from the day before, so I couldn’t get the logging back. I think I was playing around with the enable button at the time, so perhaps that’s the solution to my problem.

From what I understand, when enabling the ble_client (setting the switch in HA to enabled), the measurement should show up within minutes. How did you setup the automation? I played around with this a bit yesterday, turning it on every hour for 30 minutes, or turning it on every 10 minutes for 5 minutes. Both without result.

@Peerke I solved my issue by adding the ble scan_parameters from @oxident yaml file to my configuration. My final esphome file can be found here:

Hi Peter, sorry for the delay, I had time today to tinker with ESPHome and had to learn some things before configuring the device. Luckily, it was easier than I expected. I’m using an M5Stack Atom Lite device, and the platform is supported by esphome. I’ll tag @JosePortillo in case he wants to take a look at data.

Here’s the log lines you asked for, I hope the fourth sensor regarding the salinity can be decoded:

INFO Starting log output from blueconnect-m5.axel.dom using esphome API
INFO Successfully connected to blueconnect-m5.axel.dom
[15:26:16][I][app:102]: ESPHome version 2023.5.5 compiled on Jun  4 2023, 15:25:35
[15:26:17][I][main:266]: Reading Blueconnect
[15:26:29][I][raw_hex:152]: 33.FC.08.75.07.80.03.CC.00.C4.0D.19 (12)
[15:26:29][I][temp:155]: 23.000000
[15:26:29][I][ph:160]: 7.599138
[15:26:29][I][orp:164]: 232.124359
[15:26:29][I][bat:168]: 25.000000
[15:26:29][I][ble_client:041]: [00:A0:50:A6:76:DC] Disabling BLE client.
[15:26:29][W][ble_text_sensor:040]: [Pool reading data] Disconnected!
[15:26:49][I][esp32_ble_client:064]: [0] [00:A0:50:A6:76:DC] 0x00 Attempting BLE connection
[15:26:49][I][ble_text_sensor:034]: [Pool reading data] Connected successfully!
[15:26:51][I][esp32_ble_client:196]: [0] [00:A0:50:A6:76:DC] Connected
[15:26:51][I][main:232]: Connected to Blueconnect
[15:26:53][I][main:266]: Reading Blueconnect
[15:27:37][I][raw_hex:152]: 33.F2.08.71.07.81.03.CC.00.C4.0D.1A (12)
[15:27:37][I][temp:155]: 22.900000
[15:27:37][I][ph:160]: 7.616379
[15:27:37][I][orp:164]: 232.383423
[15:27:37][I][bat:168]: 26.000000
[15:27:37][I][ble_client:041]: [00:A0:50:A6:76:DC] Disabling BLE client.
[15:27:37][W][ble_text_sensor:040]: [Pool reading data] Disconnected!
[15:50:37][I][esp32_ble_client:064]: [0] [00:A0:50:A6:76:DC] 0x00 Attempting BLE connection
[15:50:39][I][ble_text_sensor:034]: [Pool reading data] Connected successfully!
[15:50:42][I][esp32_ble_client:196]: [0] [00:A0:50:A6:76:DC] Connected
[15:50:42][I][main:232]: Connected to Blueconnect
[15:50:44][I][main:266]: Reading Blueconnect
[15:50:55][I][raw_hex:152]: 33.F2.08.4F.07.3C.03.CC.00.C4.0D.19 (12)
[15:50:55][I][temp:155]: 22.900000
[15:50:55][I][ph:160]: 7.762931
[15:50:55][I][orp:164]: 214.507767
[15:50:55][I][bat:168]: 25.000000
[15:50:55][I][ble_client:041]: [00:A0:50:A6:76:DC] Disabling BLE client.
[15:50:55][W][ble_text_sensor:040]: [Pool reading data] Disconnected!

Hi @nheinemans,

Below is my automation.
What I do is just enable the switch, the esp device does the disabling. With me, the reading takes around 10sec.

alias: Swimmingpool reading every 2 hours
description: ""
trigger:
  - platform: time_pattern
    hours: /2
condition:
  - condition: state
    entity_id: switch.blueconnect_pool_sensor_enable
    state: "off"
    for:
      hours: 0
      minutes: 1
      seconds: 0
action:
  - service: switch.turn_on
    data: {}
    target:
      entity_id: switch.blueconnect_pool_sensor_enable
mode: single

Yes, that should be possible.
Make sure that the id and name are unique for both devices and that the HA sensors are also unique.
Then you can enable them 1 by 1 in HA
or
you could do that in the code at the end of text_sensor 1 and activate the second button after turning the first off. They should then go 1 by 1. I would add a small delay allowing the BLE to really disconnect.

Maybe the scan_parameters are needed here to control better timing. Not sure as with my hardware those are not needed, for 2 others they where needed.

I will see what comes out of the calculation using the one provided earlier in this thread. What does you app give for reading so there is a reference to calculate towards?

Here is a repo for the MQTT integration, they reversed-engineered the API. In there should also be a calculation of the received data.
GitHub - LordMike/MBW.BlueRiiot2MQTT: Utility to map between Blue Riiots pool API, and Home Assistant MQTT ( is have it installed to compare the API vs ESP32 data. Never figured out how they get the data back/converted with that integration

Salinity: 2.7 g/l

I was looking for methods to convert conductivity to salinity, it seems it’s not really linear because temperature affects it: Linear Conversion Of Conductivity To Salinity | Environmental ...

I tested this calculator, using values reported by Jose with his assumptions on the linear formula, and it seems to be close to the expected values:

The formulas are in the source code of that page, but it’s not a simple formula, I don’t know if we can do that stuff in ESPHome…maybe in HA we could…

This is a linear equation with a 5% error range for the estimate (good enough if you ask me), the conductivity provided by the device seems in micro, so it has to be first divided by 1000 I guess.

I’ve been using that one since it was released. The problem is that it’s cloud-based, it gets the values from the blueriiot cloud service (didn’t look at the code deeply). From what I understand, it doesn’t reverse engineer the values, it gets them straight from the API. It works beautifully, but it’s cloud based so I prefer to read them locally, if possible.

I think there might be a small design issue with the yaml: It will automatically connect to the Blueconnect upon boot, won’t it?

In this case, it tries to get a reading and if it somehow fails, it tries repeatedly to re-connect.

Does someone know if it’s possible to start the connection manually after boot up?

As far as I can tell, the switch will be reverted to false automatically after the measurement is done and the connection has been closed (hopefully).

If it doesn’t then you’ve got a problem :frowning:

I think you can add this option to the ble_client switch:

restore_mode: ALWAYS_OFF

Edit: This doesn’t seem to work with this particular switch, the restore_mode is already ALWAYS_OFF by default. This is probably just the way ble_client works, it tries to connect to the mac address after boot up.