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.
@@ -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,13 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Captured logs
13
+ fnb58_*.csv
@@ -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"]
@@ -0,0 +1,3 @@
1
+ """Read voltage/current/power and more from a FNIRSI FNB58 USB power meter over BLE."""
2
+
3
+ __version__ = "0.1.0"