Pool Monitor Device Yieryi BLE-YC01

Hi @RubenKona
Was you able to do some tests ?
I have a similar sensor, the BLE-C600 with more inputs (EC / TDS / SALT / PH / ORP and temp).
I did convert your c++ code to python one and for now i’m only able to find the ORP value on the data received.

Anas

Hi @anasm2010 , can you share your python code? Maybe I can test it with my two floating devices.

Thanks

Here is a script example that can decode the full packet.
For now i’m locking for a solution to disable the auto-turn off function … Any idea?

import asyncio

from bleak import BleakClient

import time

from decodeInt import Messure

address = "C0:00:00:00:8A:D1"

# MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb"

MODEL_NBR_UUID = "0000180D-0000-1000-8000-00805F9B34FB"

Battery_Service = "00002a19-0000-1000-8000-00805f9b34fb"

Vendor_specific = "0000ff10-0000-1000-8000-00805f9b34fb"

"""

00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile

00001801-0000-1000-8000-00805f9b34fb (Handle: 8): Generic Attribute Profile

0000180a-0000-1000-8000-00805f9b34fb (Handle: 12): Device Information

0000180f-0000-1000-8000-00805f9b34fb (Handle: 31): Battery Service

0000ff01-0000-1000-8000-00805f9b34fb (Handle: 36): Vendor specific

"""

class Messure(object):

    def __init__(self, frame : str) -> None:

        self.frame = frame

       

        self.packet = self.decode(frame)

       

        self.data = self.packet[0]

        self.constant = self.packet[1]

        self.product_name_code = self.packet[2]

       

        self.hold_reading = self.packet[17] >> 4

        self.backlight_status = (self.packet[17] & 0x0F) >> 3

        self.battery = self.decode_position(15)

       

        self.ec = self.decode_position(5)

        self.tds = self.decode_position(7)

        self.salt_tds = self.decode_position(9)

        self.salt_sg = self.decode_position(11) ##TO DO

        self.ph = self.decode_position(3)/100

        self.orp = self.decode_position(20)

        self.temperature = self.decode_position(13)/10

       

    def decode(self, byte_frame : bytes ):

        frame_array = [int(x) for x in byte_frame]

        size = len(frame_array)

        for i in range(size-1, 0 , -1):

            tmp=frame_array[i]

            hibit1=(tmp&0x55)<<1

            lobit1=(tmp&0xAA)>>1

            tmp=frame_array[i-1]

            hibit=(tmp&0x55)<<1

            lobit=(tmp&0xAA)>>1

            frame_array[i]=0xff -(hibit1|lobit)

            frame_array[i-1]= 0xff -(hibit|lobit1)

       

        return frame_array

   

    def reverse_bytes(self, bytes : list):

        return (bytes[0] << 8) + bytes[1]

       

    def decode_position(self,idx):

        return self.reverse_bytes(self.packet[idx:idx+2])

   

    def show_values(self):

        return_string = f"h=[{self.hold_reading}/bl={self.backlight_status}/B={self.battery}] EC={self.ec:4}  TDS={self.tds:4}  SALT(TDS)={self.salt_tds:4}  SALT(S.G.)={self.salt_sg:4}  pH={self.ph:4}  ORP(mV)={self.orp:4}  Temperature(C)={self.temperature:4} "

        return return_string

   

async def main(address):

    async with BleakClient(address) as client:

        print(f"Connected: {client.is_connected}")

       

        paired = await client.pair(protection_level=2)

        print(f"Paired: {paired}")

       

        old_time = time.time()

        print(time.time() - old_time)

        while True:

            model_number = await client.read_gatt_char(Vendor_specific)    

            print(Messure(model_number).show_values())

            time.sleep(1)

asyncio.run(main(address))
2 Likes

Try to check the saved data buttom and assign an interval, it may keep it alive.

Yes thank you, it seems to work on the app. However, once I switch to the python script it goes off again after 5min. I assume the app is sending a request that force it to stay on … I need to find what.

2 Likes

Hi,

Here is a functional code for an ESP32 with Esphome associated with Home Assistant. Every 30 minutes, the BLE device is polled for new datas.

time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      # Turn on BLE client every 30 minutes for 2 minutes
      - seconds: 0
        minutes: /30
        then:
          - switch.turn_on: ble_switch
          - delay: 2min
          - switch.turn_off: ble_switch


esp32_ble_tracker: #### Stop the active scan ####
  scan_parameters: 
    active: false

######################################################
##                                                  ##
##   To initiate to connection with the BLE device  ##
##                                                  ##
######################################################

ble_client: 
  - mac_address: YO:UR:_A:DD:RE:SS #Use the MAC address of your BLE device
    id: ble_yc01
    on_connect: #### Actions to perform when connecting to the BLE device ####
      then:
      #### Wait until characteristic is discovered ####
        - wait_until:   
            lambda: |-
              esphome::ble_client::BLEClient* client = id(ble_yc01);
            
              auto service_uuid = 0xFF01;
              auto char_uuid = 0xFF02;
              
              //#### When waiting for connection, we extract the available characteristics ####
              esphome::ble_client::BLECharacteristic* chr = client->get_characteristic(service_uuid, char_uuid);
              
              return chr != nullptr;
              
        #### Official connection to the BLE device with the desired characteristic ####
        - lambda: |- 
            ESP_LOGD("ble_client_lambda", "Connected to BLE-YC01");
            
            esphome::ble_client::BLEClient* client = id(ble_yc01);
            
            auto service_uuid = 0xFF01; 
            auto char_uuid = 0xFF02;
            
            esphome::ble_client::BLECharacteristic* chr = client->get_characteristic(service_uuid, char_uuid);
            
            if (chr == nullptr) {
                ESP_LOGW("ble_client", "[0xFF01] Characteristic not found.  State update can not be written.");
            }
        
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client", "Disconnected from BLE-YC01");
            
######################################################
##                                                  ##
##     Sensors associated with the BLE device       ##
##                                                  ##
######################################################
            
sensor: #### Template sensor as their values are publish from a lambda or the BLE client ####

  - platform: template
    name: "BLE-YC01 EC"
    id: ble_yc01_ec_sensor
    unit_of_measurement: "µS/cm"
    accuracy_decimals: 0
    state_class: measurement
    icon: mdi:water-opacity
    
  - platform: template
    name: "BLE-YC01 TDS"
    id: ble_yc01_tds_sensor
    unit_of_measurement: "ppm"
    accuracy_decimals: 0
    state_class: measurement
    icon: mdi:water-opacity
    
  - platform: template
    name: "BLE-YC01 Temperature"
    id: ble_yc01_temperature_sensor
    unit_of_measurement: "F"
    accuracy_decimals: 1
    state_class: measurement
    device_class: temperature
    
  - platform: template
    name: "BLE-YC01 ORP"
    id: ble_yc01_orp_sensor
    unit_of_measurement: "mV"
    accuracy_decimals: 0
    state_class: measurement
    device_class: voltage
    
  - platform: template
    name: "BLE-YC01 pH"
    id: ble_yc01_ph_sensor
    unit_of_measurement: "pH"
    accuracy_decimals: 2
    state_class: measurement
    icon: mdi:ph

  - platform: template
    name: "BLE-YC01 batterie"
    id: ble_yc01_battery
    unit_of_measurement: "%"
    accuracy_decimals: 0
    state_class: measurement
    device_class: battery
    icon: mdi:battery
    
  - platform: ble_client ####  Sensor required to manage values coming from the BLE device ####
    ble_client_id: ble_yc01
    id: ble_yc01_sensor
    internal: true
    service_uuid: FF01
    characteristic_uuid: FF02
    notify: true
    #### Lambda to decode values and push to the associated sensors ####
    lambda: |-
    
      if (x.size() == 0) return NAN;
      
      //ESP_LOGD("ble_client.receive", "value received with %d bytes: [%.*s]", x.size(), x.size(), &x[0]); // ####  Useful for debugging ####
 
 
      // ### DECODING ###
      uint8_t tmp = 0;
      uint8_t hibit = 0;
      uint8_t lobit = 0;
      uint8_t hibit1 = 0;
      uint8_t lobit1 = 0;
      auto message = x;
      
      for (int i = x.size() -1 ; i > 0; i--) {
        tmp=message[i];
        hibit1=(tmp&0x55)<<1;
        lobit1=(tmp&0xAA)>>1;
        tmp=message[i-1];	
        hibit=(tmp&0x55)<<1;
        lobit=(tmp&0xAA)>>1;
        
        message[i]=~(hibit1|lobit);
        message[i-1]=~(hibit|lobit1);

      }
      
      //ESP_LOGD("ble_client.receive", "value received with %d bytes: [%.*s]", message.size(), message.size(), &message[0]); // #### For debug ####


      // #### Extraction of individual values ####
      auto temp = ((message[13]<<8) + message[14]);
      auto ph = ((message[3]<<8) + message[4]);
      auto orp = ((message[20]<<8) + message[21]);
      auto battery = ((message[15]<<8) + message[16]);
      auto ec = ((message[5]<<8) + message[6]);
      auto tds = ((message[7]<<8) + message[8]);
      
      // #### Sensors updated with new values
      id(ble_yc01_temperature_sensor).publish_state(((temp/10.0) * (9.0/5.0)) + 32.0);
      id(ble_yc01_ph_sensor).publish_state(ph/100.0);
      id(ble_yc01_orp_sensor).publish_state(orp);
      id(ble_yc01_battery).publish_state(battery/45);
      id(ble_yc01_ec_sensor).publish_state(ec);
      id(ble_yc01_tds_sensor).publish_state(tds);
       
      return 0.0; // this sensor isn't actually used other than to hook into raw value and publish to template sensors


switch:  #### To switch on and off the communication with the BLE device ####
  - platform: ble_client
    id: ble_switch
    ble_client_id: ble_yc01
    name: "Enable BLE-YC01"

Here is a way to calculate free chlorine in HA. For now, I can’t figure out how to integrate it directly in Esphome.

The formula comes from raspipool github: https://github.com/segalion/raspipool

sensors:
  - platform: template
    sensors:
      chlore_libre_spa:
        value_template: "{{ ( 0.23 * (1 + 0 / 100 ) * ( 14 - states('sensor.ble_yc01_ph')|float(0.0)) ** ( 1 / (400 - states('sensor.ble_yc01_orp')|float(0.0)) ) * ( states('sensor.ble_yc01_ph')|float(0.0) -4.1) ** ( ( states('sensor.ble_yc01_orp')|float(0.0) - 516)/145) + 10.0 ** ( (states('sensor.ble_yc01_orp')|float(0.0) + states('sensor.ble_yc01_ph')|float(0.0) * 70 -1282) / 40 ) ) |round(1) }}"
        unit_of_measurement: ppm
        icon_template: mdi:react
        friendly_name: "Chlore libre estimé spa"
5 Likes

Be careful to switch off the comm with the BLE device before uploading in the ESP32. The BLE device seems to freeze sometimes if the connection isn’t properly disconnected. You will have to remove the batteries to restart the device… A little bit annoying.

2 Likes

Thank you bro. It Work for me.

1 Like

Great!!!
I’m looking for a orp and ph sensor for my swimming pool, I’m automating everything. do you recomend it? It looks so good but in amazon there are terrible descriptions.
Please let me know.
Thanks

I would personally take a look to the Blueriiot products. I bought the BLE-YC01 just for testing given its price and can see massive differences in the measures even enough I made the calibration (which I am pending of repeating just in case). The Blueriiiot is perfectly integrated with HA too although it is far more expensive. Take a look to the integration and forum and make your own decision. I have been running the Blueriiot for +2y and my pool have never been better. I have a full automation of the pumps for filtering and chemical’s (bleach, etc).

Thanks, I’ve built an automated system with injectors of ph and chrorine,pumps with vvd and also ph sensor but with orp sensor I have some problems and after checking 2 orp electrodes nothing works, i used drobot kit but after several hours working, values get crazy.
This is why i’m looking for something different without invest too much money.
I’ve taked a look blueriot devices but they are quite expensive and the probe need to be changed every 2 years with more than 100€ for this part.

If the values of ble-yc01 are correct and it works at least 1 year it deserves it.

Thanks

So the code that was posted by kymote125 also works for my BLE enabled yinmik branded pH/ec handheld tester. Everything worked with essentially no modification required.

The model I have is BLE-9908, though there are several other BLE models with slightly different features that may also work.

There is potential in modifying these handheld testers to function as part of a hydroponics system or possibly an aquarium. I may make a new thread if I’m able to reliably have mine return 24/7 pH, EC, and temp readings from my hydroponic system into HA.

1 Like

I’ve also received my BLE-YC01, and I’ve integrated it in my swimming-pool.
I’ve calibrated PH and also ORP, but I don’t know why the ORP value doesn’t match with the expected, with the stripes the value of free chlorine is 1 and here is 0,1, PH is ok but orp is lower than normal.

Any idea what can I do?

image

Wow, sorry for not response, I was not able to watch the forum from my last post. I have my unit now on pool using with his app. I try to test the code with EspHome, thanks so much for your work. By now, seems that my unit power on button is not working very properly after two weeks on pool.

Do you know how much last battery using a regular BTLE pooling? Because is seems that is not very much time as I read on some coments.

Thanks!
Ruben

In my case 1% every 12h so 2% each day.

Hello RubenKona,
I’m interested by all the information you could have from the brands of the device, can you share an archive of all the documents you have ?

Thanks

How do you calculate free chlorine ?

Do you mean manual and box of the product?

Hi, I was looking for something like that all summer ! do you think it will work on this model :

1 Like

You have talked about excel files : "I don’t know how to share the information, as can’t attach the excel with the information. Probably better to send to interested people? "
does they result from reverse engineering or direct from manufacturer ?