BM2 battery monitoring using ble tracker component

@peterkeen thank you for your work. Just a question. Is it possible to change the script so that if the bm2 is not available, it returns the value “unknown” or “not available”? The background is that if my car is not there, the last value is still there.

1 Like

Did you figure out how to fix this :point_up_2: ? I find it really anoying and I can not even update the ESP configuration wirelessly because of this as I get a timeout on confirmation message.

Sorry no. I gave up. I got the impression the current incarnation does not allow you to change it.

1 Like

@coyote1 I updated the script in the gist to output “unknown” for the voltage and 0.0 for the charge because HA doesn’t seem to appreciate a non-numeric number there. That said I couldn’t get it to actually change to that value, so if you want to play with the script and/or the template sensor and see if you can make it do that, I’d love to hear how to fix it.

Thanks for posting this! I’m trying to set this up and I am running into some errors and hoping for some guidance if you can!

I installed Pyscript with both config boxes checked. I also copied your bt.py file into the Pyscript directory under /custom_components/pyscript and added your template.yaml code to my configuration.yaml with the serial / mac address for my BM2 unit.

As of now, I have the entity showing in HA as unavailable and am getting the following two errors:

Logger: custom_components.pyscript.file.bt.get_charger_data
Source: custom_components/pyscript/eval.py:510
integration: Pyscript Python scripting

Exception in <file.bt.get_charger_data> line 26: for (k,v) in service_info.manufacturer_data.items(): ^ AttributeError: ‘NoneType’ object has no attribute ‘manufacturer_data’

As well as:

Logger: custom_components.pyscript.function
Source: custom_components/pyscript/function.py:453
integration: Pyscript Python scripting

run_coro: got exception Traceback (most recent call last): File “/config/custom_components/pyscript/eval.py”, line 755, in call raise TypeError(f"{self.name}() called with unexpected keyword arguments: {unexpected}") TypeError: get_charger_data() called with unexpected keyword arguments: response_variable

I BELIEVE I’ve followed everything correctly and just hoping you could provide some insight to point me in the right direction on this. I don’t believe there are any other additions needed in the ESPHome config for my Bluetooth ESP32 or anywhere else with your config correct? Thanks again for provide the code for ESPHome!

I updated the gist to gracefully handle this case. There are a few potential reasons for this, but the first thing I would check is if you have the mac address for your bm2 correct and in the correct format. It needs to be all upper-case letters and numbers, in groups of two, each group separated by a colon.

Check the indentation in your YAML file for response_variable. It needs to be at the same level as data (i.e. one level less indented than address). Make sure not to mix tabs and spaces in the same file.

@peterkeen

Hi Peter, I have updated the python script. But it doesn’t work the way I imagined it would :slight_smile: After the update it is now like this: The auto with the BM2 was not there when I reloaded the script, it was then also displayed as “unknown” for voltage. So far correct. The car was then within range and the voltage and charge status were displayed, but if the car is not there now, the last value remains on the display. I thought that if the BM2 is not reachable, “unknown” is displayed, but this is not the case.
Here is another screenshot where you can see it clearly, in the area marked in red, the car was not in range. Can anything be done about this?

i must be missing something because I get this error on compile.

I found a way to reduce the amount of values sent to HA. I just added a filter to the BM2 sensor so that the value needs to change more then 0.05 before a new value is sent like this:

sensor:
  - platform: ble_client   
    type: characteristic
    update_interval: 5min
    ble_client_id: bm2_battery_meter
    name: Voltage
    service_uuid: 'fff0'
    characteristic_uuid: 'fff4'
    icon: 'mdi:battery'
    unit_of_measurement: 'V'
    accuracy_decimals: 2
    state_class: measurement
    device_class: voltage
    force_update: true
    expire_after: 240min
    notify: true
    lambda: |-
      mbedtls_aes_context aes;
      mbedtls_aes_init(&aes);
      unsigned char output[16];
      unsigned char key[16] = { 108, 101, 97, 103, 101, 110, 100, 255, 254, 49, 56, 56, 50, 52, 54, 54, };
      unsigned char iv[16] = {};
      mbedtls_aes_setkey_dec(&aes, key, 128);
      mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, 16, iv, (uint8_t*)&x[0], output);
      mbedtls_aes_free(&aes);
      return ((output[2] | (output[1] << 8)) >> 4) / 100.0f;
    filters:
      - delta: 0.05

image
Hi, I’m getting this error anybody with similar problem or suggestion how to troubleshoot?

fouud this great project, that worked for me:
https://github.com/JeffWDH/bm6-battery-monitor/tree/main

Without ESP is here Rafciq/BM6: Home Assistant integration for Battery Monitor BM6

2 Likes

Very interested in this as my travel trailer keeps having battery issues. I run a RPi 4 with HomeAssistant in it already so in theory this should work without any additional hardware?

Yes, if your RPi 4 can see the BM6 device via Bluetooth. If the Bluetooth range is too small, you need to use a proxy.

If anyone would like a battery percentage readout aswell as the voltage. I’ve added this onto my implementation:

sensor:
  - platform: ble_client
    type: characteristic
    ble_client_id: bm2_client
    name: "Battery Voltage"
    id: battery_voltage
    service_uuid: 'fff0'
    characteristic_uuid: 'fff4'
    unit_of_measurement: 'V'
    accuracy_decimals: 2
    state_class: measurement
    device_class: voltage
    notify: true
    lambda: |-
      if (x.size() != 16) return {};
      
      mbedtls_aes_context aes;
      mbedtls_aes_init(&aes);
      unsigned char output[16];
      unsigned char key[16] = { 108, 101, 97, 103, 101, 110, 100, 255, 254, 49, 56, 56, 50, 52, 54, 54 };
      unsigned char iv[16] = {};
      
      mbedtls_aes_setkey_dec(&aes, key, 128);
      mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, 16, iv, (uint8_t*)&x[0], output);
      mbedtls_aes_free(&aes);
      
      return ((output[2] | (output[1] << 8)) >> 4) / 100.0f;

  - platform: template
    name: "Battery Percentage"
    id: battery_percentage
    unit_of_measurement: '%'
    accuracy_decimals: 1
    state_class: measurement
    device_class: battery
    lambda: |-
      float voltage = id(battery_voltage).state;
      if (isnan(voltage)) return {};
      
      // 12V AGM battery voltage to percentage calculation with linear interpolation
      float voltage_points[] = {11.31, 11.58, 11.75, 11.90, 12.06, 12.20, 12.32, 12.42, 12.50, 12.70};
      float percentage_points[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
      int num_points = 10;
      
      // Clamp voltage to valid range
      if (voltage <= voltage_points[0]) return percentage_points[0];
      if (voltage >= voltage_points[num_points-1]) return percentage_points[num_points-1];
      
      // Find the two points to interpolate between
      for (int i = 0; i < num_points - 1; i++) {
        if (voltage >= voltage_points[i] && voltage <= voltage_points[i+1]) {
          float v1 = voltage_points[i];
          float v2 = voltage_points[i+1];
          float p1 = percentage_points[i];
          float p2 = percentage_points[i+1];
          
          // Linear interpolation
          return p1 + (voltage - v1) * (p2 - p1) / (v2 - v1);
        }
      }
      
      return 0;

It’s important to note the voltage points are based around an AGM battery. If you have a non-AGM battery likely lead acid, you can use these voltage points instead:

      // 12V Lead Acid battery voltage to percentage calculation with linear interpolation
      float voltage_points[] = {11.31, 11.66, 11.81, 11.95, 12.05, 12.15, 12.25, 12.35, 12.45, 12.65};
      float percentage_points[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
      int num_points = 10;
2 Likes

I’ve raised this issue on the BM6 integration, as the integration is quite good.

As an alternative to ESPhome: You can also flash (Option 1) Upload from the web | Theengs OpenMQTTGateway v1.8.1 to an ESP32 with BLE and setup the credentials for WIFI and your MQTT gateway. It auto discovers nearby BM2 after a few minutes, provides battery levels (instantly) and voltage levels (every hour). You will find a new MQTT device in HA for every BM2 that’s in range.

Not sure what version of openmqtt gateway I’m running. I’ve long since lost the password I set up :smiling_face:

But I’ve found it can go days without updating home assistant. My battery’s voltage has been unknown for 3 days now.

Have a look with MQTT Explorer under the SYStoMQTT message which updates every 2 minutes. There you should see the version :wink:

But I’ve found it can go days without updating home assistant. My battery’s voltage has been unknown for 3 days now.

In MQTT Explorer you should also see the messages received by your BM2, which lets you see if there might be a Bluetooth reception problem with your gateway’s position and/or antenna, or if there are regular received message if it might be a HA MQTT issue.

Also make sure that bleconnect is true and intervalcnct set to 60000.

These settings are also visible and settable in the gateway’s HA UI.