home-assistant / iOS

:iphone: Home Assistant for Apple platforms
https://companion.home-assistant.io
Other
1.54k stars 297 forks source link

Expose switch to turn off Mac screen #1317

Closed andyshinn closed 3 years ago

andyshinn commented 3 years ago

Is your feature request related to a problem? Please describe.

Feature request. Related to #1271. I'd love to have a switch or other action to sleep the screen on my iMac. The use case is automations or switches where I turn off the lights in my office I would like to turn off the screen as well.

I currently do this using a command which runs pmset sleepdisplaynow over a SSH connection to my Mac. But this is unreliable (have to remember to load the SSH key to the agent on the HA side after reboot).

Describe the solution you'd like I think a native switch toggle exposed by the Mac app would be great.

Additional context Example code on how Apple does this is in the pmset utility under displaySleepNow(): https://opensource.apple.com/source/PowerManagement/PowerManagement-703.30.3/pmset/pmset.c.auto.html

static void displaySleepNow()
{
    io_registry_entry_t disp_wrangler = IO_OBJECT_NULL;
    kern_return_t kr;

    disp_wrangler = IORegistryEntryFromPath(
                            kIOMasterPortDefault,
                            kIOServicePlane ":/IOResources/IODisplayWrangler");
    if(disp_wrangler == IO_OBJECT_NULL)
        return ;

    kr = IORegistryEntrySetCFProperty( disp_wrangler, CFSTR("IORequestIdle"),kCFBooleanTrue);

    if(kr)
        fprintf(stderr, "pmset: Failed to set the display to sleep(err:0x%x)\n", kr);

    IOObjectRelease(disp_wrangler);

    return ;

}

Uses https://developer.apple.com/documentation/iokit/1514882-ioregistryentrysetcfproperty from IOKit. Some searching around I found a snippet of Swift where someone does this already:

https://github.com/th507/screen-resolution-switcher/blob/073b38faed8ce3eb55fd1471088366f4696610bb/scres.swift#L219-L224

Does not appear to require elevated privileges.

jasoncodes commented 3 years ago

I’ve been doing something similar myself with a custom app communicating with Home Assistant over MQTT.

I am calling CGDisplayIsAsleep(CGMainDisplayID() at startup to detect the initial state and subscribing to NSWorkspace.screensDidSleepNotification and NSWorkspace.screensDidWakeNotification to detect changes in state. I am shelling out to pmset displaysleepnow and caffeinate -u true to sleep and wake the display.

The IORequestIdle sample code works on my 2014 MacBook Pro but seems to do nothing on an M1 MacBook Air. I wonder if the private entitlements such as com.apple.private.SkyLight.displaycontrol are required on an M1 to control the display. Things have definitely changed in this area as pmset -g powerstate | grep ^IODisplayWrangler on my Intel Mac returns the current state but IODisplayWrangler is not present in the output of pmset -g powerstate on the M1 Mac.

zacwest commented 3 years ago

We need to build a mechanism to expose switches in the mobile_app integration to make progress on this.

mattgalloway commented 3 years ago

Howdy, I'm not involved with this project but I found this conversation when searching for "IORequestIdle not working on m1". I use this same technique to lock the screen in a small app and can confirm that this works on Intel based Macs but is seemingly ignored on M1 Macs (with no error codes returned). Thought you might like an independent confirmation.

Cheers,

M.

fire1ce commented 3 years ago

IODisplayWrangler is not present on m1. looking for alternate