Async Python client for Besen BS20 EV chargers over BLE
Project description
Besen BS20 Home Assistant Integration
Native Home Assistant integration for Besen BS20-family EV chargers over Bluetooth Low Energy.
This integration talks directly to the charger through Home Assistant's Bluetooth stack. It does not need MQTT, Docker, a sidecar process, or a Home Assistant add-on. It is designed to work through existing ESPHome Bluetooth proxies as long as those proxies support active GATT connections.
Python Library
The reusable BLE client and protocol parser are published as the besen-bs20 Python package. Home Assistant installs that package as this integration's communication dependency.
pip install besen-bs20
Disclaimer
This is an unofficial community project. It is not affiliated with, associated with, authorized by, endorsed by, or in any way officially connected with BESEN, Besen Group, or any related company. Product names, trademarks, and registered trademarks belong to their respective owners and are used only to identify compatible devices.
Use this integration at your own risk. EV charging equipment controls real electrical hardware; incorrect configuration, device firmware behavior, Bluetooth issues, automation mistakes, or software defects may cause charging interruption, equipment damage, vehicle damage, electrical hazards, or other loss. The maintainer and contributors are not responsible or liable for damage, injury, data loss, costs, or other consequences arising from use of this integration.
Requirements
- Home Assistant with the Bluetooth integration enabled.
- A Besen BS20 or compatible charger advertising as
ACP#.... - The charger BLE address and 6-digit PIN.
- For ESPHome Bluetooth proxies:
bluetooth_proxy:with active connections enabled.- A connectable proxy close enough to the charger.
- Enough free active connection slots.
ESPHome Bluetooth proxies default to active connections enabled in current ESPHome releases. Each continuously connected charger uses one active GATT connection slot on the selected proxy.
Migrating From evseMQTT
If you previously used evseMQTT with the same charger, fully stop or terminate the old evseMQTT bridge before installing this integration. This includes any Docker container, Home Assistant add-on, Raspberry Pi service, systemd unit, or other process that may still connect to the charger.
The charger can only keep one active BLE client connection. In some cases, after evseMQTT has been stopped, you may also need to turn the EV charger off and back on so it forgets the old component pairing before Home Assistant can discover or connect to it through this integration.
Installation
HACS custom repository
- Open HACS.
- Add this repository as a custom integration repository.
- Install Besen BS20.
- Restart Home Assistant.
- Go to Settings > Devices & services.
- Add Besen BS20 or accept the discovered
ACP#...device.
HACS uses GitHub releases when they are available. Install the latest release tag unless you explicitly want to test the default branch.
Manual installation
- Copy
custom_components/besen_bs20into your Home Assistantcustom_componentsdirectory. - Restart Home Assistant.
- Add Besen BS20 from Settings > Devices & services.
Configuration
- BLE address: The charger Bluetooth address. Discovery fills this automatically when Home Assistant sees an
ACP#...advertisement. - PIN: The charger's 6-digit Bluetooth PIN. Many units default to
123456. - Sync charger clock: Keeps the charger's internal clock aligned during heartbeat responses.
The PIN can be updated later through the integration reconfigure flow.
Entities
The exact entity set depends on charger model, board revision, reported phase count, and supported firmware responses.
Enabled by default:
- Charge switch.
- Charge amps number.
- Current Energy sensor.
- Total/session energy sensors.
- L1 Voltage and L1 Amperage.
- Charger status, plug state, output state, current state, and error state.
- Charger temperature.
- Device name text entity.
- Temperature unit and language selectors.
Diagnostic or less commonly used entities may be disabled by default:
- RSSI.
- L2/L3 Voltage and Amperage on three-phase chargers.
- System time.
- LCD brightness.
- Integration/protocol version details.
Controls And Actions
The integration does not register custom Home Assistant service actions. Use the standard entity actions instead:
switch.turn_on/switch.turn_offon the charging switch.number.set_valueon charge amps.select.select_optionon language or temperature unit.text.set_valueon the charger name.
Command failures are raised back to Home Assistant and the device is marked unavailable if the BLE write path fails.
Updating Data
The charger sends status over BLE notifications after login. Home Assistant keeps one active BLE connection open, listens for notifications, and responds to charger heartbeats. If notifications stop for about 45 seconds, the integration reconnects without repeatedly filling the logs.
This is a local-push integration. There is no cloud dependency.
Common Automations
Start charging when solar surplus is available:
alias: Start EV charging on solar surplus
triggers:
- trigger: numeric_state
entity_id: sensor.solar_surplus_power
above: 2500
for: "00:05:00"
conditions:
- condition: state
entity_id: sensor.besen_bs20_plug_state
state: Connected Locked
actions:
- action: number.set_value
target:
entity_id: number.besen_bs20_charge_amps
data:
value: 8
- action: switch.turn_on
target:
entity_id: switch.besen_bs20_charging
Stop charging before peak tariff:
alias: Stop EV charging before peak tariff
triggers:
- trigger: time
at: "17:00:00"
actions:
- action: switch.turn_off
target:
entity_id: switch.besen_bs20_charging
Supported Devices
Known target:
Likely compatible:
- Besen wallboxes using the same
ACP#BLE protocol and one of the known UUID pairs.
Unsupported or not implemented:
- Wi-Fi provisioning.
- Password reset.
- Device reset.
- Charging history download.
- Firmware updates through Home Assistant.
- Safety-certified load balancing.
Troubleshooting
The charger is not discovered
- Confirm it appears in Settings > Bluetooth > Advertisement monitor as
ACP#.... - Move an ESPHome Bluetooth proxy closer to the charger.
- Make sure the proxy is added to Home Assistant through the ESPHome integration.
- Run an active scan or temporarily place a local Bluetooth adapter near the charger.
Setup says no connectable Bluetooth path is available
The charger may be visible only through a passive/non-connectable adapter. Use an ESPHome Bluetooth proxy with active connections enabled, or a local Bluetooth adapter supported by Home Assistant.
Stop any existing evseMQTT bridge, Docker container, add-on, Raspberry Pi service, or other app that is already connected to the charger before setting up this integration. The charger can expose live data through the old MQTT bridge while Home Assistant has no free connectable BLE path for the native integration.
The charger becomes unavailable
- Check Settings > Bluetooth > Connection monitor.
- Verify the proxy has free active connection slots.
- Stop any old evseMQTT/MQTT bridge or companion process that may still hold the charger's BLE connection.
- Prefer Ethernet ESP32 Bluetooth proxies when possible.
- Avoid placing the proxy next to strong Wi-Fi or USB 3.0 interference sources.
The PIN is rejected
Use the integration reauthentication prompt or reconfigure flow to enter the correct 6-digit PIN. The integration redacts PINs from diagnostics.
Diagnostics
From the device page, download diagnostics before opening an issue. Diagnostics include the board revision, charger metadata, latest parsed state, availability, and last error. The PIN is redacted.
Removal
- Go to Settings > Devices & services.
- Open Besen BS20.
- Select the integration menu and choose Delete.
- Restart Home Assistant if you also manually copied the integration files and want to remove them from
custom_components.
Safety Notes
This integration exposes charger controls but is not a safety controller. Do not rely on it as the only protection for electrical limits, overheating, grid constraints, or vehicle safety. Keep charger hardware, breaker sizing, wiring, and local electrical code protections correct independently of Home Assistant. Use manual supervision and conservative automation defaults when controlling charging equipment.
Attribution
The Bluetooth protocol implementation is based on the MIT-licensed work in slespersen/evseMQTT, with the MQTT/runtime portions replaced by native Home Assistant integration code.
Additional attribution details are maintained in NOTICE.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file besen_bs20-0.2.2.tar.gz.
File metadata
- Download URL: besen_bs20-0.2.2.tar.gz
- Upload date:
- Size: 35.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef280bae06461b02a81bca939c62a2e4a4a520eef9bbbd7e4f5841ffd704ae6b
|
|
| MD5 |
4fa88a448ba845f3e19ac57fc1f4d1a3
|
|
| BLAKE2b-256 |
19276533e189d9f77825c2efe67ae293dd32eb1004e5500bb4b1aeaa64008254
|
Provenance
The following attestation bundles were made for besen_bs20-0.2.2.tar.gz:
Publisher:
release.yml on moryoav/ha_besen-bs20
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
besen_bs20-0.2.2.tar.gz -
Subject digest:
ef280bae06461b02a81bca939c62a2e4a4a520eef9bbbd7e4f5841ffd704ae6b - Sigstore transparency entry: 2046018282
- Sigstore integration time:
-
Permalink:
moryoav/ha_besen-bs20@a373be9b807a866a9c44d0a0e6788199bfe7d983 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/moryoav
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a373be9b807a866a9c44d0a0e6788199bfe7d983 -
Trigger Event:
push
-
Statement type:
File details
Details for the file besen_bs20-0.2.2-py3-none-any.whl.
File metadata
- Download URL: besen_bs20-0.2.2-py3-none-any.whl
- Upload date:
- Size: 20.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a01e9a7ba1fbf1c2f1487eeb9ff4b185d00e02589a1419ea2a3b88f39baa3fb5
|
|
| MD5 |
1ed699d24d66d4074fc01ad0952fe34e
|
|
| BLAKE2b-256 |
175f962fa98fb5982d0e3f5775b00550de926ee92db00ab4f2456bb12a0bd2bb
|
Provenance
The following attestation bundles were made for besen_bs20-0.2.2-py3-none-any.whl:
Publisher:
release.yml on moryoav/ha_besen-bs20
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
besen_bs20-0.2.2-py3-none-any.whl -
Subject digest:
a01e9a7ba1fbf1c2f1487eeb9ff4b185d00e02589a1419ea2a3b88f39baa3fb5 - Sigstore transparency entry: 2046018487
- Sigstore integration time:
-
Permalink:
moryoav/ha_besen-bs20@a373be9b807a866a9c44d0a0e6788199bfe7d983 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/moryoav
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a373be9b807a866a9c44d0a0e6788199bfe7d983 -
Trigger Event:
push
-
Statement type: