Skip to main content

fujilib

Async-first Python driver for Fuji Electric ZP-series NDIR gas analyzers (ZPA, and the ZPB / ZPG / ZPAJ / ZPG3E models that share its MODBUS map), built on anyserial and anymodbus.

fujilib is a member of the *lib instrument-driver family (alicatlib, sartoriuslib, watlowlib, servomexlib, …). It shares their entry point, frozen models, error hierarchy, streaming / sinks / sync / CLI conventions and the unified device-library API; its internals are shaped to this analyzer.

Status

Alpha. 0.1.0 is the first release. The read-only API works against the development analyzer: open_device(), identification, polls with validity, metadata, settings, logs, discovery, recording at a fixed rate to memory, CSV or Parquet, a blocking facade, and the fuji-read, fuji-discover, fuji-configure, fuji-decode, fuji-stream, fuji-capture and fuji-diag commands. A reviewed subset of settings writes, settings documents, return to measurement, and manual zeros and spans watched at the panel or driven from the host (fuji-calibrate) work on the development analyzer too; auto calibration and auto zero have run only on the simulated analyzer, since the development analyzer has no calibration valves. The documentation is at https://fujilib.graysonbellamy.dev/. See docs/design.md for the architecture and the phased plan, and CHANGELOG.md for what has landed.

import anyio

from fujilib import open_device


async def main() -> None:
    async with await open_device(
        "COM8", channel_map={"CH1": "co2", "CH2": "co", "CH3": "o2"}
    ) as anz:
        frame = await anz.poll()  # every channel and the analyzer status, 2 transactions
        o2 = frame.channel("CH3")
        print(o2.value, o2.unit, o2.state)  # 20.2 vol% ok


anyio.run(main)

Record to a file from the command line (Parquet needs fujilib[parquet]):

fuji-capture COM8 --gas CH1=co2 --gas CH2=co --gas CH3=o2 --out run.parquet

The first release, 0.1.0, covers:

  • monitoring, metadata and acquisition. poll() returns every channel with its hold, calibration and error state in two Modbus transactions, and the library streams and records to memory, CSV or Parquet;
  • a reviewed subset of settings writes and the documented operation commands (auto calibration, auto zero, blowback, return to measurement), behind confirm=True, pre-I/O validation and read-back verification;
  • a record of every manual zero and span made at the front panel while it is connected, since the analyzer keeps no calibration log on older firmware;
  • a manual zero or span driven from the host (fuji-calibrate): fujilib presses the calibration keys while the operator switches the gas valves, waits for the reading to settle on the gas named, and records the run.

Design points

  • Validity and provenance travel with every value. Hold, calibration and error state, and where each channel's gas label came from, are carried into every reading and row. Unknown validity stays unknown.
  • Gas labels that feed a calculation are asserted by the caller. The analyzer's type code is treated as a hint, not an authority.
  • Writes are hard to get wrong. Everything fujilib may ever write is a frozen envelope of documented user settings and operation commands. Factory parameters are out of scope, and the only front-panel keys fujilib presses are the six of a manual zero or span, never the keys into the menus.
  • No hardware needed to develop or test. A simulated analyzer runs the full stack in CI.

Oxygen measurements

Over Modbus the analyzer reports O2 with the display's resolution: 0.01 vol% (100 ppm) per step on the development unit. Modbus O2 is not validated for oxygen-consumption calorimetry. See design §2.11.

Installation

Requires Python 3.13+.

pip install fujilib
pip install "fujilib[parquet]"  # with the Parquet sink

From source:

git clone https://github.com/GraysonBellamy/fujilib
cd fujilib
uv sync --all-extras --dev

Development

uv run pre-commit install
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run pyright
uv run pytest

See CONTRIBUTING.md.

License

MIT. See LICENSE.

Release files for fujilib 0.1.0

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

Source distribution (sdist)

Source distribution for fujilib 0.1.0
File Size Uploaded
fujilib-0.1.0.tar.gz 552.7 kB Details

Built distribution (wheel)

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

Total release size: 842.6 kB

Release files / fujilib-0.1.0.tar.gz

Download URL fujilib-0.1.0.tar.gz
Size 552.7 kB
Tags Source
SHA-256 checksum
How to use checksums
055cc416cfb0656c64565240ac559d73af69019c018586a75a5547a6892cd64b
BLAKE2b-256 checksum
How to use checksums
b67e58d680cdfad8c776bdfba5cc57b330b317406513d3c2fcc3a111599a54f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 30, 2026.

Transparency log

Release files / fujilib-0.1.0-py3-none-any.whl

Download URL fujilib-0.1.0-py3-none-any.whl
Size 289.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0694257daf75474363a7bfb56e9409bc5e9e87a31a55880d87e1300696b5ceed
BLAKE2b-256 checksum
How to use checksums
3f7bc7d493e93b7351135dd017b3e44d79b5aaf7193e5f1be8d7aa24f819af2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release 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