for people who like tinkering with sensors

sensortap

One registry. Every sensor your machine has. sensortap finds the camera, the motion sensors, the battery, the radios, and the hardware telemetry, whatever is actually there, and reads every one of them through the same small API.

It's the layer under a project like WinDuo or Mac Duo: one sensor turned into one effect, hand-wired to one platform. sensortap makes the next ten ideas like that fast to build, instead of a fresh reverse-engineering job every time.

Free and open source. Windows today, Linux adapters open as a contribution. No account, no telemetry.

tested on Dell G3 3779
$ pip install sensortap
$ sensortap list
accel.reference.0          accel       m/s2   present
accel.winrt.0               accel       m/s2   absent
battery.win-pct...          battery     %      present
clock.hwmon.08ba84...       clock       MHz    present
hinge-angle.winrt.0         hinge-angle deg    absent
load.hwmon.037e93...        load        %      present
microphone.reference.0      microphone         present
radio-signal.win-radio.0    radio-signal %     present
temp.hwmon.2aed454...       temp        degC   present
voltage.hwmon.2da99...      voltage     V      present
… 76 more rows
total sensors: 86  kinds: 16  backends contributing: 10  non-contributing: 0

That transcript is real

86 sensors. 16 kinds. 10 of 10 backends loaded, zero failing. That's the real output of running the CLI on an ordinary laptop, a Dell G3 3779, re-run right before this page was published.

Half of it is the obvious hardware: a camera, a microphone, a battery, Wi‑Fi and Bluetooth radios. The other half is the part most owners have never seen surfaced anywhere on this exact machine: a hinge‑angle sensor, an inclinometer, per‑core CPU load for all eight logical cores, live die temperatures, individual power rail voltages, and clock speeds. The last 65 of those arrive once sensortap's bundled Windows helper is running, through the same sensortap list call, no extra code.

One registry, many adapters, none of them knowing about each other

A camera adapter and a hardware-telemetry adapter have nothing in common as code: one talks to WinRT, the other spawns a bundled .NET helper and reads LibreHardwareMonitor over a local pipe. Each sensor family is one adapter, one file, registered through a normal Python entry point. The registry's job is narrow: load every adapter it finds, run discovery concurrently with a shared timeout so one slow backend can't stall the rest, de-duplicate and validate what comes back, and hand you one flat, typed list.

Every sensor, regardless of source, reports the same shape: a stable id, a kind from one closed vocabulary, a unit, a sampling rate, and whether it's currently reachable. That's what makes the CLI's one command work identically whether it's asking a webcam or a voltage rail.

registry camera motion battery radio .NET helper hwmon
Each adapter owns one sensor family and one backend. The registry never imports an adapter's internals; it only calls discover() and read(), the two methods every adapter has to implement.

Enumeration never opens a device

Listing sensors never turns on a camera light or triggers an OS permission prompt, not even for the camera and microphone entries in the list above.

Reading or streaming a camera, a microphone, or a touchpad's raw capacitive image needs your code to name that exact sensor id in a consent grant first. Grants live in memory only, are never written to disk, and end automatically when your code is done with them. Everything else, motion, battery, radios, hardware telemetry, carries no personal information and needs no grant at all.

Two ways to use it

A CLI for looking, a Python API for building.

  1. Look

    $ pip install sensortap
    $ sensortap list
    $ sensortap read accel.reference.0
    $ sensortap stream microphone.reference.0 --consent microphone.reference.0
  2. Build

    import sensortap
    
    # enumerate — opens nothing, prompts nothing
    sensors = sensortap.list_sensors()
    
    # read a sensor that needs no consent
    reading = sensortap.read("accel.winrt.0")
    
    # a privacy-sensitive sensor needs an explicit grant
    with sensortap.consent(["microphone.winrt.0"]):
        for block in sensortap.stream("microphone.winrt.0"):
            ...
  3. Contribute a sensor

    One sensor family is one adapter file, registered through a standard Python entry point — no edit to sensortap's own source. A reusable Conformance_Check ships in the package itself, so you can validate your adapter against the exact same rules sensortap's own nine Windows adapters were checked against, without needing sensortap's repository at all.

What's real today, what isn't yet

A young, one-person project. No adoption numbers exist to quote, so none are invented here.

Windows is where it's built out
Nine adapters ship: WinRT motion, orientation, light, camera, and microphone; battery; Wi‑Fi and Bluetooth; touchpad; and a bundled .NET helper bridging LibreHardwareMonitor for CPU, GPU, and board telemetry. All nine run against real hardware, none of them mocked.
Linux is designed, not built
The adapter interface and the registry are already platform-neutral. Nothing in the core imports anything Windows-specific. A Linux backend (sysfs hwmon, V4L2, ALSA, evdev) is the clearest open contribution, and it's exactly the kind of one-file-per-family task the adapter interface was built for.
The hardware-telemetry helper is optional weight
pip install sensortap alone stays small: motion, camera, battery, and radio sensors all work with zero extra downloads. Per-core CPU load, temperatures, and voltages need sensortap[hwmon], which pulls in a self-contained .NET helper of a few dozen megabytes. That weight is disclosed, not hidden in the base install.
A sensor can be there and still say absent
Several rows in the transcript above read absent. A WinRT sensor class the operating system doesn't expose reports absent rather than guessing whether the physical chip exists underneath it. Reporting a wrong number is worse than reporting none.