Skip to main content

fakemeter

A Linux BLE peripheral emulator that impersonates Bluetooth multimeters, so a meter's official phone app connects to it and decodes frames you craft. Set a known reading (e.g. 4.200 V DC); if the app displays it, the decode is correct — if it differs, you've found a bug. A hardware-free way to verify BLE multimeter decode logic (built to validate the uni-t-mmu-ble drivers).

   ┌─────────────────────────┐         BLE         ┌──────────────────────────┐
   │  fakemeter (this tool)  │  advertise 0xFFF0   │   phone                  │
   │  ───────────────────    │ ──────────────────▶ │   ┌────────────────────┐ │
   │  craft bytes for a      │                     │   │ vendor app /       │ │
   │  KNOWN reading, e.g.    │   notify (0xFFF4)   │   │ Web-Bluetooth app  │ │
   │  "4.200 V DC"           │ ──── frame bytes ──▶│   │ DECODES + DISPLAYS │ │
   │                         │                     │   └────────────────────┘ │
   │  (you know the input)   │ ◀── writes (0xFFF3) │   (human reads output)   │
   └─────────────────────────┘     logged          └──────────────────────────┘

Eleven profiles across the OWON, Voltcraft, UNI-T, AiCare and BDM families — five live-validated against the real vendor apps (table below).

Requirements

  • Linux with BlueZ (tested on 5.72) and a working BLE adapter — hciconfig should show it UP RUNNING.
  • bluezero, which sits on the distro's python3-gi + python3-dbus (PyGObject). Those don't pip-build cleanly, so install them from apt and let the install see them (below).

Install

sudo apt install python3-gi python3-dbus            # system BLE stack

pipx install --system-site-packages fakemeter       # isolated CLI, recommended
# — or into a venv —
python3 -m venv --system-site-packages .venv && . .venv/bin/activate
pip install fakemeter

Both give you the fakemeter command. (From source: clone, then pip install -e . in the same kind of --system-site-packages venv.)

Run

fakemeter --profile voltcraft        # advertises + opens a REPL

--profile takes any id from the table. Connect the vendor app to the advertised device — it shows the initial 4.200 V DC. Drive it from the REPL:

p           play a preset             v 230.5 V    set value / function / prefix
f hold      toggle a flag             r            re-send the current frame
raw <hex>   inject an arbitrary frame s            voltcraft bit-sweep
series <id> / auth <mode> / walk on|off            ?  help        q  quit

Useful flags:

  • --name NAME — advertised name. Several apps only list an exact model name (see Gotchas) — pass the real one, not the *-FAKE default.
  • --adapter hciN — which adapter (default hci0; also accepts a BD address).
  • --self-check — publish, verify advert + GATT, run encoder round-trips, exit. No phone needed; confirms the host is sane.
  • --no-walk — fixed reading (for precise byte-mapping). --no-unsolicited — disable the raw-HCI no-CCCD delivery path. -v — log every notify / write.

Profiles

"Live-validated" = a reading was read off the real vendor app's screen. "Byte-verified" = the encoder round-trips bit-exact against a port of the app's own decoder, but hasn't been put on-screen yet.

Profile Family / format Vendor app (Android pkg) Status Notes / quirks
voltcraft OWON R10W, 15-byte LE Voltcraft VC800/900 (com.voltcraft.series800, OWON iMeter rebadge) ✅ live-validated Flag order settled (LSB-first). Interactive buttons + value-walk + HOLD all on-screen. ⚠️ device-card shows a red "disconnected" badge even while live data flows — app quirk, does NOT block the reading.
owon-plus OWON 6-byte binary (R2W) OWON iMeter (com.owon.imeter) ✅ live-validated + real-HW corroborated The OWON workhorse. Confirmed against a physical B35T+. The "+" meters (B35T+/B41T+) are this binary format. Use iMeter (writes the CCCD), not BLE4.0.
owon-old OWON 14-byte ASCII (B35) OWON BLE4.0 (com.owon.MultimeterBLE) ⚠️ byte-verified, LEGACY 31/31 round-trip green, but no live oracle and no real hardware exists in the wild — every real meter is binary. Flagged for likely removal (see its module docstring).
bdm YSCoCo XOR-scrambled Bluetooth DMM (com.yscoco.wyboem), AN9002 ✅ live-validated Needed a device-type-byte fix (descrambled byte[2]=0x03, AB_300) to render the right unit. Advert name must be exactly Bluetooth DMM or ZY.
ai-care AiCare self-addressing (FFB0) INTELLIGENT MULTIMETER (aicare.net.cn.iMultimeter) ✅ live-validated Scan gate is manufacturer-data, not name — the emulator advertises AC FF <mac-reversed> so the app lists it (see Gotchas). The readout only updates after you tap the green "Start" button.
uni-t UNI-T AB-CD, 19-byte (polled) UNI-T Smart Measure (com.uni_t.multimeter), as UT60BT ✅ live-validated Handshake-then-stream. Needed a range-index unit fix (range 0 = mV, not V). Advert name must be exactly a supported model (e.g. UT60BT).
ut202bt UNI-T (shares uni_t.encode) Smart Measure 🟡 inherits uni-t fix Same encoder as uni-t, so it inherits the range fix; not separately put on-screen.
ut117c UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified Own encoder; per-model unit/range sweep against its app still owed.
ut171 UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified Own encoder; not live-swept.
ut181a UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified, partial MAIN value block only; secondary block + datalog deferred (need a HW capture).
ut219p UNI-T 16-bit-len encoder Smart Measure 🟡 byte-verified, partial Standard live-data frame only; daoPos→param dispatch + battery-gate handshake deferred.

Verifying a decode

  1. fakemeter --profile <id> --name <exact-model>.
  2. Open the vendor app (or nRF Connect / a Web-Bluetooth client) and connect.
  3. It shows the initial reading. Set values (v 230.5 V), toggle flags (f hold), play presets (p) — if the display matches what you sent, the decode is right. Changing the value live proves it's not a static template; raw <hex> maps any byte/bit to the screen.

Gotchas

The practical ones — full detail in docs/PROGRESS.md.

  • Use the real model name. App scan lists are filtered: Bluetooth DMM accepts only Bluetooth DMM/ZY; UNI-T Smart Measure only exact models (UT60BT, UT219P, …). ai-care instead gates on advertised manufacturer-data, which the profile emits automatically — so any --name works there.
  • OWON apps need an LE-only adapter. They connectGatt(AUTO); on a dual-mode adapter Android picks Classic and bonds, blocking LE GATT. Run sudo btmgmt --index 0 bredr off and clear the phone's cached device record.
  • The OWON BLE4.0 (Java) app never subscribes via CCCD. fakemeter still reaches it by injecting ATT notifications over a raw HCI socket (on by default for stream profiles). That path needs CAP_NET_RAW: sudo setcap cap_net_raw+ep "$(readlink -f "$(which python3)")". Apps that subscribe normally use the standard BlueZ path and need no extra privilege.
  • Restarting drops a phone's bond (stale LTK → "incorrect PIN"). Clear it on both sides: bluetoothctl remove <addr> + toggle the phone's Bluetooth, then re-add from the app's scan screen.

Tests

pip install pytest && pytest -q      # 262 passed

Pure-Python (no BLE hardware needed): each profile round-trips readings through its encoder and a port of the matching decoder (tests/decode_*.py), checking value / unit / decimal point / sign, plus the FFF1 auth math, the interactive reactions, the meter-core engine, and the raw-HCI PDU layout. CI runs them on Python 3.10–3.13.

Docs

  • docs/PROGRESS.md — per-profile validation log, protocol details, and gotchas in full.
  • docs/adding-a-profile.md — how to add a profile + the layering map (base → meter_core → owon_base/uni_t_base → per-model).
  • docs/voltcraft-measurement-protocol.md, docs/owon-voltcraft-handshake.md — the R10W measurement frame and the OWON FFF1/FFF2 connect handshake.

License

MIT — see LICENSE.

Metadata

Release files for fakemeter 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fakemeter 0.1.1
File Size Uploaded Provenance Actions
fakemeter-0.1.1.tar.gz 101.1 kB With provenance Details for fakemeter-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fakemeter 0.1.1
File Interpreter ABI Platform Actions
fakemeter-0.1.1-py3-none-any.whl Python 3 none any Details for fakemeter-0.1.1-py3-none-any.whl

Total release size: 200.5 kB

Release files / fakemeter-0.1.1.tar.gz

Download URL fakemeter-0.1.1.tar.gz
Size 101.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7d5a932ef20e2676d2dbbb77ad9749e73c4342cfee3a54f077a9509bffb7bf80
BLAKE2b-256 checksum
How to use checksums
f1c72a2e4989396b7d2264dba6108c18a97e22f7580b626b9ec2ed4e4730285d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release files / fakemeter-0.1.1-py3-none-any.whl

Download URL fakemeter-0.1.1-py3-none-any.whl
Size 99.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
342c4c30a9e1e4554117aae2bb36b9a42ac6d4b9ede3374669193dd2e6ca67cd
BLAKE2b-256 checksum
How to use checksums
e42c675fee085862e4313ab3337b4b1ccb7364d90ea13aa43715c4258e4f59cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Security for fakemeter 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for fakemeter-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

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