Async barcode scanner fleet manager for Vention industrial automation. Supports Keyence TCP, USB evdev, and mock scanners.
Project description
vention-barcode-scanner
Async barcode scanner fleet manager for Vention industrial automation. Unified Scanner ABC over Keyence TCP, USB HID (evdev), and mock backends.
Scanner Types
| Type | Protocol | Platform | Use Case |
|---|---|---|---|
KeyenceScanner |
Async TCP (port 9004) | Any | Keyence SR-series (SR-1000, SR-2000, SR-5000) |
EvdevScanner |
Linux USB (evdev) | Linux | USB keyboard-emulating barcode readers |
MockScanner |
In-memory | Any | Simulation and testing |
Usage
This is an async library: every scanner call uses await, so it must run
inside an async def you start with asyncio.run(...). (await at the top
level of a script raises SyntaxError: 'await' outside function.)
Quickstart — no hardware (mock mode)
Runs as-is, no scanner connected. mode="mock" swaps in in-memory scanners;
mock_label_probability=1.0 makes every scan return a label so the demo is
deterministic.
import asyncio
from barcode_scanner.models import ScannerConfig, ScannerFleetConfig
from barcode_scanner.service import ScannerService
config = ScannerFleetConfig(
scanners=[
ScannerConfig(id="s1", host="192.168.7.100", location_id="station-a"),
],
mock_label_probability=1.0, # always "read" a label (drop to taste, e.g. 0.3)
mock_labels=["GT-205"],
)
service = ScannerService.from_config(config, mode="mock")
async def main():
await service.connect_all()
result = await service.scan("station-a") # one scan window open→dwell→close
await service.disconnect_all()
print(result.status) # ScanStatus.OK | NO_READ | TIMEOUT | ERROR
print(result.label_detected) # True / False
print(result.label_string) # "GT-205" or None
asyncio.run(main()) # runs everything in main()
Connecting real hardware
Same code — swap mode="mock" for mode="real" and pick the backend. host is
each scanner's IP; location_id is the name you scan by.
service = ScannerService.from_config(config, mode="real", scanner_type="keyence")
# ^ "keyence" (TCP) | "evdev" (USB, Linux)
Label Validation
Optional callback to reject invalid labels at the scanner layer:
def validate_label(label: str) -> bool:
return label.startswith("GT-") and len(label) <= 20
service = ScannerService.from_config(config, label_validator=validate_label)
# Labels that fail validation are downgraded to NO_READ
Metrics
metrics = service.get_metrics()["s1"]
metrics.total_scans # 1234
metrics.success_rate # 0.89
metrics.avg_scan_ms # 45.2
Tuning and Diagnostics
await service.auto_tune("station-a") # One-shot FTUNE + SAVE
await service.tune("station-a", bank=0) # Interactive TUNE per bank + TQUIT
status = await service.get_scanner_status("station-a")
# {"busy": 0, "last_cmd": 0, "error": 0} # BUSYSTAT / CMDSTAT / ERRSTAT
Structured Logging
from barcode_scanner.logger import set_log_callback
def on_scanner_log(code, source, level, message):
mqtt_publish("scanner/logs", {"code": code, "source": source, "message": message})
set_log_callback(on_scanner_log)
Keyence Protocol
TCP port 9004. ASCII commands terminated with \r.
Scan sequence: LON\r (open scan window) -> dwell -> LOFF\r (close window) -> read response. The scanner only sends data after LOFF.
| Command | Response | Purpose |
|---|---|---|
LON\r |
barcode or ERROR\r |
Open scan window (default bank) |
LON,01\r |
barcode or ERROR\r |
Open scan window (bank 1) |
LOFF\r |
-- | Close scan window |
BCLR\r |
OK\r |
Clear read buffer |
RESET\r |
OK\r |
Reset scanner (reboots, drops TCP) |
FTUNE\r |
OK,FTUNE\r then result |
One-shot auto-focus calibration |
TUNE,00\r |
OK\r |
Start interactive tuning (bank 0) |
TQUIT\r |
OK\r |
Stop tuning and save |
SAVE\r |
OK\r |
Persist settings across power cycles |
BUSYSTAT\r |
0/1/2 |
Query busy state (idle/reading/tuning) |
CMDSTAT\r |
0/1/2 |
Query last command result |
ERRSTAT\r |
0/1/2 |
Query hardware error state |
API
ScannerService
class ScannerService:
@classmethod
def from_config(cls, config, mode="real", scanner_type="keyence",
label_validator=None) -> ScannerService
async def scan(self, location_id, timeout=None, bank=None) -> ScanResult
async def connect_all(self) -> dict[str, bool]
async def disconnect_all(self) -> None
async def reset(self, location_id) -> bool
async def reset_all(self) -> dict[str, bool]
async def auto_tune(self, location_id) -> bool
async def tune(self, location_id, bank=0) -> bool
async def get_scanner_status(self, location_id) -> dict
def get_metrics(self) -> dict[str, ScanMetrics]
def get_metrics_summary(self) -> dict
ScanResult
class ScanResult:
status: ScanStatus # OK | NO_READ | TIMEOUT | ERROR
label_detected: bool
label_string: str | None
scanner_id: str
error: str | None
Scanner ABC
class Scanner(ABC):
async def scan(self, timeout=None, bank=None) -> ScanResult
async def connect(self) -> bool
async def disconnect(self) -> None
async def reset(self) -> bool
async def auto_tune(self) -> bool
async def tune(self, bank=0) -> bool
async def get_status(self) -> dict
connected: bool # property
Development
cd barcode-scanner
uv sync
make test # 115 tests
make lint # ruff check + format
make type-check # pyright
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vention_barcode_scanner-0.4.2.tar.gz.
File metadata
- Download URL: vention_barcode_scanner-0.4.2.tar.gz
- Upload date:
- Size: 29.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9034c0cca989933c47d78e29cfafa4a1510d1bbe9bc5b8c78928720b1530d499
|
|
| MD5 |
620daa42b0de27aefb46d7225fd4919c
|
|
| BLAKE2b-256 |
6d8798ac3d0fc02876609c5327143f5a878013a051b033cac86913941ee68977
|
File details
Details for the file vention_barcode_scanner-0.4.2-py3-none-any.whl.
File metadata
- Download URL: vention_barcode_scanner-0.4.2-py3-none-any.whl
- Upload date:
- Size: 27.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63beb6b4c3fa6892e4ab97ad4ea724ab308f788895dbe05c254492e4ebb6f889
|
|
| MD5 |
8625e45c45c6ee3e947434fd8159b3ec
|
|
| BLAKE2b-256 |
8c378a90bb842306fcd00826221452e2df2cd68fbfff957a83b52d7ff49fbbdb
|