Skip to main content

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.

CI PyPI

Credits

Most of the protocol reverse engineering comes from two projects that did the heavy lifting:

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_CHANGED command (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_bytes accepts 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, …) are None when 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.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for safera-sense-ble 0.1.0
File Size Uploaded
safera_sense_ble-0.1.0.tar.gz 13.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for safera-sense-ble 0.1.0
File Interpreter ABI Platform
safera_sense_ble-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 26.7 kB

Release files / safera_sense_ble-0.1.0.tar.gz

Download URL safera_sense_ble-0.1.0.tar.gz
Size 13.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c80d35b878d54cb4a2b4ab6494aa201558cd4917d1f2ad99cfc06510b8c63289
BLAKE2b-256 checksum
How to use checksums
a793473eaa5926ddb8d55fd0250012d7cddc5a5a24596dc6f49933cea074998b
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

Release files / safera_sense_ble-0.1.0-py3-none-any.whl

Download URL safera_sense_ble-0.1.0-py3-none-any.whl
Size 13.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a45c0031785a76577844bc21e91ca0f6741639266622c1025847577240b5013
BLAKE2b-256 checksum
How to use checksums
906f0bd6fbb9209eae2f989ec59d7fba2b89eaad9ee82f2591539323d27fa819
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

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page