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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e810264de1ef385bff89ba9214341fa9128853043e054f22ece2105824e61d6a
|
|
| MD5 |
939c27119dfc97d477e277b3df745628
|
|
| BLAKE2b-256 |
2de44a26716b63f608c534aa834f4ed8254937dbcebb4d681529f41f4576be84
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef78427cc20a5842fe5f6d6766848ae4bf7d4f50fbe63cefe71605067620320b
|
|
| MD5 |
250484c6b00c519ea23e4f3576dd797f
|
|
| BLAKE2b-256 |
b72b28fd01188a76e94d15de7991569263406b3ca5006124af5285d8cc3f096f
|