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.
Thanks @fortred2 for the quick response . I will try and let you know how it went.
I tried, and it is working! So, I marked it as "Solution." Thanks @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.