How to Tell When the Laptop Lid is Closed

I sometimes have trouble with the MagSafe power connector on my MacBook coming unplugged. When I'm actively at my keyboard, I have a macro that is triggered by Power Status Changed that alerts me. That handles most of my power problems.

However, I sometimes use a utility that I found on GitHub, NoSleep, which stops the computer from sleeping when I close the lid. (I installed it when I was working in a big company and would carry my laptop to meetings in the elevator. I saw people every day carrying their laptops open so that they wouldn't have to log back in when they got where they were going. That seemed awkward and risky.) I use it now when I've started a backup or I've put on some music and I want that to continue when I close the lid.

Unfortunately, if the MagSafe connector disconnects while the lid is closed, my Power Status Changed macro can't alert me and if the computer goes all night like that, I can find an empty battery in the morning, like I did this morning.

Also unfortunately, there is no "Lid Status Changed" trigger or "Lid Status" token that I could use to change the mode of the alert to an audible one only when the lid is closed.

For now, I'm adding the Play Sound action every time, but I'd really rather it be only when the lid is closed.

Any ideas?

image

There is an astoundingly powerful macOS shell command called "pmset" that tells you a TON of information about your Mac. Try this:

pmset -g ps / batt

Since I don't have a battery, all it tells me is that I'm running on AC power. But for you, it may tell you the battery's power level.

By writing a macro that triggers every minute (or 5 minutes?) I think you could use this to tell if the battery was draining. Generally speaking, power drains only if the device is unplugged, right? Would that help you?

You could put that command into a KM Execute Shell Script command that returns the result into a KM variable which you could test intermittently for changes.

Come to think of it, I'm going to add to my TODO list the creation of a macro that lets the user interface with all the features of the pmset command. Unless someone else does it first.

Okay, here's a preliminary macro that displays the top ~24 items, based on what option you choose.

PMSET Action (v11.0.1)

Group.kmactions (7.3 KB)

Keyboard Maestro Export

Using the pmset command, I could get KM to send me a text message when my trackpad or keyboard's battery level drops below, say 10%.

This macro is really nice and a tool I'll definitely be using to learn better how to utilise the possibilities of pmset!

However I did not upon my first look over the list of different pmset's see one that returns a status of wether the MacBook lid is closed or not. I found a script via google that returns a AppleClamshellState, ending with Yes if the lid is closed, and No if it is open.

ioreg -r -k AppleClamshellState -d 4 | grep AppleClamshellState | head -1

I was wondering if it was something resembling what I've uploade bellow that you where looking for @August?

Running on battery Notification.kmmacros (4.2 KB)

Macro Image

EDIT: Or a bette solution could maybe be to run just the script in a separate macro, periodically triggered every five minutes or so, and have the rest of the macro triggered by a Power Status Changed trigger.

2 Likes

Thanks. It's just the start of what can be done. A KM macro could also be written to provide direct access to change many of the functions of your Mac using the pmset command. (I actually did this once in the past for a pmset command that I needed, but a more general solution could allow access to all pmset features.)

Thanks @Alexander!

Yes, I think your clamshellState variable might do the trick. However, I'm not clear on the need to update it every five minutes. Why not simply have your macro above triggered by the KBM Power State Changed trigger?

In that approach, clamshellState would be evaluated whenever the Power State changes, and not until then. And I'll make it a local... variable because, without the periodic polling, it cannot be counted on to be meaningful outside this context.

Wow! Thanks @Airy! That looks hugely useful as a framework. I like using the output of pmset -g getters as a Prompt With List list. However, I don't know what a lot of those terms mean. Have you found anywhere that has the various pmset options/getters documented?

You are right, it makes the most sense only triggering it from Power State Changed trigger. I was thinking about it in a weird way where it seemed meaningful not having to wait (the fraction of a second) for the script to execute.

Only way I can now give reason to setting the script up as a separate macro, not affected by the Power State Changed trigger, would be if your unstable MagSafe fell in and out constantly, but if this was the case I guess you'd have bigger problems than this script being ran unnecessarily often.

1 Like

It also appears that the original ioreg command reports a lot of useful stuff. Removing the grep, I found that it also reports

  |   "AppleClamshellCausesSleep" = Yes
  |   "Last Sleep Reason" = "Clamshell Sleep"

and that's without researching what the -r and -k options do.

1 Like

If you type "man pmset" in a macOS Terminal you will get the information that I use to learn about this command. I'm sure there are websites which go into it in more detail.

2 Likes

9 posts were split to a new topic: Why does text show differently between Terminal and KM Window?

@Alexander's Shell Script line answers the "lid closed" issue, but I've discovered that still doesn't solve my probem, so I've asked the next question in a separate thread:

Hi, I'm brand new to KM and I'm not sure if this will be useful. I also wanted to determine if the lid was closed and I was on battery power. For me, this is because I have an external monitor on my MacBook. When I close the lid on AC power, the external monitor becomes the main screen. However, I want the machine to go to sleep regardless of the power source.

I discovered that I can use the "Display layout changed" trigger to activate the macro, because the internal display is removed from the list of displays when the lid closes.

Then, the token %ScreenVisible%Internal% equals 0,0,0,0. This works for me, and I hope it helps others with this issue. This is my first macro, so please let me know if you see a way it can be improved.

1 Like

Thanks, @horo,

Welcome to the KBM Forum!

That's a great way to determine if the lid is closed: How much Internal Screen is visible? If none, the lid is close. Simple.

Once you have suggested it, it's kind of obvious, but how did you think of it? Were you using the %ScreenVisible% token for something else?

August

I like SCREEN for this -- less typing :wink:

But with both you can go a step further -- get either Width or Height and you can use the function directly in your If test:

image

1 Like

and, as a footnote, in the context of scripts we can define ourselves a builtInScreenOpen() function:

Expand disclosure triangle to view JS source of `builtInScreenOpen()`
// builtInScreenOpen :: IO () -> Bool
const builtInScreenOpen = () => {
    // True, on a laptop system,
    // only if the clamshell is open.
    const uw = ObjC.unwrap;

    return uw($.NSScreen.screens)
    .some(
        screen => $.CGDisplayIsBuiltin(
            uw(
                uw(screen.deviceDescription)
                .NSScreenNumber
            )
        )
    );
};

Is laptop clamshell closed ?.kmmacros (6.1 KB)


Expand disclosure triangle to view full JS source
ObjC.import("AppKit");
ObjC.import("CoreGraphics");

const main = () =>
    builtInScreenOpen();

// builtInScreenOpen :: IO () -> Bool
const builtInScreenOpen = () => {
    // True, on a laptop system,
    // only if the clamshell is open.
    const uw = ObjC.unwrap;

    return uw($.NSScreen.screens)
    .some(
        screen => $.CGDisplayIsBuiltin(
            uw(
                uw(screen.deviceDescription)
                .NSScreenNumber
            )
        )
    );
};

return main();

Thanks for the welcome! This was actually the first macro I attempted in KM. I was looking through the Wiki for something having to do with displays, and I found the various entries for "screen". I wasn't sure how to reference a calculation directly, which is why I used variables. Thanks to @Nige_S, I have simplified the macro:

Screenshot 2024-03-31 at 7.58.22 AM

So, not only does my MacBook go to sleep when the lid is closed in all circumstances, but I've also learned a few things today. A win all around! Thanks to you both!

1 Like

Perhaps worth noting that the =0 bits are only necessary because of how you've structured your macro -- although doing it as you have may better fit your mental model, making things much easier when you go back it in 6 months time. To explain...

Calculation conditions evaluate to FALSE when the result of the calculations is 0 or TRUE for any other number.

So SCREEN(Internal, Width) returns TRUE for anything but 0 and so actually tests if the lid is open; similarly, BATTERY() tests if the device is running on battery power.

In English, your action is saying "If the lid is closed and we're connected to power then put the computer to sleep". But you can also state that as "If the lid is open or we are running on battery then don't leave computer awake, else put it to sleep":

image

Or even "If none of 'the lid is open' or 'we are on power' are true then put the computer to sleep":

image

Just different ways of expressing the same decision-making. Theoretically these two methods are more efficient because they don't have the extra equality tests -- but what we're doing is so insignificant compared to our available compute power that it's far more important that you use the method that's obviously meaningful to you, and especially to the "future you" who'll have to make sense of it later!

1 Like