Skip to main content

Python client for Scentience olfaction instruments over BLE Bluetooth

Project description

scentience

Python client for Scentience olfaction instruments over BLE Bluetooth.

Installation

BLE only:

pip install scentience

BLE + COLIP embedding models:

pip install "scentience[models]"

Requirements

  • Python 3.8+
  • Bluetooth 4.0+ adapter
  • A Scentience developer API key (obtain from the Scentience portal)
  • For embedding models: torch, torchvision, transformers, huggingface-hub, Pillow (installed via [models] extra)

Connecting a single device

Auto-discover

Scans for the first Scentience device in range and connects to it.

import scentience as scn

device = scn.ScentienceDevice(api_key="YOUR_API_KEY")
device.connect_ble(char_uuid="YOUR_CHAR_UUID")

Target a specific device

Pass the device UID (serial number or BLE name) to connect to a particular instrument.

device.connect_ble(char_uuid="YOUR_CHAR_UUID", device_uid="A00022")

Take a single reading

data = device.sample_ble()   # returns dict
print(data)

Stream continuously

def on_sample(data: dict) -> None:
    print(data["UID"], data.get("CO2"), data.get("ENV_temperatureC"))

device.stream_ble(callback=on_sample)

# ... your application runs here ...

device.stop_stream()
device.disconnect()

Context manager

disconnect() is called automatically on exit.

with scn.ScentienceDevice(api_key="YOUR_API_KEY") as device:
    device.connect_ble(char_uuid="YOUR_CHAR_UUID", device_uid="A00022")
    data = device.sample_ble()
    print(data)

Connecting multiple devices

Pass a list of device UIDs to connect_ble. All devices are scanned for in a single BLE scan and connected concurrently. The API key and characteristic UUID are the same for every device.

Snapshot from all devices

import scentience as scn

device = scn.ScentienceDevice(api_key="YOUR_API_KEY")
device.connect_ble(
    char_uuid="YOUR_CHAR_UUID",
    device_uids=["A00022", "A00010"],   # as many UIDs as needed
)

readings = device.sample_ble()   # returns List[dict], one entry per device
for r in readings:
    print(r)

Stream from all devices simultaneously

The same callback receives packets from every device. Use the UID field to identify the source.

def on_sample(data: dict) -> None:
    print(f"[{data['UID']}]  CO2={data.get('CO2')}  temp={data.get('ENV_temperatureC')}°C")

device.stream_ble(callback=on_sample)

# ... your application runs here ...

device.stop_stream()
device.disconnect()

Context manager (multiple devices)

with scn.ScentienceDevice(api_key="YOUR_API_KEY") as device:
    device.connect_ble(
        char_uuid="YOUR_CHAR_UUID",
        device_uids=["A00022", "A00010"],
    )
    readings = device.sample_ble()

Discovering devices

Before connecting, use scan_devices() to see exactly what BLE devices are visible and confirm the names your instruments advertise.

nearby = scn.ScentienceDevice.scan_devices(timeout=10.0)
for d in nearby:
    print(d["name"], d["address"], d["rssi"])

Pass any value from the name column as device_uid / device_uids.


Logging and export

All readings from sample_ble() and stream_ble() are automatically buffered in memory across all connected devices.

print(device.log)             # list of dicts

device.export_json("readings.json")
device.export_csv("readings.csv")

device.clear_log()

CSV column order: UID and TIMESTAMP first, then all remaining fields alphabetically. Missing fields are written as empty values.


Response payload

Each reading dict may contain:

Key Description
UID Device serial number
TIMESTAMP Reading timestamp
ENV_temperatureC Ambient temperature (°C)
ENV_humidity Relative humidity (%)
ENV_pressureHpa Barometric pressure (hPa)
BATT_health Battery health
BATT_v Battery voltage
BATT_charge Battery charge (%)
BATT_time Estimated battery time remaining
STATUS_opuA Operational status
CO2, NH3, NO, NO2, CO, C2H5OH Chemical compounds (non-zero only)
H2, CH4, C3H8, C4H10, H2S, HCHO, SO2, VOC Chemical compounds (non-zero only)

COLIP embedding models

ColipModel integrates the Olfaction-Vision-Language Embeddings to produce joint embeddings across olfaction, vision, and language modalities.

Four variants are available:

Variant Embed dim Architecture Best for
colip-small-base 512 base GNN Fast inference / edge devices
colip-small-gat 512 GAT Higher accuracy on edge devices
colip-large-base 2048 base GNN Accuracy-critical tasks
colip-large-gat 2048 GAT Highest accuracy, slower inference

Weights are downloaded from HuggingFace Hub and cached locally on first use.

Example — small base model

import scentience as scn
from PIL import Image

model = scn.ColipModel.from_pretrained("colip-small-base")
# ColipModel(variant='colip-small-base', embed_dim=512, device='cpu')

image   = Image.open("scene.jpg")
olf_vec = [0.0] * 138          # 138-dimensional olfactory descriptor

embedding = model.embed(image, olf_vec)       # torch.Tensor, shape (512,)
arr       = model.embed_numpy(image, olf_vec) # numpy array

Example — large GAT model

model = scn.ColipModel.from_pretrained("colip-large-gat")
embedding = model.embed(image, olf_vec)       # torch.Tensor, shape (2048,)

Example — BLE streaming + real-time embedding

import scentience as scn
from PIL import Image

model  = scn.ColipModel.from_pretrained("colip-small-base")
device = scn.ScentienceDevice(api_key="YOUR_API_KEY")
device.connect_ble(char_uuid="YOUR_CHAR_UUID", device_uid="A00022")

scene = Image.open("scene.jpg")

def on_sample(data: dict) -> None:
    olf_vec   = [data.get(k, 0.0) for k in sorted(data) if k not in ("UID", "TIMESTAMP")]
    embedding = model.embed(scene, olf_vec)
    print(f"embedding shape: {embedding.shape}, norm: {embedding.norm():.4f}")

device.stream_ble(callback=on_sample)

Targeting a specific compute backend

model = scn.ColipModel.from_pretrained("colip-large-base", device="cuda")  # GPU
model = scn.ColipModel(variant="colip-small-gat", device="mps")             # Apple Silicon

API reference

See the full BLE API documentation.

License

Apache 2.0

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

scentience-0.4.0.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

scentience-0.4.0-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file scentience-0.4.0.tar.gz.

File metadata

  • Download URL: scentience-0.4.0.tar.gz
  • Upload date:
  • Size: 17.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.0

File hashes

Hashes for scentience-0.4.0.tar.gz
Algorithm Hash digest
SHA256 e810264de1ef385bff89ba9214341fa9128853043e054f22ece2105824e61d6a
MD5 939c27119dfc97d477e277b3df745628
BLAKE2b-256 2de44a26716b63f608c534aa834f4ed8254937dbcebb4d681529f41f4576be84

See more details on using hashes here.

File details

Details for the file scentience-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: scentience-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 19.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.0

File hashes

Hashes for scentience-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ef78427cc20a5842fe5f6d6766848ae4bf7d4f50fbe63cefe71605067620320b
MD5 250484c6b00c519ea23e4f3576dd797f
BLAKE2b-256 b72b28fd01188a76e94d15de7991569263406b3ca5006124af5285d8cc3f096f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page