fnb58 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fnb58-0.1.0/.github/workflows/ci.yml +34 -0
- fnb58-0.1.0/.github/workflows/publish.yml +44 -0
- fnb58-0.1.0/.gitignore +13 -0
- fnb58-0.1.0/.python-version +1 -0
- fnb58-0.1.0/CLAUDE.md +67 -0
- fnb58-0.1.0/PKG-INFO +96 -0
- fnb58-0.1.0/README.md +85 -0
- fnb58-0.1.0/pyproject.toml +29 -0
- fnb58-0.1.0/src/fnb58/__init__.py +3 -0
- fnb58-0.1.0/src/fnb58/cli.py +344 -0
- fnb58-0.1.0/tests/test_decoder.py +98 -0
- fnb58-0.1.0/uv.lock +553 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Lint, test & build (py${{ matrix.python-version }})
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up uv
|
|
20
|
+
uses: astral-sh/setup-uv@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: uv sync
|
|
26
|
+
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: uvx ruff check .
|
|
29
|
+
|
|
30
|
+
- name: Test
|
|
31
|
+
run: uv run pytest -q
|
|
32
|
+
|
|
33
|
+
- name: Build
|
|
34
|
+
run: uv build
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes the package to PyPI when a GitHub Release is published.
|
|
4
|
+
# Can also be triggered manually from the Actions tab.
|
|
5
|
+
on:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
name: Build distribution
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- name: Set up uv
|
|
17
|
+
uses: astral-sh/setup-uv@v5
|
|
18
|
+
- name: Build sdist and wheel
|
|
19
|
+
run: uv build
|
|
20
|
+
- name: Upload artifacts
|
|
21
|
+
uses: actions/upload-artifact@v4
|
|
22
|
+
with:
|
|
23
|
+
name: dist
|
|
24
|
+
path: dist/
|
|
25
|
+
|
|
26
|
+
publish:
|
|
27
|
+
name: Publish to PyPI
|
|
28
|
+
needs: build
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
# The environment is referenced by the PyPI trusted-publisher config.
|
|
31
|
+
environment:
|
|
32
|
+
name: pypi
|
|
33
|
+
url: https://pypi.org/project/fnb58/
|
|
34
|
+
permissions:
|
|
35
|
+
# Required for Trusted Publishing (OIDC); no API token needed.
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- name: Download built distributions
|
|
39
|
+
uses: actions/download-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
fnb58-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
fnb58-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
A single-command CLI (`fnb58`) that reads measurements from a **FNIRSI FNB58**
|
|
8
|
+
USB power meter over **Bluetooth LE** and logs them to CSV. The entire
|
|
9
|
+
implementation lives in `src/fnb58/cli.py`; everything else is packaging.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv sync # install deps + the package (editable) into .venv
|
|
15
|
+
uv run fnb58 --help # run the CLI from the working tree
|
|
16
|
+
uv build # build wheel + sdist into dist/
|
|
17
|
+
uv tool install . # install `fnb58` as an isolated global tool (~/.local/bin)
|
|
18
|
+
python -m py_compile src/fnb58/cli.py # quick syntax check
|
|
19
|
+
|
|
20
|
+
# Runtime modes (require a real meter with Bluetooth enabled in its menu):
|
|
21
|
+
uv run fnb58 # discover + log all matching meters to one CSV
|
|
22
|
+
uv run fnb58 --scan # list nearby BLE devices and exit
|
|
23
|
+
uv run fnb58 --discover # dump GATT table + raw packets (debugging new firmware)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
There is **no automated test suite**. The BLE path cannot be exercised without
|
|
27
|
+
hardware; verify protocol/parse changes by driving `Decoder`/`CsvSink` directly
|
|
28
|
+
with synthetic bytes (see how segments are built: `aa | type | len | payload |
|
|
29
|
+
checksum`), and run a real meter for end-to-end confirmation.
|
|
30
|
+
|
|
31
|
+
## Architecture (the non-obvious parts)
|
|
32
|
+
|
|
33
|
+
**The BLE protocol is completely different from the device's USB-HID protocol.**
|
|
34
|
+
This is the central gotcha — do not reuse USB knowledge:
|
|
35
|
+
|
|
36
|
+
- USB-HID (the widely-documented baryluk protocol) uses 64-byte fixed frames,
|
|
37
|
+
an `aa 81/82/83` handshake, and ÷100000 scaling. **None of that works over BLE.**
|
|
38
|
+
- BLE uses **short 4-byte commands**: `aa 81 00 f4` (request info) then
|
|
39
|
+
`aa 82 00 a7` (start streaming); stop is `aa 84 00 01`. No keep-alive needed.
|
|
40
|
+
- GATT: notify char `0000ffe4-…`, write char `0000ffe9-…` (constants at top of
|
|
41
|
+
`cli.py`).
|
|
42
|
+
- Data is a **TLV stream**, not fixed frames: `0xAA | type | len | payload[len]
|
|
43
|
+
| checksum`. One BLE notification may contain several concatenated segments,
|
|
44
|
+
and segments may be split across notifications.
|
|
45
|
+
- Segment types and scaling: `0x04` V/I/power (÷10000), `0x05` resistance/temp,
|
|
46
|
+
`0x06` D+/D−, `0x08` energy/capacity, `0x03` device info. Protocol source:
|
|
47
|
+
https://github.com/zhiyb/FNB58_mqtt_forwarder
|
|
48
|
+
|
|
49
|
+
**`Decoder`** owns reassembly: it buffers incoming bytes, resyncs on `0xAA`,
|
|
50
|
+
waits for a complete segment, dispatches by type, keeps the *latest* value of
|
|
51
|
+
each quantity in `self.state`, and fires `on_reading` only when a `0x04`
|
|
52
|
+
measurement arrives (merging in the most recent D+/D−, temp, etc.).
|
|
53
|
+
|
|
54
|
+
**Multi-device design:** `find_devices()` returns *all* name-matching meters;
|
|
55
|
+
`run()` opens one shared `CsvSink` and `asyncio.gather`s one `log_device` task
|
|
56
|
+
per meter. All workers write to the same CSV, distinguished by the `device`
|
|
57
|
+
column (the meter's advertised name). The shared writer needs **no lock**
|
|
58
|
+
because bleak dispatches all notification callbacks on the single asyncio
|
|
59
|
+
event-loop thread. Per-worker `try/except` + `return_exceptions=True` isolate a
|
|
60
|
+
failing meter so it can't abort the others.
|
|
61
|
+
|
|
62
|
+
## Conventions
|
|
63
|
+
|
|
64
|
+
- Measurement/log output goes to **stdout**; all status, errors, and device
|
|
65
|
+
info go to **stderr** (so CSV piping stays clean).
|
|
66
|
+
- The console-script entry point is `fnb58 = "fnb58.cli:main"` (`pyproject.toml`).
|
|
67
|
+
Build backend is hatchling with a `src/` layout.
|
fnb58-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fnb58
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Read voltage, current, power and more from a FNIRSI FNB58 USB power meter over Bluetooth (BLE).
|
|
5
|
+
Project-URL: Homepage, https://github.com/baryluk/fnirsi-usb-power-data-logger
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ble,bleak,bluetooth,fnb58,fnirsi,power-meter,usb
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: bleak>=0.21
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# fnb58
|
|
13
|
+
|
|
14
|
+
Command-line tool to read **voltage, current, power, D+/D−, temperature,
|
|
15
|
+
resistance, energy and capacity** from a [FNIRSI FNB58](https://www.fnirsi.com/)
|
|
16
|
+
USB power meter over **Bluetooth (BLE)**, with live console output and CSV logging.
|
|
17
|
+
|
|
18
|
+
Works on macOS, Linux and Windows (via [bleak](https://github.com/hbldh/bleak)).
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
With [`uv`](https://docs.astral.sh/uv/) (recommended — isolated, on your PATH):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv tool install fnb58 # from PyPI
|
|
26
|
+
uv tool install . # from a local checkout
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or with `pipx`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pipx install fnb58
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or plain `pip`:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install fnb58
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
Enable Bluetooth in the FNB58's on-screen menu, then:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
fnb58 # auto-discover, stream to console + timestamped CSV
|
|
47
|
+
fnb58 --scan # list nearby BLE devices and exit
|
|
48
|
+
fnb58 -o run1.csv # choose the CSV filename
|
|
49
|
+
fnb58 -o run1.csv --quiet # log to CSV only, no console output
|
|
50
|
+
fnb58 --no-file # console only, no CSV
|
|
51
|
+
fnb58 --address <UUID> # connect to a specific device
|
|
52
|
+
fnb58 --discover # dump GATT table + raw packets (debugging)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Console output:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
2026-06-13T12:25:31.004+00:00 5.0123 V 0.4988 A 2.4998 W D+=2.70 D-=2.70 28.3C 0.0021 Wh
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
CSV columns (one row per measurement frame, flushed immediately, UTC timestamps):
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
epoch,utc_time,device,voltage_V,current_A,power_W,dplus_V,dminus_V,temp_C,resistance_ohm,energy_Wh,capacity_Ah
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Multiple meters
|
|
68
|
+
|
|
69
|
+
If several FNB58s are in range, **all** matching meters are logged at once into
|
|
70
|
+
**one** CSV file. The `device` column (the meter's advertised name, e.g.
|
|
71
|
+
`FNB58-051772`) tells you which row came from which meter, and console lines are
|
|
72
|
+
prefixed with `[<device>]`:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
[FNB58-051772] 2026-06-13T12:25:31.004+00:00 5.0123 V ...
|
|
76
|
+
[FNB58-0517A0] 2026-06-13T12:25:31.020+00:00 9.0001 V ...
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
epoch,utc_time,device,voltage_V,...
|
|
81
|
+
1781742331.004,2026-06-13T12:25:31.004+00:00,FNB58-051772,5.01230,...
|
|
82
|
+
1781742331.020,2026-06-13T12:25:31.020+00:00,FNB58-0517A0,9.00010,...
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Use `--name` or `--address` to narrow down to a specific meter. A meter that
|
|
86
|
+
drops out is logged to stderr but does not stop the others.
|
|
87
|
+
|
|
88
|
+
## Notes
|
|
89
|
+
|
|
90
|
+
- On macOS, grant your terminal Bluetooth permission in
|
|
91
|
+
*System Settings → Privacy & Security → Bluetooth*, and note that BLE
|
|
92
|
+
addresses are reported as UUIDs (use `--scan` to find yours).
|
|
93
|
+
- The BLE protocol differs from the device's USB-HID protocol; this tool speaks
|
|
94
|
+
BLE only. Protocol credit:
|
|
95
|
+
[zhiyb/FNB58_mqtt_forwarder](https://github.com/zhiyb/FNB58_mqtt_forwarder)
|
|
96
|
+
and [baryluk/fnirsi-usb-power-data-logger](https://github.com/baryluk/fnirsi-usb-power-data-logger).
|
fnb58-0.1.0/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# fnb58
|
|
2
|
+
|
|
3
|
+
Command-line tool to read **voltage, current, power, D+/D−, temperature,
|
|
4
|
+
resistance, energy and capacity** from a [FNIRSI FNB58](https://www.fnirsi.com/)
|
|
5
|
+
USB power meter over **Bluetooth (BLE)**, with live console output and CSV logging.
|
|
6
|
+
|
|
7
|
+
Works on macOS, Linux and Windows (via [bleak](https://github.com/hbldh/bleak)).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
With [`uv`](https://docs.astral.sh/uv/) (recommended — isolated, on your PATH):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv tool install fnb58 # from PyPI
|
|
15
|
+
uv tool install . # from a local checkout
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or with `pipx`:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pipx install fnb58
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or plain `pip`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install fnb58
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
Enable Bluetooth in the FNB58's on-screen menu, then:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
fnb58 # auto-discover, stream to console + timestamped CSV
|
|
36
|
+
fnb58 --scan # list nearby BLE devices and exit
|
|
37
|
+
fnb58 -o run1.csv # choose the CSV filename
|
|
38
|
+
fnb58 -o run1.csv --quiet # log to CSV only, no console output
|
|
39
|
+
fnb58 --no-file # console only, no CSV
|
|
40
|
+
fnb58 --address <UUID> # connect to a specific device
|
|
41
|
+
fnb58 --discover # dump GATT table + raw packets (debugging)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Console output:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
2026-06-13T12:25:31.004+00:00 5.0123 V 0.4988 A 2.4998 W D+=2.70 D-=2.70 28.3C 0.0021 Wh
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
CSV columns (one row per measurement frame, flushed immediately, UTC timestamps):
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
epoch,utc_time,device,voltage_V,current_A,power_W,dplus_V,dminus_V,temp_C,resistance_ohm,energy_Wh,capacity_Ah
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Multiple meters
|
|
57
|
+
|
|
58
|
+
If several FNB58s are in range, **all** matching meters are logged at once into
|
|
59
|
+
**one** CSV file. The `device` column (the meter's advertised name, e.g.
|
|
60
|
+
`FNB58-051772`) tells you which row came from which meter, and console lines are
|
|
61
|
+
prefixed with `[<device>]`:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
[FNB58-051772] 2026-06-13T12:25:31.004+00:00 5.0123 V ...
|
|
65
|
+
[FNB58-0517A0] 2026-06-13T12:25:31.020+00:00 9.0001 V ...
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
epoch,utc_time,device,voltage_V,...
|
|
70
|
+
1781742331.004,2026-06-13T12:25:31.004+00:00,FNB58-051772,5.01230,...
|
|
71
|
+
1781742331.020,2026-06-13T12:25:31.020+00:00,FNB58-0517A0,9.00010,...
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Use `--name` or `--address` to narrow down to a specific meter. A meter that
|
|
75
|
+
drops out is logged to stderr but does not stop the others.
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- On macOS, grant your terminal Bluetooth permission in
|
|
80
|
+
*System Settings → Privacy & Security → Bluetooth*, and note that BLE
|
|
81
|
+
addresses are reported as UUIDs (use `--scan` to find yours).
|
|
82
|
+
- The BLE protocol differs from the device's USB-HID protocol; this tool speaks
|
|
83
|
+
BLE only. Protocol credit:
|
|
84
|
+
[zhiyb/FNB58_mqtt_forwarder](https://github.com/zhiyb/FNB58_mqtt_forwarder)
|
|
85
|
+
and [baryluk/fnirsi-usb-power-data-logger](https://github.com/baryluk/fnirsi-usb-power-data-logger).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fnb58"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Read voltage, current, power and more from a FNIRSI FNB58 USB power meter over Bluetooth (BLE)."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
keywords = ["fnb58", "fnirsi", "usb", "power-meter", "bluetooth", "ble", "bleak"]
|
|
9
|
+
dependencies = [
|
|
10
|
+
"bleak>=0.21",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
fnb58 = "fnb58.cli:main"
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://github.com/baryluk/fnirsi-usb-power-data-logger"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=8",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["hatchling"]
|
|
26
|
+
build-backend = "hatchling.build"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/fnb58"]
|