Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: build

on:
push:
branches: [master]
branches: ['**']
tags: ['*.*.*']
pull_request:
branches: [master]
Expand All @@ -17,11 +17,6 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev

- name: Set up uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0

Expand All @@ -46,12 +41,6 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Install system dependencies
if: matrix.platform.os == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev

- name: Set up uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
Expand All @@ -74,10 +63,6 @@ jobs:
whl=$(ls dist/*.whl)
python -m pip install "$whl[examples]"

# TODO: Enable when tests are added
# - name: Run tests
# run: uv run pytest -ra -v

release:
if: startsWith(github.ref, 'refs/tags/')
needs: [lint, test]
Expand All @@ -87,11 +72,6 @@ jobs:
with:
fetch-depth: 0

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev

- name: Set up uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ pip install --upgrade 'radiacode[examples]'

Run the web interface shown in the screenshot above:
```bash
# Via Bluetooth (Linux only, replace with your device's address)
# Via Bluetooth (use the device's MAC address, or its CoreBluetooth UUID on macOS)
$ python3 -m radiacode.examples.webserver --bluetooth-mac 52:43:01:02:03:04

# Via USB connection (Linux/MacOS/Windows)
# Via USB connection (Linux/macOS/Windows)
$ sudo python3 -m radiacode.examples.webserver
```

Expand Down Expand Up @@ -69,7 +69,7 @@ device.set_language('en') # 'en' or 'ru'

#### More Features
```python
# Bluetooth connection (Linux only)
# Bluetooth connection (use the device's CoreBluetooth UUID on macOS)
device = RadiaCode(bluetooth_mac="52:43:01:02:03:04")

# Connect to specific USB device
Expand Down Expand Up @@ -109,19 +109,19 @@ device.set_display_off_time(30) # Auto-off after 30 seconds

## ⚠️ Platform-Specific Notes

### MacOS
### macOS
- ✅ USB connectivity works out of the box
- ❌ Bluetooth is not supported (bluepy limitation)
- ✅ Bluetooth connectivity supported via Bleak (use the device's CoreBluetooth UUID)
- 📝 Required: `brew install libusb`

### Linux
- ✅ Both USB and Bluetooth fully supported
- 📝 Required: `libusb` and Bluetooth libraries
- 📝 Required: `libusb` and BlueZ
- 🔑 May need [udev rules](radiacode.rules) for USB access without root

### Windows
- ✅ USB connectivity supported
- ❌ Bluetooth is not supported (bluepy limitation)
- ✅ Bluetooth connectivity supported via Bleak
- 📝 Required: USB drivers

## License
Expand Down
23 changes: 12 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ classifiers = [
"Programming Language :: Python :: 3 :: Only",
]
dependencies = [
"bluepy~=1.3 ; sys_platform == 'linux'",
"pyusb~=1.3",
"bleak>=1.1.1,<4",
"pyusb~=1.3.1",
]

[project.optional-dependencies]
examples = [
"aiohttp~=3.9",
"prometheus-client>=0.19,<0.20",
"matplotlib~=3.7",
"numpy~=1.26",
"pyyaml~=6.0",
"aiohttp>=3.13.5,<4",
"prometheus-client~=0.25.0",
"matplotlib>=3.9.4,<4",
"numpy>=2.0.2,<3",
"pyyaml~=6.0.3",
]

[project.urls]
Expand All @@ -31,20 +31,21 @@ Repository = "https://github.com/cdump/radiacode"

[dependency-groups]
dev = [
"ruff>=0.11,<0.12",
"mypy~=1.16",
"ruff>=0.15.22,<0.16",
"mypy>=1.19.1,<2",
]

[build-system]
requires = ["uv_build>=0.9.13,<0.10.0"]
requires = ["uv_build>=0.12.3,<0.13.0"]
build-backend = "uv_build"

[tool.uv]
exclude-newer = "1 week"
exclude-newer-package = { uv-build = false }

[tool.ruff]
line-length = 130
target-version = "py310"
target-version = "py39"

[tool.ruff.lint]
extend-select = ["B", "Q"]
Expand Down
2 changes: 2 additions & 0 deletions src/radiacode/decoders/databuf.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import datetime

from radiacode.bytes_buffer import BytesBuffer
Expand Down
24 changes: 12 additions & 12 deletions src/radiacode/examples/basic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import argparse
import time
import platform

from radiacode import RadiaCode
from radiacode.transports.usb import DeviceNotFound as DeviceNotFoundUSB
Expand All @@ -10,14 +9,12 @@
def main():
parser = argparse.ArgumentParser()

# Bluetooth is only supported on Linux via bluepy
if platform.system() == 'Linux':
parser.add_argument(
'--bluetooth-mac',
type=str,
required=False,
help='(Linux only) Bluetooth MAC address of radiascan device (e.g. 00:11:22:33:44:55)',
)
parser.add_argument(
'--bluetooth-mac',
type=str,
required=False,
help='Bluetooth device identifier (MAC address or macOS UUID)',
)

parser.add_argument(
'--serial',
Expand All @@ -28,8 +25,8 @@ def main():

args = parser.parse_args()

if hasattr(args, 'bluetooth_mac') and args.bluetooth_mac:
print(f'Connecting to Radiacode via Bluetooth (MAC address: {args.bluetooth_mac})')
if args.bluetooth_mac:
print(f'Connecting to Radiacode via Bluetooth (identifier: {args.bluetooth_mac})')

try:
rc = RadiaCode(bluetooth_mac=args.bluetooth_mac)
Expand Down Expand Up @@ -68,4 +65,7 @@ def main():


if __name__ == '__main__':
main()
try:
main()
except KeyboardInterrupt:
print()
4 changes: 3 additions & 1 deletion src/radiacode/examples/narodmon.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ async def send_data(d):

def main():
parser = argparse.ArgumentParser()
parser.add_argument('--bluetooth-mac', type=str, required=True, help='MAC address of radiascan device')
parser.add_argument(
'--bluetooth-mac', type=str, required=True, help='Bluetooth device identifier (MAC address or macOS UUID)'
)
parser.add_argument('--connection', choices=['usb', 'bluetooth'], default='bluetooth', help='device connection type')
parser.add_argument('--interval', type=int, required=False, default=600, help='send interval, seconds')
args = parser.parse_args()
Expand Down
4 changes: 3 additions & 1 deletion src/radiacode/examples/radiacode-exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

def main():
parser = argparse.ArgumentParser()
parser.add_argument('--bluetooth-mac', type=str, required=False, help='bluetooth MAC address of radiascan device')
parser.add_argument(
'--bluetooth-mac', type=str, required=False, help='Bluetooth device identifier (MAC address or macOS UUID)'
)
parser.add_argument('--update-interval', type=int, default=3, required=False, help='update interval (seconds)')
parser.add_argument('--port', type=int, default=5432, required=False, help='prometheus http port')
args = parser.parse_args()
Expand Down
38 changes: 38 additions & 0 deletions src/radiacode/examples/scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import argparse
import asyncio

from bleak import BleakClient, BleakScanner


async def scan():
devices = await BleakScanner.discover(return_adv=True)
for device, advertisement in devices.values():
name = advertisement.local_name or device.name
if isinstance(name, str) and name.startswith('RadiaCode'):
print(f'RadiaCode device: {device.address} ({name}), RSSI={advertisement.rssi} dB')


async def inspect_device(address):
async with BleakClient(address) as client:
for service in client.services:
print(f'Service {service.uuid}')
for characteristic in service.characteristics:
print(f' Characteristic {characteristic.uuid}: {", ".join(characteristic.properties)}')
if 'read' in characteristic.properties:
print(f' {bytes(await client.read_gatt_char(characteristic))!r}')


async def main():
parser = argparse.ArgumentParser()
parser.add_argument('--scan', action='store_true', help='scan for RadiaCode devices instead of connecting')
parser.add_argument('--address', default='52:43:06:50:01:d7', help='Bluetooth device identifier to inspect')
args = parser.parse_args()

if args.scan:
await scan()
else:
await inspect_device(args.address)


if __name__ == '__main__':
asyncio.run(main())
8 changes: 5 additions & 3 deletions src/radiacode/examples/show-spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

Options:
-h, --help show this help message and exit
-b BLUETOOTH_MAC, --bluetooth-mac BLUETOOTH_MAC Bluetooth MAC address of device
-b BLUETOOTH_MAC, --bluetooth-mac BLUETOOTH_MAC Bluetooth device identifier
-s SERIAL_NUMBER, --serial-number SERIAL_NUMBER serial number of device
-r, --restart restart spectrum accumulation
-R, --Reset reset spectrum stored in device
Expand Down Expand Up @@ -136,7 +136,9 @@ def on_mpl_window_closed(ax):
+ 'show differential and updated cumulative spectrum, '
+ 'and optionally save data to file in yaml format.'
)
parser.add_argument('-b', '--bluetooth-mac', type=str, required=False, help='bluetooth MAC address of device')
parser.add_argument(
'-b', '--bluetooth-mac', type=str, required=False, help='Bluetooth device identifier (MAC address or macOS UUID)'
)
parser.add_argument('-s', '--serial-number', type=str, required=False, help='serial number of device')
parser.add_argument('-r', '--restart', action='store_true', help='restart spectrum accumulation')
parser.add_argument('-R', '--Reset', action='store_true', help='reset spectrum stored in device')
Expand All @@ -163,7 +165,7 @@ def on_mpl_window_closed(ax):
if not quiet:
print(f'\n *==* script {sys.argv[0]} executing')
if bluetooth_mac is not None:
print(f' connecting via Bluetooth, MAC {bluetooth_mac}')
print(f' connecting via Bluetooth, identifier {bluetooth_mac}')
elif serial_number is not None:
print(f' connect via USB to device with SN {serial_number}')
else:
Expand Down
4 changes: 3 additions & 1 deletion src/radiacode/examples/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ async def on_startup(app):

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--bluetooth-mac', type=str, required=False, help='bluetooth MAC address of radiascan device')
parser.add_argument(
'--bluetooth-mac', type=str, required=False, help='Bluetooth device identifier (MAC address or macOS UUID)'
)
parser.add_argument('--listen-host', type=str, required=False, default='0.0.0.0', help='listen host for webserver')
parser.add_argument('--listen-port', type=int, required=False, default=8080, help='listen port for webserver')
args = parser.parse_args()
Expand Down
Loading