Open-source project
fl4p/batmon-ha avatar
fl4p/batmon-ha

BatMON: reading your battery management system over Bluetooth in Home Assistant

Add-on for Home Assistant to connect JK, JBD, Daly, ANT, SOK, Supervolt and other BMS via Bluetooth

526 stars125 forksPythonMIT

At a glance

What is it?
BatMON is a Home Assistant add-on that talks to dozens of different battery management systems over Bluetooth Low Energy and republishes their readings over MQTT, so no USB, serial, RS485 or ESP32 hardware is needed. It also exposes the BMS charging and discharging switches for control, and can run standalone on Linux, macOS and Windows.
Who is it for?
Adopt BatMON if you have a Bluetooth-capable battery management system and want its cell voltages, current and state of charge in Home Assistant without running RS485 cabling to a converter. Do not adopt it if you need the vendor's own app features beyond basic monitoring, because this is a read and control surface, not a full management tool.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the add-on does

BatMON is a Home Assistant add-on for monitoring and controlling various battery management systems over Bluetooth, and the description of its job is precise.

It reads the BMS and sends sensor data through MQTT to Home Assistant. Using Bluetooth on the Home Assistant host system, it does not need any additional hardware, so no USB, serial, RS485 or ESP32. It can also run without Home Assistant on Linux, macOS and Windows.

The no additional hardware claim is the part that changes what is possible for most people. A typical DIY battery build uses a BMS with RS485 or a UART serial port, which means running a cable to a converter plugged into a server or a USB adapter on the machine running Home Assistant. If the BMS speaks Bluetooth, none of that is needed.

The reason the author built it is stated plainly and it is a good indicator of the project's character: he created this to compare BMS readings for a detailed evaluation of BMS reliability and accuracy. Comparing readings across several systems on the same bank is exactly the kind of measurement problem that needs an integration layer, and the tool has grown out of that.

The feature list covers what it captures and what it can do. State of charge, current, power, individual cell voltages and temperatures, with multiple devices monitored at the same time. Energy consumption meters using trapezoidal power integrators, which is how you turn a current reading into an accumulating energy figure that the Home Assistant Energy dashboard can consume. Integration with the Home Assistant Energy dashboard and the Utility Meter sensor helper. Home Assistant MQTT Discovery, so the entities appear without manual configuration.

And then control: BMS charging and discharging switches, and the ability to set the state of charge gauge from Home Assistant through a set SOC number entity, for instance to reset a drifted gauge to zero percent after a low-voltage cut-off. That is listed as supported for Daly and JK with firmware 11 and above.

There is also a custom Bluetooth stack for increased reliability, and the readme calls out low latency for responsive automation such as fast load shedding.

The device list, and the two connector families

The device support is extensive, and the structure of the list explains how the project is put together.

BatMON has its own connectors for some popular BMS, and it also wraps aiobmsble, which covers many other BMS for read-only access. The distinction between the two is the first thing to understand.

The batmon connectors are the ones with full functionality. The list runs through JK BMS and jikong with the JK02 protocol, both over Bluetooth and over RS485 for the serial variant, and the JK-PB inverter BMS. Daly appears in three connector flavours, daly, daly2 and daly_ble over Bluetooth, plus daly_uart over RS485. JBD, Xiaoxiang and LLT appear over Bluetooth and over TTL UART or RS485. Seplos V2 for 48 volt rack packs, Renogy smart lithium batteries over Bluetooth through the BT-2 dongle, Felicity Solar 48 volt LiFePo4 packs, ANT, Supervolt, SOK and ABC-BMS, Basen, LiTime, and legacy Offgridtec OGT-12200 and Topband batteries over an unsolicited FFE4 ASCII-hex protocol.

Then there are the smaller devices with narrower data. The BM6 Bluetooth car battery monitor gives voltage, temperature, state of charge and the charging or low-voltage state but no current. The Quicklynks BM2 and rebrands such as the Ancel BM200 give voltage and state of charge only. The Victron SmartShunt is supported, with a firmware caveat.

The aiobmsble connectors are the read-only long tail. CBT Power and Creabest, Seplos V3 and V2, TianPwr, the ATORCH CW20 DC Meter, TDT, with a separate tdt_nocrc variant for units with broken CRC firmware such as Humsienk golf-cart batteries. E&J Technology, Chunguang Song ABC-BMS, D-powercore, ECO-WORTHY BW02, Ective, Offgridtec LiFePo4 Smart Pro, Redodo, RoyPow, Braun Power, Neey, Pro BMS Smart Shunt, and Renogy in two variants, plus everything else aiobmsble supports.

The switching mechanism is simple and worth knowing. You switch from the batmon to the aiobmsble connectors by appending _ble to the type field, so instead of type: daly you write type: daly_ble. The readme gives the reason: it can help if you experience connection issues, because some of the aiobmsble connectors are more up to date.

So the project gives you a fallback path when its own driver for your model misbehaves. That is a practical design choice for a library of device drivers maintained against hardware with many firmware variants.

Configuration, and the details that cause trouble

The configuration is a list of devices, and the readme walks through the fields with the ones that cause trouble called out.

The example entry is short:

yaml
- address: CC:44:8C:F7:AD:BB
  type: jk
  alias: battery1            # MQTT topic prefix (regex [\w_.-/])
  pin: "12345"               # pairing PIN, victron and newer JBD firmware (optional)
  adapter: "hci0"            # switch the bluetooth hw adapter (optional)
  debug: true                # verbose log for this device only (optional)
  current_calibration: 1.0   # current [I] correction factor (optional)
  display_name: House battery     # device name in HA; topics stay (optional)
  temperature_names: Cell 1,Cell 2 # probe names in HA, in order (optional)

The address is the MAC address of the Bluetooth device, and if you do not know it the answer is to start the add-on and read the add-on log, which lists the visible Bluetooth devices. You can also enter the device name as displayed in that discovery list.

The pin field applies to the Victron and newer JBD firmware, which require a pairing PIN. The adapter field switches the Bluetooth hardware adapter, which matters on a host with more than one radio. The current_calibration field is a correction factor for the current reading, and it exists because current sensors drift.

Two of the fields are about presentation rather than function, and both have a subtlety. The display_name changes the device name shown in Home Assistant while the topics stay the same, so renaming a device does not break existing dashboards. The temperature_names field maps probe names in Home Assistant in order, so you can label Cell 1 through Cell 16 rather than showing unnamed temperature sensors.

The type field accepts a long list of values, and the readme enumerates them: jk, jk_24s, jk_32s, jk_uart, jk_pb_uart, jbd, jbd_uart, ant, daly, daly2, daly_ble, daly_uart, pace_uart, seplos_uart, braunpwr_uart, renogy_uart, felicity_uart, supervolt, sok, sok_legacy, basen, basen_uart, litime, offgridtec, bm6, bm2 and victron, plus any tag listed under the supported devices section.

The existence of so many type values, including separate entries for the 24S and 32S JK variants and legacy and current SOK drivers, reflects a real constraint of this hardware: the BMS families use more than one protocol, and the same family name covers units that speak different dialects.

Installation, and the standing against the host

Installation is three steps and one dependency you have to set up yourself.

You add the add-on repository to your Home Assistant add-on store, pointing at the home-assistant-addons repository. Then you install the BatMON add-on. Then you install, configure and start the Mosquitto MQTT broker, with the reminder to configure the MQTT integration in Home Assistant as well.

That third step is the one that catches people out, because the add-on publishes everything over MQTT and MQTT Discovery. Without a broker and the integration configured, the add-on runs and the data goes nowhere.

The standalone option is the other half of the deployment story. It can run without Home Assistant on Linux, macOS and Windows and write directly to InfluxDB, so the same collectors can feed a time series database without a home automation system in the loop at all.

The add-on also brings features that belong to the Home Assistant side rather than the protocol side. Battery Groups handle parallel batteries and are documented separately. Charge Algorithms are documented separately too, which tells you they are a layer above the raw BMS data. And the state of charge gauge reset is the control feature, listed as working for Daly and JK with firmware 11 and above.

The author tested the add-on on a Raspberry Pi 4 and 5 using Home Assistant Operating System, which is a useful baseline because the Bluetooth radio in a Pi is one of the more awkward cases, with the hci adapter selection existing precisely for hosts where the adapter index is not what you would expect.

The repository layout shows the structure of a Home Assistant add-on. There is a config.yaml and a build.yaml for the add-on metadata and build, an addon_main.sh and entrypoint.sh for startup, a Dockerfile, an apparmor.txt for the sandbox profile, a main.py, a bmslib/ directory for the device libraries, charts/ and docs/ and doc/ for the documentation, translations/ for the localised user interface, plans/ for the roadmap, tools/ and a test_and_deploy.sh script. The presence of both doc/ and docs/ directories is a small untidiness that suggests the documentation was reorganised without moving everything.

The build, and the dependency pins that carry comments

The Dockerfile is where the hard-won lessons about Bluetooth and Python versions are recorded, and the comments are the interesting part.

The base is the Home Assistant base image, and Python is installed with a fallback chain across 3.13, 3.12 and then whatever is available. BlueZ is installed for the Bluetooth stack, and py-pip and git are added separately.

Then there is a version check that fails early and legibly. The comment explains why: it should fail early and legibly instead of letting pip report a confusing message about not finding a version that satisfies the requirement for bleak 2.0.0. The check asserts that Python is 3.10 or newer, since bleak 2.0.0 is a universal wheel that needs Python 3.10 or above, and a versions: none message can only mean a too-old interpreter or an unreachable index.

The next check distinguishes those two cases. It attempts a plain TCP connection to port 443 on pypi.org and files.pythonhosted.org, without TLS, so it checks exactly what pip's error message hides, which is DNS resolution and routing. The comment notes that pip needs both hosts, and that the check is skipped when a proxy is configured, because a direct connect legitimately fails while pip still works through the proxy.

That is careful work. A build failure that names the actual cause is worth a lot of lines of shell.

The requirements file has the same character, with comments attached to the pins. bleak is pinned at 2.0.0 with a reference to an issue about it, because aiobmsble is installed with --no-deps so it cannot drag bleak to 3.x, and batmon itself imports bleak_retry_connector. paho-mqtt is pinned at 2.1.0, with backoff, crcmod and pyserial alongside. The InfluxDB and aiobmsble entries are commented out with an explanation that the influxdb package is not available from some platforms, so it is installed only if needed.

The most recent release is v1.95-daly356, described as a test build for issue 356, which tells you the project versions releases per device family and that the numbering scheme is a running count rather than a semver line.

Editorial conclusion

Adopt BatMON if you have a Bluetooth-capable battery management system and want its cell voltages, current and state of charge in Home Assistant without running RS485 cabling to a converter. Do not adopt it if you need the vendor's own app features beyond basic monitoring, because this is a read and control surface, not a full management tool. Before you commit, check the supported type list for your exact model and note the firmware caveats, since the Victron SmartShunt in particular needs the latest firmware with GATT enabled.

Frequently asked questions

What does the BatMON Home Assistant add-on do?

It monitors and controls battery management systems over Bluetooth Low Energy, reading state of charge, current, power, individual cell voltages and temperatures, and publishing the sensor data to Home Assistant over MQTT with MQTT Discovery. It needs no USB, serial, RS485 or ESP32 hardware because it uses the Bluetooth radio of the Home Assistant host.

Which battery management systems does BatMON support?

BatMON has its own connectors for JK and jikong, the JK-PB inverter, Daly, JBD and Xiaoxiang and LLT, Seplos V2, Renogy, Felicity Solar, ANT, Supervolt, SOK and ABC-BMS, Basen, LiTime, legacy Offgridtec, the BM6 and BM2 car monitors and the Victron SmartShunt. It also wraps aiobmsble, which adds read-only support for many more brands, and you can switch to those connectors by appending _ble to the type field.

How do I install BatMON in Home Assistant?

Add the home-assistant-addons repository to your Home Assistant add-on store, install the BatMON add-on, then install, configure and start the Mosquitto MQTT broker and configure the MQTT integration in Home Assistant. The add-on can also run standalone on Linux, macOS and Windows and write directly to InfluxDB.

What configuration options does BatMON support for a device?

Each entry takes an address, which is the Bluetooth MAC address or the device name from the discovery list in the add-on log, a type, an alias used as the MQTT topic prefix, and optional fields including a pairing pin for the Victron and newer JBD firmware, a Bluetooth hardware adapter selection, per-device debug logging, a current calibration factor, a display name that does not change the topics, and temperature probe names in order.

Why does the BatMON Docker build check for Python version and network reachability?

The readme explains it is to fail early and legibly rather than letting pip report a confusing message about not finding a version that satisfies the requirement for bleak 2.0.0. The Python check catches a too-old interpreter, and the plain TCP connection to the package index hosts distinguishes an unreachable index from an old interpreter, with the network check skipped when a proxy is configured.

Official sources

  1. fl4p/batmon-ha on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fl4p-batmon-ha.svg)](https://hysenlabs.com/projects/fl4p-batmon-ha)

Was this useful?