From 49628a784976d1d766518497d7fe4471d1e6848a Mon Sep 17 00:00:00 2001 From: jbranchaud Date: Wed, 20 May 2026 12:39:28 -0500 Subject: [PATCH] Add Read The Lid Angle Sensor For A MacBook as a Mac TIL --- README.md | 3 +- ...read-the-lid-angle-sensor-for-a-macbook.md | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 mac/read-the-lid-angle-sensor-for-a-macbook.md diff --git a/README.md b/README.md index 024ed50..9770e22 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/). For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter). -_1793 TILs and counting..._ +_1794 TILs and counting..._ See some of the other learning resources I work on: @@ -753,6 +753,7 @@ If you've learned something here, support my efforts writing daily TILs by - [Open Finder.app To Specific Directory](mac/open-finder-app-to-specific-directory.md) - [Prevent Sleep With The Caffeinate Command](mac/prevent-sleep-with-the-caffeinate-command.md) - [Quickly Type En Dashes And Em Dashes](mac/quickly-type-en-dashes-and-em-dashes.md) +- [Read The Lid Angle Sensor For A MacBook](mac/read-the-lid-angle-sensor-for-a-macbook.md) - [Require Additional JS Libraries In Postman](mac/require-additional-js-libraries-in-postman.md) - [Resize App Windows With AppleScript](mac/resize-app-windows-with-applescript.md) - [Resizing Both Corners Of A Window](mac/resizing-both-corners-of-a-window.md) diff --git a/mac/read-the-lid-angle-sensor-for-a-macbook.md b/mac/read-the-lid-angle-sensor-for-a-macbook.md new file mode 100644 index 0000000..4fa0592 --- /dev/null +++ b/mac/read-the-lid-angle-sensor-for-a-macbook.md @@ -0,0 +1,89 @@ +# Read The Lid Angle Sensor For A MacBook + +MacOS has a bunch of internal HID (Human Interface Device) data that can surface +details about all kinds of "devices" that comprise your machine. Some obvious +ones are the keyboard and trackpad as well as external mice and keyboards. The +battery and power source details are another which is sometimes integrated into +tools that display battery status (e.g. +[`tmux-battery`](https://github.com/tmux-plugins/tmux-battery)), though it uses +`pmset` directly). And many, many more. + +One example I'd never considered is that there is a sensor for the lid angle of +the laptop that can tell the system whether the lid is open or closed and how +open it is (i.e. at what angle). There is no public interface for this lid angle +sensor, but people exploring all the HID devices have found the identifiers that +correspond to it (e.g. +[`pybooklid`](https://github.com/tcsenpai/pybooklid/blob/main/pybooklid/macbook_lid.py)). + +Here is a minimal script that uses `uv`, `hidapi` (python bindings), and +`libhidapi` (shared runtime lib for those bindings): + +```python +#!/usr/bin/env -S uv run --quiet --script +# /// script +# requires-python = ">=3.10" +# dependencies = ["hidapi"] +# /// +"""Print MacBook lid angle in degrees.""" +import os, sys + +if sys.platform == "darwin": + brew = "/opt/homebrew/lib" + if os.path.exists(brew): + os.environ["DYLD_LIBRARY_PATH"] = f"{brew}:{os.environ.get('DYLD_LIBRARY_PATH','')}" + +import hid + +VENDOR_ID, PRODUCT_ID = 0x05AC, 0x8104 +USAGE_PAGE, USAGE = 0x0020, 0x008A +REPORT_ID = 1 + +def read_angle(): + for info in hid.enumerate(VENDOR_ID, PRODUCT_ID): + if info.get("usage_page") == USAGE_PAGE and info.get("usage") == USAGE: + d = hid.device() + path = info["path"] + d.open_path(path if isinstance(path, bytes) else path.encode()) + try: + data = d.get_feature_report(REPORT_ID, 8) + if data and len(data) >= 3: + return float((data[2] << 8) | data[1]) + finally: + d.close() + return None + +if __name__ == "__main__": + a = read_angle() + if a is None: + sys.exit("sensor not available") + print(f"{a:.0f}") +``` + +These IDs and usage values are the undocumented values that allow the script to +navigate specifically to the lid angle sensor and specifically to the usage page +and value that represent the current lid angle reading. + +``` +VENDOR_ID, PRODUCT_ID = 0x05AC, 0x8104 +USAGE_PAGE, USAGE = 0x0020, 0x008A +REPORT_ID = 1 +``` + +I added [this +script](https://github.com/jbranchaud/dotfiles/blob/cbc7196607d1d6b25885f5387ca85b658bd765de/bin/lidangle) +to [my dotfiles](https://github.com/jbranchaud/dotfiles) and made it executable +(`chmod +x bin/lidangle`) so that I can try it out. I first ran it while it was +closed and connected to my external monitor (`0`), then I opened it as far as it +could go (`129`), and then I tried angling it close to what I thought was 90 +degress (`92`, so close). + +```bash +❯ lidangle +0 + +❯ lidangle +129 + +❯ lidangle +92 +```