custom-components / ble_monitor

BLE monitor for passive BLE sensors
https://community.home-assistant.io/t/passive-ble-monitor-integration/
MIT License
1.93k stars 248 forks source link

Question about HHCCJCY01 #1390

Open danergo opened 1 month ago

danergo commented 1 month ago

Hi, I'm trying to understand this plant sensor's LE Service data. I know it's not sending its battery state, but I'm interested in others (temperature, soil, fertilization, sun).

Can you help me decoding, or point me to some docs?

For example: (service data in LE packet):

71209800a5FFEEDDCCBBAA0d0810011f

Packets have a fixed prefix:

71209800a5FFEEDDCCBBAA

(AA:BB:CC:DD:EE:FF is its MAC)

But I'm having issues understanding the rest bytes:

0d0810011f

Can you please help me with this?

Ernst79 commented 1 month ago

This device is also sending data in Xiaomi format (look for messages with 1695FE) That is what I use to get the data. https://github.com/custom-components/ble_monitor/blob/master/custom_components/ble_monitor/ble_parser/xiaomi.py

However, you might also be able to use the other data format you are showing.

In general, it is either a one byte or two byte data format, that has to be reversed order.

for example 0d08 has to be read as 080D. After converting it from hex to dec you get 2061, which most likely is 20,61°C 1001 is 0110 --> hex to dec --> 272 1f --> hex to dec --> 31 https://www.rapidtables.com/convert/number/hex-to-decimal.html

you have to do some guessing how the message is spit up, but the first two bytes look like temperature to me. In helps to make a long log with multiple messages. If a byte changes a lot, is is most likely the second byte of a two byte value. If it hardly changes, it is most likely a single byte value.

danergo commented 1 month ago

Thanks!

My values were:

Moisture: 31% Light: 105lux Temperature: 23.1C Fertility: 770us/cm

So 1f can be matched to Moisture, but rest are unclear.

So my device is not sending these broadcasts with xiaomi format?

Can I somehow force it to send the xiaomi format?

Service data is fe95, and interpreted as xiaomi (by btmon).

Ernst79 commented 1 month ago

You can force it to send xiaomi format by adding it to the Xiaomi Mi Home app. After adding it, it should start broadcasting in xiaomi format.

danergo commented 1 month ago

Thanks, I gave it a shot, but MiHome doesn't see it at all. It works with "Flower care" app, is it possible this is something different device?

On the packaging it shows: HHCCJCY01HHCC

Ernst79 commented 2 weeks ago

95fe should be xiaomi format, so you can use that info. Can you post the 95fe service data?

myhomeiot commented 1 week ago

@danergo In "Flower care" you can check firmware version of HHCCJCY01HHCC (... in right top corner, Hardware settings), in my case the latest is v3.3.5

Chreece commented 6 days ago

This device returns in passive ble monitor battery as unknown but in the Xiaomi BLE integration reports a percentage.

I think here is parsed: https://github.com/Bluetooth-Devices/xiaomi-ble/blob/main/src/xiaomi_ble/parser.py line: 1619

Ernst79 commented 6 days ago

That is because the battery data can only be received with a connection, unlike the other sensor data, which is send passively. BLE monitor can’t connect to devices.

myhomeiot commented 6 days ago

@Chreece You can do same with BLE Monitor using this example This example read battery data using ESPHome ble_client and send it to BLE Monitor like it's was received as BLE Advertisement.

Chreece commented 4 days ago

@myhomeiot Unfortunately that didn't help, in ESP logs the battery % is parsed but in HA it stays unavailable.

external_components:
  - source: github://myhomeiot/esphome-components

myhomeiot_ble_host:

myhomeiot_ble_client:
  - mac_address: ${flower_care_mac}
    service_uuid: '1204'
    characteristic_uuid: '1A02'
    update_interval: 24h
    on_value:
      then:
        homeassistant.event:
          event: esphome.on_ble_advertise
          data:
            packet: !lambda |-
              if (x.size() < 2)
              {
                ESP_LOGE("myhomeiot_ble_client", "payload has wrong size (%d)", x.size());
                return "";
              };
              ESP_LOGI("myhomeiot_ble_client", "Battery (%d%%), firmware (%s)", x[0], std::string(x.begin(), x.end()).substr(2).c_str());
              static char buffer[70 + 1];
              const uint8_t *remote_bda = xthis.remote_bda();
              snprintf(buffer, sizeof(buffer), "043E2002010000%02X%02X%02X%02X%02X%02X14020106030295FE0C1695FE41209800000A1001%02X00",
                remote_bda[5], remote_bda[4], remote_bda[3], remote_bda[2], remote_bda[1], remote_bda[0], x[0]);
              return reinterpret_cast<const char *>(buffer);
myhomeiot commented 4 days ago

@Chreece I use this for years, should work. Check that you have Flower Care with ${flower_care_mac} defined in BLE Monitor and you already receives data like illuminance, conductivity and temperature.

Edit: Check that you have battery sensor enabled for this Flower Care in Passive BLE Monitor settings. Edit2: In my case even it's report 0% battery it's keep working for 6 month.

image

Chreece commented 4 days ago

image image

myhomeiot commented 4 days ago

@Chreece: Looks good. The same ESP forwards data about this Flower Care (illuminance, conductivity and temperature) for Passive BLE Monitor using BLE Gateway?

Chreece commented 4 days ago

No, the data that you see in ha are parsed on the host from its own bt chi. They existed before installing your library on an esp32 that I have near the plant sensor

myhomeiot commented 4 days ago

@Chreece OK, in this case you need to check if on HA side you have automation which receives esphome.on_ble_advertise event from ESP and pass data to BLE Monitor, more information you can find here, but it's should look like:

automation:
  - alias: ESPHome BLE Advertise
    mode: queued
    trigger:
      - platform: event
        event_type: esphome.on_ble_advertise
    action:
      - service: ble_monitor.parse_data
        data:
          packet: "{{ trigger.event.data.packet }}"
          gateway_id: "{{ trigger.event.data.gateway_id if trigger.event.data.gateway_id is defined else 'unknown' }}"
Chreece commented 4 days ago

Wonderful! Worked as intended. Thank you