Lid angle trigger

@Andreas_Hegenberg I just installed this app called LidAngleSensor,

I thought having the current lid angle as a variable and/or trigger in BTT would be neat, enabling new use cases. What are your thoughts?

hehe yes, I also saw this earlier and it sounds like a fun trigger/variable :slight_smile:

Might be limited to some Macbook models tough

1 Like

According to the GitHub issues and the README file, M1 isn't supported. But it's working perfectly well on my M1 MacBook Pro!

❯ ./diagnose_lid_sensor.sh
==============================================
MacBook Lid Angle Sensor Diagnostic Tool
==============================================

System Information:
- Model: MacBook Pro
- Chip: Apple M1 Max
- macOS: 15.6.1

Step 1: Looking for exact expected sensor...
Command: hidutil list --matching '{"VendorID":0x5ac,"ProductID":0x8104,"PrimaryUsagePage":32,"PrimaryUsage":138}'
✅ FOUND EXACT MATCH:
Services:
VendorID ProductID LocationID UsagePage Usage RegistryID  Transport Class             Product UserClass Built-In
0x5ac    0x8104    0x0        32        138   0x100000b7e SPU       AppleSPUHIDDriver (null)  (null)    1

Devices:
VendorID ProductID LocationID UsagePage Usage RegistryID  Transport Class             Product UserClass Built-In
0x5ac    0x8104    0x0        32        138   0x100000b51 SPU       AppleSPUHIDDevice (null)  (null)    1

Your sensor should work!

great! I‘m sure users will find creative ways to make use of it

1 Like

I had a look at the code and the examples for different generic devices, but it seems it is only designed to trigger a device when certain conditions are set, but there's no way to just report the number?

I.e. I could create 131 triggers from 0˚ angle to 130˚ angle. But I can't create a trigger that mimics the battery charge triggers. Is that correctly understood?

Here's a sample for using the Generic Device BTT features with the lid sensor. It pops up a HUD when the lid closes to below 35˚:

Device JSON:

[
  {
    "BTTLastUpdatedAt" : 1757309391.03107,
    "BTTTriggerType" : 763,
    "BTTTriggerTypeDescriptionReadOnly" : "Generic Device & Input Analyzer",
    "BTTTriggerTypeDescription" : "Apple:LidSensor",
    "BTTTriggerClass" : "BTTTriggerTypeGenericDevice",
    "BTTUUID" : "6AE63F0E-DA4F-455B-93F4-F2EA3DCFB62B",
    "BTTOrder" : 0,
    "BTTGenericDeviceConfiguration" : {
      "BTTGenericDeviceActiveReports" : {
        "1" : true
      },
      "BTTGenericDeviceVendorID" : "0x5ac",
      "BTTGenericDeviceUsagePair2" : {
        "BTTGenericDeviceUsagePage" : "",
        "BTTGenericDeviceUsage" : ""
      },
      "BTTGenericDeviceSelectedTab" : 0,
      "BTTGenericDeviceName" : "Apple:LidSensor",
      "BTTGenericDeviceDropdownSelection" : {
        "manufacturer" : "Apple",
        "usagePairs" : [
          {
            "DeviceUsagePage" : 32,
            "DeviceUsage" : 138
          }
        ],
        "productID" : 33028,
        "serial" : "",
        "vendorID" : 1452
      },
      "BTTGenericDeviceScript" : "function analyzeDeviceInput(targetDevice, reportID, reportDataHex) {\n        let reportBuffer = buffer.Buffer.from(reportDataHex, 'hex');\n        if(reportBuffer.readUInt8(1) < 0x23) {\n        log('trigger!');\n        bttTriggerDeviceTrigger(targetDevice, 'closing');\n}\n",
      "BTTGenericDeviceProvidedTriggers" : "closing",
      "BTTGenericDeviceUsagePair1" : {
        "BTTGenericDeviceUsagePage" : "0x20",
        "BTTGenericDeviceUsage" : "0x8a"
      },
      "BTTGenericDeviceProductID" : "0x8104",
      "BTTGenericDeviceWatchedBytes" : {
        "1" : [
          1
        ]
      }
    },
    "BTTGestureNotes" : "Apple:LidSensor"
  }
]

Trigger JSON:

[
  {
    "BTTLastUpdatedAt" : 1757309172.5881009,
    "BTTTriggerType" : 764,
    "BTTTriggerTypeDescription" : "Apple:LidSensor: closing",
    "BTTTriggerClass" : "BTTTriggerTypeGenericDevice",
    "BTTUUID" : "C1270650-ECCE-418A-AA79-1BB7A9E7452B",
    "BTTAdditionalConfiguration" : "6AE63F0E-DA4F-455B-93F4-F2EA3DCFB62B;;closing",
    "BTTOrder" : 1,
    "BTTActionsToExecute" : [
      {
        "BTTLastUpdatedAt" : 1757309181.780602,
        "BTTTriggerParentUUID" : "C1270650-ECCE-418A-AA79-1BB7A9E7452B",
        "BTTIsPureAction" : true,
        "BTTTriggerClass" : "BTTTriggerTypeGenericDevice",
        "BTTUUID" : "BABD05E7-D325-4BF7-858B-050B18D5A2D0",
        "BTTPredefinedActionType" : 254,
        "BTTPredefinedActionName" : "Show HUD Overlay",
        "BTTHUDActionConfiguration" : "{\"BTTActionHUDBlur\":true,\"BTTActionHUDBackground\":\"0.000000, 0.000000, 0.000000, 0.000000\",\"BTTIconConfigImageHeight\":100,\"BTTActionHUDPosition\":0,\"BTTActionHUDDetail\":\"\",\"BTTActionHUDDuration\":0.89999997615814209,\"BTTActionHUDDisplayToUse\":0,\"BTTIconConfigImageWidth\":100,\"BTTActionHUDSlideDirection\":0,\"BTTActionHUDHideWhenOtherHUDAppears\":false,\"BTTActionHUDWidth\":220,\"BTTActionHUDAttributedTitle\":\"{\\\\rtf1\\\\ansi\\\\ansicpg1252\\\\cocoartf2822\\n\\\\cocoatextscaling0\\\\cocoaplatform0{\\\\fonttbl\\\\f0\\\\fnil\\\\fcharset0 SFPro-Bold;\\\\f1\\\\fswiss\\\\fcharset0 Helvetica;\\\\f2\\\\fnil\\\\fcharset0 SFPro-Regular;\\n}\\n{\\\\colortbl;\\\\red255\\\\green255\\\\blue255;\\\\red0\\\\green0\\\\blue0;}\\n{\\\\*\\\\expandedcolortbl;;\\\\cssrgb\\\\c0\\\\c0\\\\c0\\\\c84706\\\\cname labelColor;}\\n\\\\pard\\\\tx560\\\\tx1120\\\\tx1680\\\\tx2240\\\\tx2800\\\\tx3360\\\\tx3920\\\\tx4480\\\\tx5040\\\\tx5600\\\\tx6160\\\\tx6720\\\\pardirnatural\\\\qc\\\\partightenfactor0\\n\\n\\\\f0\\\\b\\\\fs80 \\\\cf2 NO!\\n\\\\f1\\\\b0\\\\fs24 \\\\\\n\\\\pard\\\\tx560\\\\tx1120\\\\tx1680\\\\tx2240\\\\tx2800\\\\tx3360\\\\tx3920\\\\tx4480\\\\tx5040\\\\tx5600\\\\tx6160\\\\tx6720\\\\pardirnatural\\\\qc\\\\partightenfactor0\\n\\n\\\\f2\\\\fs48 \\\\cf2 Please don't leave me!}\",\"BTTActionHUDBorderWidth\":0,\"BTTActionHUDTitle\":\"\",\"BTTActionHUDHeight\":220}",
        "BTTOrder" : 0
      }
    ],
    "BTTGestureNotes" : "Apple:LidSensor: closing"
  }
]

The link in the device analyzer code comments points at https://docs.folivora.ai/1500_generic_devices.html, which should be changed to Generic Devices · GitBook

I used it to create a trigger to pause spotify and set the system volume to 0% if the lid goes below 20˚. Very handy!

Is there a way to keep state in the analyzer script?

I'm basically trying to do this (non-working semi-pseudo-code):

function analyzeDeviceInput(targetDevice, reportID, reportDataHex) {
    let reportBuffer = buffer.Buffer.from(reportDataHex, 'hex');
    var closing = false;
    var opening = false;
    if(reportBuffer.readUInt8(1) < 0x14) {
        log('trigger!');
        if (!closing) {
            bttTriggerDeviceTrigger(targetDevice, 'closing');
            closing = true;
            opening = false;
        }
    }
    if(reportBuffer.readUInt8(1) > 0x14) {
        log('trigger!');
        if (!opening) {
            bttTriggerDeviceTrigger(targetDevice, 'opening');
            opening = true;
            closing = false;
        }
     }
}

But it doesn't work. If I move the lid below 20˚, the closing trigger fires every degree. And the opening trigger fires every degree above 20˚. Is there a way to do what I'm trying to? Can I set some sort of persistent global variable?

Edit: I can't reply to myself any more, monologues are not permitted. But here is how I solved it:

So I have now made my lid into a media player!

Just realized that I could make it into a volume control... :smiley:

to keep state put the variables outside the function:

Of course. Me and scopes...

Moved the var declarations up above the function, and it worked as intended.

Now I just need to write 100 if-statements to make my lid into a volume control. :slight_smile:

in theory you could also change the volume from your script but that would make it less generic :wink:

 set_number_variable({ variableName: "OutputVolume", to: value });

where value is a value between 0 and 1

Perfect!

I should do that and post it here: https://uxdesign.cc/the-worst-volume-control-ui-in-the-world-60713dc86950

1 Like

yes! :joy:

Damn it! Someone already did!

https://miro.medium.com/v2/resize:fit:720/format:webp/1*x96b41G9y84eC--p18ghcQ.gif

haha too bad :sweat_smile:

This is the exact kind of creativity I was looking for with this new trigger :joy:

Here's a Preset that includes:

  • Generic Device setup with a "Close", "Open", and "Current Angle" Actions. The JS script is well documented and defines global variables which can easily be modified (e.g. the lid angle to trigger "close" or "open")
  • "Close" trigger runs a Shell script that says "Closing my Mac"
  • "Open" trigger runs a Shell script that says "Opening my Mac"
  • "Current Angle" displays a HUD with the current lid angle on change.

lid_angle_preset.bttpreset (8.3 KB)

4 Likes

Here's a preset that:

Displays a menu bar icon with the current angle. It turns red when below the cutoff.
Pause Spotify when lid is < the cutuff.
Play Spotify when lid is > the cutuff.

You can easily change the cutoff at the top of the analyzer script (default: 50˚), and/or add/change your own actions to the three triggers.

Why? Because you can never have too many menu bar icons.

Lid Angle.bttpreset (17.8 KB)

Edit: Updated it to only start Spotify when opening the lid if it was playing before it was closed.

2 Likes

@Andreas_Hegenberg : The alternate font color is flakey. It turns the icon red as expected, but the text is red for a little while and then turns white again. So the icon and the text has different colors. How is that even possible?

There's no way to send text / icon colors using update_menubar_item?

My regex is ^[0-4][0-9]˚$.

You can update the text/icon color using update_menubar_item like this:

tell application "BetterTouchTool" to update_menubar_item "2F307570-D516-42FB-89DC-6F3C5FB32771" text "new text" font_color "255,244,100,255"

When updating from JS you should be able to use these parameters:

{'text':'newTitle', 'sf_symbol_name': 'house', 'sf_symbol_size': 12,  'icon_data': 'base64_icon_data', 'icon_path':'path_to_new_icon', 'background_color': '255,85,100,255', 'font_color': '100,200,100,255', 'font_size': 10}

(Alternatively you can provide a basic html string as title like <html><span style="color:red">test</span></html> but this is harder to get to look right)

I'll check whether I can reproduce the regex font color issue!

If you use a script to define the item you can also return all of these parameters directly:

I already tried font_color and icon_color, but it didn't make any difference in behavior.