Skip to main content

besen

release CI license

besen is an async Python client for Besen EV chargers over Bluetooth Low Energy. It provides the BLE connection management, login flow, protocol parsing, typed state models, and charger control commands needed by applications such as Home Assistant integrations.

The library has been verified with a Besen BS20 charger. Other Besen chargers that advertise as ACP#... and use the same BLE protocol may also work.

Installation

pip install besen

Python 3.12 or newer is required.

Basic Usage

Applications provide the BLE device lookup function. This keeps discovery policy outside the library, so callers can use bleak, Home Assistant Bluetooth helpers, or another BLE stack integration.

import asyncio
import logging

from bleak import BleakScanner

from besen import BesenClient, BesenData

ADDRESS = "AA:BB:CC:DD:EE:FF"
PIN = "123456"


async def main() -> None:
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("besen")

    device = await BleakScanner.find_device_by_address(ADDRESS, timeout=10.0)
    if device is None:
        raise RuntimeError("Charger was not found")

    def device_provider():
        return device

    client = BesenClient(
        address=ADDRESS,
        pin=PIN,
        ble_device_provider=device_provider,
        logger=logger,
    )

    def handle_update(data: BesenData) -> None:
        print(
            "available=",
            data.available,
            "charging=",
            data.charge.charger_status,
            "power_w=",
            data.charge.power,
            "session_kwh=",
            data.charge.session_energy,
        )

    remove_listener = client.add_listener(handle_update)

    try:
        await client.async_start()
        await client.async_start_charging(amps=8)
        await asyncio.sleep(5)
        await client.async_stop_charging()
    finally:
        remove_listener()
        await client.async_stop()


asyncio.run(main())

Client API

Create one BesenClient per charger:

client = BesenClient(
    address="AA:BB:CC:DD:EE:FF",
    pin="123456",
    ble_device_provider=device_provider,
    logger=logger,
    advertised_name="ACP#Garage",
    sync_clock=True,
)

The BLE device provider is called before connection attempts and reconnects. It must return a connectable bleak.backends.device.BLEDevice or None when no connectable path is available.

Lifecycle methods:

  • await client.async_start() connects, subscribes to notifications, and completes the charger login flow.
  • await client.async_stop() cancels background tasks and disconnects.
  • client.add_listener(callback) registers a synchronous state callback and returns a function that removes it.
  • client.state returns the latest BesenData snapshot.
  • client.is_connected reports whether the underlying BLE connection is open.

Control methods:

  • await client.async_start_charging(amps=None)
  • await client.async_stop_charging()
  • await client.async_set_charge_amps(amps)
  • await client.async_refresh_charge_amps()
  • await client.async_set_lcd_brightness(brightness)
  • await client.async_set_temperature_unit(unit)
  • await client.async_set_language(language)
  • await client.async_set_device_name(name)
  • await client.async_refresh_config()

State Model

State updates are immutable dataclasses. Every listener receives a full BesenData snapshot.

Important fields:

  • BesenData.available: whether the latest BLE state is usable.
  • BesenData.authenticated: whether the PIN login flow completed.
  • BesenData.info: charger metadata such as serial, model, phases, firmware, and board revision.
  • BesenData.config: configuration values such as charge amps, device name, language, temperature unit, LCD brightness, and RSSI.
  • BesenData.charge: live charging state such as voltage, current, energy, temperature, plug state, output state, and charger status.
  • BesenData.last_command: last parsed command response.
  • BesenData.last_error: last connection, protocol, or command error string.

Important telemetry fields:

  • BesenData.charge.power: charger-reported charging power in watts.
  • BesenData.charge.total_energy: lifetime energy counter in kWh.
  • BesenData.charge.session_energy: energy delivered during the current or most recently completed charging session in kWh.
  • BesenData.charge.inner_temp_c and BesenData.charge.outer_temp: temperatures in Celsius, or None when the charger reports an invalid value.
  • BesenData.charge.l1_voltage, l2_voltage, and l3_voltage: phase voltages in volts. L2 and L3 are populated only when the charger sends three-phase data.
  • BesenData.charge.l1_amperage, l2_amperage, and l3_amperage: phase currents in amperes. L2 and L3 are populated only when the charger sends three-phase data.

Exceptions

All library-specific errors inherit from BesenError.

  • CannotConnect: the charger could not be reached or login timed out.
  • NoConnectablePath: no active BLE path is available.
  • InvalidAuth: the charger rejected the configured PIN.
  • ProtocolError: malformed charger data was received.
  • CommandFailed: a charger command could not be sent or was invalid.

Bluetooth Notes

Besen chargers normally allow only one active BLE client connection. Stop other tools or apps that may already be connected to the charger before starting this client.

The client keeps one active BLE connection open, listens for notifications, replies to heartbeats, and schedules reconnects when notifications stop. The caller remains responsible for device discovery, adapter/proxy selection, and deciding when to start or stop the client.

Home Assistant

This package is the reusable Python communication library used by the Besen Home Assistant integration. Home Assistant user-facing setup and troubleshooting notes are kept separately in docs/home-assistant-custom-integration.md.

Safety

EV charging equipment controls real electrical hardware. This library is not a safety controller. Keep charger hardware, breaker sizing, wiring, and local electrical code protections correct independently of any software using this package. Use conservative defaults and manual supervision when automating charging.

Attribution

The Bluetooth protocol implementation is based on the MIT-licensed work in slespersen/evseMQTT, with MQTT-specific runtime behavior replaced by a reusable async Python client.

Additional attribution details are maintained in NOTICE.md.

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

besen-0.4.0.tar.gz (38.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

besen-0.4.0-py3-none-any.whl (20.7 kB view details)

Uploaded Python 3

File details

Details for the file besen-0.4.0.tar.gz.

File metadata

  • Download URL: besen-0.4.0.tar.gz
  • Upload date:
  • Size: 38.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for besen-0.4.0.tar.gz
Algorithm Hash digest
SHA256 ad7230e848ac0d5f2abf34c7b963aa2d8738e888d035e29346c49daf3baa2ab3
MD5 05ad290efc879fadb851275a0ff607c2
BLAKE2b-256 715021acf87e59a0ff0a8638ea866a2f3fc189a0be4a1897e9bb8d79a829450f

See more details on using hashes here.

Provenance

The following attestation bundles were made for besen-0.4.0.tar.gz:

Publisher: release.yml on moryoav/besen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file besen-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: besen-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 20.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for besen-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ef0b85f4705f18559f6041d19e450deedd771af2e7a900e0713c603efa544cc9
MD5 31c048235a84bb891c5f01fb3f3bf314
BLAKE2b-256 2324e1b292bbbfc839e72d9aa990421ffa96751454c6704581d8693b3c36468c

See more details on using hashes here.

Provenance

The following attestation bundles were made for besen-0.4.0-py3-none-any.whl:

Publisher: release.yml on moryoav/besen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.1

2 files

This release

0.4.0 This release

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page