Automatically turn on and off the grayscale (color filter) option

How can we automatically turn on and off the greyscale (color filter) option between certain times? E.g., I want to turn it on between 18:45 and 4:00, so it will automatically start at 18:45 and end at 4:00.

Hi @Sagar_Patel ,

You can programmatically turn on greyscale using this Python code:

from ctypes import cdll
lib = cdll.LoadLibrary("/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess")
lib.UAGrayscaleSetEnabled(True)

Try pasting this command in your terminal, it will toggle on and off greyscale:

python3 -c 'from ctypes import cdll; lib = cdll.LoadLibrary("/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess"); current_state = lib.UAGrayscaleIsEnabled(); lib.UAGrayscaleSetEnabled(not current_state); print("{} grayscale mode".format("Enabled" if not current_state else "Disabled"))'

Now, we can hook up this logic with two separate BTT Triggers of type "Repeating or Time Based Trigger" with the "Trigger When Matching Date/Time Conditions" setting selected. One will be set to run every day at 18:45 and the other at 4:00.

For both "Repeating or Time Based Trigger" Triggers, set the Action to "Execute Shell Script / Task" and the launch path to /usr/local/bin/python3. For the Trigger running at 18:45 everyday, set the script to:

from ctypes import cdll
lib = cdll.LoadLibrary("/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess")
lib.UAGrayscaleSetEnabled(True)

And for the Trigger running at 4:00, set the script to:

from ctypes import cdll
lib = cdll.LoadLibrary("/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess")
lib.UAGrayscaleSetEnabled(False)

For your convenience, I'm sharing with you the BTT JSON configuration for both of these Trigger & Actions. Simply copy the JSON below and paste it into your BTT GUI and you should be all setup.

[
  {
    "BTTActionCategory" : 0,
    "BTTLastUpdatedAt" : 1743067781.7204108,
    "BTTTriggerType" : 678,
    "BTTTriggerTypeDescriptionReadOnly" : "Repeating or Time Based Trigger",
    "BTTTriggerClass" : "BTTTriggerTypeOtherTriggers",
    "BTTUUID" : "4ECB6486-7140-41F5-AC14-06A3DC356F28",
    "BTTPredefinedActionType" : 366,
    "BTTPredefinedActionName" : "Empty Placeholder",
    "BTTAdditionalConfiguration" : "{\"BTTTimedWeekday\":\"\",\"BTTTimedRunDateExamples\":\"The current settings result in these upcoming trigger dates:\\n2025 M03 27, Thu 02:16:22 GMT-07:00\\n2025 M03 27, Thu 02:16:23 GMT-07:00\\n2025 M03 27, Thu 02:16:24 GMT-07:00\",\"BTTTimedSecond\":\"0\",\"BTTTimedDay\":\"\",\"BTTTimedHour\":\"18\",\"BTTTimedMinute\":\"45\",\"BTTTimedWhenToTrigger\":1,\"BTTTimedMonth\":\"\",\"BTTTimedYear\":\"\"}",
    "BTTEnabled" : 1,
    "BTTEnabled2" : 1,
    "BTTOrder" : 9,
    "BTTAdditionalActions" : [
      {
        "BTTActionCategory" : 0,
        "BTTLastUpdatedAt" : 1743067785.5508389,
        "BTTTriggerParentUUID" : "4ECB6486-7140-41F5-AC14-06A3DC356F28",
        "BTTIsPureAction" : true,
        "BTTTriggerClass" : "BTTTriggerTypeOtherTriggers",
        "BTTUUID" : "DC343F66-282C-405B-9E85-CA1DF3DCAA33",
        "BTTPredefinedActionType" : 206,
        "BTTPredefinedActionName" : "Execute Shell Script  or  Task",
        "BTTShellTaskActionScript" : "from ctypes import cdll\nlib = cdll.LoadLibrary(\"\/System\/Library\/PrivateFrameworks\/UniversalAccess.framework\/UniversalAccess\")\nlib.UAGrayscaleSetEnabled(True)",
        "BTTShellTaskActionConfig" : "\/usr\/local\/bin\/python3:::-c:::-:::",
        "BTTEnabled" : 1,
        "BTTEnabled2" : 1,
        "BTTOrder" : 752
      }
    ]
  }
]
[
  {
    "BTTActionCategory" : 0,
    "BTTLastUpdatedAt" : 1743067841.9191918,
    "BTTTriggerType" : 678,
    "BTTTriggerTypeDescriptionReadOnly" : "Repeating or Time Based Trigger",
    "BTTTriggerClass" : "BTTTriggerTypeOtherTriggers",
    "BTTUUID" : "7FB82460-2277-4F8D-9D4A-1693DF904BDE",
    "BTTPredefinedActionType" : 366,
    "BTTPredefinedActionName" : "Empty Placeholder",
    "BTTAdditionalConfiguration" : "{\"BTTTimedWeekday\":\"\",\"BTTTimedRunDateExamples\":\"The current settings result in these upcoming trigger dates:\\n2025 M03 27, Thu 02:16:22 GMT-07:00\\n2025 M03 27, Thu 02:16:23 GMT-07:00\\n2025 M03 27, Thu 02:16:24 GMT-07:00\",\"BTTTimedSecond\":\"0\",\"BTTTimedDay\":\"\",\"BTTTimedHour\":\"4\",\"BTTTimedMinute\":\"0\",\"BTTTimedWhenToTrigger\":1,\"BTTTimedMonth\":\"\",\"BTTTimedYear\":\"\"}",
    "BTTEnabled" : 1,
    "BTTEnabled2" : 1,
    "BTTOrder" : 10,
    "BTTAdditionalActions" : [
      {
        "BTTActionCategory" : 0,
        "BTTLastUpdatedAt" : 1743067793.393518,
        "BTTTriggerParentUUID" : "7FB82460-2277-4F8D-9D4A-1693DF904BDE",
        "BTTIsPureAction" : true,
        "BTTTriggerClass" : "BTTTriggerTypeOtherTriggers",
        "BTTUUID" : "8932354E-3B46-48AD-8C75-4E02DE7E3A87",
        "BTTPredefinedActionType" : 206,
        "BTTPredefinedActionName" : "Execute Shell Script  or  Task",
        "BTTShellTaskActionScript" : "from ctypes import cdll\nlib = cdll.LoadLibrary(\"\/System\/Library\/PrivateFrameworks\/UniversalAccess.framework\/UniversalAccess\")\nlib.UAGrayscaleSetEnabled(False)",
        "BTTShellTaskActionConfig" : "\/usr\/local\/bin\/python3:::-c:::-:::",
        "BTTEnabled" : 1,
        "BTTEnabled2" : 1,
        "BTTOrder" : 752
      }
    ]
  }
]

Hope this helps.

2 Likes

Thanks @fortred2 for the quick response :heart:. I will try and let you know how it went.

1 Like

I tried, and it is working! So, I marked it as "Solution." Thanks @fortred2 :heart:

1 Like

@fortred2

Update: I did some minor changes to the BTT trigger settings to trigger the script at 18:30 to enable the grayscale. At 18:30 it turns ON the grayscale (color filter). But, if we unlock the screen after 18:30 anytime, E.g., 18:45, the system reverts (resets, turns OFF) the grayscale. The same happens when we switch between different applications! E.g., Chrome to Android Studio!

My question is: Is it OK to run the script "Repeat every # seconds" instead of "Trigger when matching date/time conditions", and enable or disable the grayscale within the python script based on time? Does it drain the battery more? If yes, what will be the better solution?

Update: I used the below single python script to "toggle grayscale" based on the time of the day and used "Repeat every # seconds" in BTT:

from datetime import datetime
from ctypes import cdll

def is_within_time_range():
    """Check if the current time is between 18:30 and 04:00."""
    now = datetime.now()
    current_time = now.time()

    # Define the time range
    start_time = now.replace(hour=18, minute=30, second=0, microsecond=0).time()
    end_time = now.replace(hour=4, minute=0, second=0, microsecond=0).time()

    # Handle the range that spans midnight (18:30 to 04:00)
    if start_time <= current_time or current_time <= end_time:
        return True
    return False

def toggle_grayscale():
    """Enable or disable grayscale based on the time range."""
    lib = cdll.LoadLibrary("/System/Library/PrivateFrameworks/UniversalAccess.framework/UniversalAccess")
    
    if is_within_time_range():
        print("Enabling grayscale...")
        lib.UAGrayscaleSetEnabled(True)
    else:
        print("Disabling grayscale...")
        lib.UAGrayscaleSetEnabled(False)

if __name__ == "__main__":
    toggle_grayscale()

So, even though the system reverts the color filter (grayscale) every time I unlock the screen or switch between any apps, within 1 minute (max) I get my expected color filter (grayscale) settings (ON or OFF depending upon the time of the day).

I just used a different script, but as @fortred2 has provided the original idea (guidance, map), I am keeping the answer provided by @fortred2 as solution.

1 Like