safera-sense-ble
Async Python library for talking to Safera Sense cooking sensors over Bluetooth Low Energy — as found in Røros Hetta cooker hoods (Safera Sense Integral) and Safera stove guards.
Credits
Most of the protocol reverse engineering comes from two projects that did the heavy lifting:
- magicus/safera-ble — the byte-level protocol documentation, derived from packet captures and analysis of the decompiled Android app;
- havardgulldahl/rorossense-ble — a working Python client and further protocol exploration against a RørosHetta hood, including the consolidated protocol docs.
This library is an independent, from-scratch implementation of that
protocol, extended with findings made during its development (verified
on an IFU10CR-PRO, firmware 13/75):
- the proprietary characteristics require BLE bonding — the device answers ATT "Insufficient authentication" until paired; this library pairs on demand and retries;
- grease filter saturation lives at byte 59 of the extended sensor
report and resets via the
SET_HOOD_FILTER_CHANGEDcommand (confirmed with a before/after diff around the vendor app's filter reset); - the "particle index" field tracks the vendor app's PM2.5 reading (µg/m³, 0.2 µg/m³ raw resolution).
Unofficial project — not affiliated with Safera Oy or Røros Metall AS.
Features
- Live sensor stream: subscribe to notifications (~1 Hz) parsed into a
typed
SensorReport— ambient/surface/pan temperature, humidity, ambient light, eCO2, tVOC, air quality index, PM2.5, stove power draw, cooking activity, alarm level, grease filter saturation, device state and error bitfields. - Control: hood fan speeds 1–3, boost, auto mode; light levels 1–3; identify; grease-filter reset.
- Device info: model, serial, hardware/firmware revisions, Wi-Fi status (SSID, RSSI, device name).
- Sensible precision: values are quantized to each sensor's meaningful resolution (0.1 °C temperatures, whole-percent humidity, whole lux, 0.1 µg/m³ PM2.5) so consumers don't see measurement jitter as state changes.
- Built on bleak and bleak-retry-connector; plays well with Home Assistant's Bluetooth stack but has no Home Assistant dependency.
Installation
pip install safera-sense-ble
Usage
Find the device
Safera devices advertise the proprietary service UUID and, depending on
branding, a name like Røroshetta, iSense… or Sense_…:
import asyncio
from bleak import BleakScanner
from safera_sense_ble import SAFERA_SERVICE_UUID
async def find():
devices = await BleakScanner.discover(return_adv=True)
for device, adv in devices.values():
if SAFERA_SERVICE_UUID in adv.service_uuids:
print(device.address, adv.local_name)
asyncio.run(find())
Stream sensor data
import asyncio
from bleak import BleakScanner
from safera_sense_ble import SaferaSenseClient, SensorReport
async def monitor(address: str):
ble_device = await BleakScanner.find_device_by_address(address)
client = SaferaSenseClient(ble_device)
await client.connect()
info = await client.fetch_device_info()
print(f"Connected to {info.model} (fw {info.firmware_rev})")
def on_report(report: SensorReport) -> None:
print(
f"{report.ambient_temperature:.1f} °C "
f"{report.humidity} %RH "
f"eCO2 {report.co2_ppm} ppm "
f"PM2.5 {report.particle_index} µg/m³ "
f"filter {report.grease_filter} %"
)
# The device requires bonding for this; the client pairs on demand.
await client.subscribe_sensor_reports(on_report)
await asyncio.sleep(30)
await client.disconnect()
asyncio.run(monitor("D4:6A:C8:XX:XX:XX"))
Control the hood
from safera_sense_ble import FanSpeed, LightLevel
await client.set_fan_speed(FanSpeed.LEVEL_2) # speeds 1-3
await client.set_fan_speed(FanSpeed.BOOST) # time-limited boost
await client.set_fan_auto() # air-quality controlled
await client.set_fan_speed(FanSpeed.OFF)
await client.set_light_level(LightLevel.LEVEL_3)
await client.set_light_level(LightLevel.OFF)
await client.identify() # make the device identify itself
await client.reset_grease_filter() # after cleaning the filter
One-shot reads
report = await client.fetch_sensor_report() # single parsed snapshot
wifi = await client.fetch_wifi_status() # SSID, RSSI, device name, IP
See examples/monitor.py for a runnable script.
API notes
- All methods are coroutines; the client is designed for a single long-lived connection (the device accepts one central at a time — close the vendor app while connected).
SensorReport.from_bytesaccepts both the documented 54-byte record and the extended (~69-byte) record sent by hood-integrated devices; hood-only fields (fan_speed_raw,light_raw,grease_filter, …) areNonewhen absent.SaferaSenseClient.dump_characteristics()hex-dumps every readable characteristic — useful for decoding the remaining unknown fields (byte 58 of the sensor report, the event log,READ_SETTINGS, …); contributions welcome here and upstream.
License
MIT — see LICENSE.
Metadata
Release files for safera-sense-ble 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| safera_sense_ble-0.2.0.tar.gz | 14.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| safera_sense_ble-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.7 kB
Release files / safera_sense_ble-0.2.0.tar.gz
| Download URL | safera_sense_ble-0.2.0.tar.gz |
|---|---|
| Size | 14.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ba4a3ce4cb107103e7585098171246fe719eb6eef22ff5331ef84b47aa2970bc
|
|
BLAKE2b-256 checksum How to use checksums |
962a705cb8bf8b2f9127677af5bb99e82e1b52fcd0db3b1ec5b74c07ec8d0726
|
| 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 3, 2026.
Transparency logRelease files / safera_sense_ble-0.2.0-py3-none-any.whl
| Download URL | safera_sense_ble-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bfecca4fc887c0defb510f925a3cd23d3381c2fe2ec7697d6d67e15621e75d35
|
|
BLAKE2b-256 checksum How to use checksums |
9f30b755e9f357bc691c6af73e1102e6a73666d020c474c1aad1ec89e5035b8c
|
| 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 3, 2026.
Transparency log