#!/usr/bin/python3 import argparse import os import threading from time import sleep import notify2 import pystray from PIL import Image from playsound import playsound from pystray import Menu as menu, MenuItem as item NAME = 'Eye Strain Reminder' MESSAGE = 'Friendly reminder to observe the 20-20-20 rule' MESSAGE_MISSED = 'Missed notification(s), resuming countdown' TIMEOUT = 20 MILLISECOND = 1000 MINUTE = 60 DIR = os.path.dirname(os.path.abspath(__file__)) icon = mute = disable_notifs = invert_icons = notify_thread = start_sound = end_sound = None def init(): print("Initializing EyeStrainReminder...") notify2.init(NAME) def create_tray_icon(): global icon image = get_image() icon = pystray.Icon(NAME, image, NAME, generate_menu_items()) icon.run() def update_icon(): global icon image = get_image() icon.icon = image def get_image(): theme = [[DIR + "/files/eye_light.png", DIR + "/files/eye_dark.png"], [DIR + "/files/eye_dark.png", DIR + "/files/eye_light.png"]] return Image.open(theme[invert_icons][disable_notifs]) def generate_menu_items(): menu_items = [] menu_items.append(item('Mute' if not mute else 'Unmute', toggle_mute)) menu_items.append(item( 'Do Not Disturb' if not disable_notifs else 'Enable Notifications', toggle_notifications)) menu_items.append(menu.SEPARATOR) menu_items.append(item('Exit', exit)) return menu_items def toggle_mute(): global mute mute = not mute update_menu() def toggle_notifications(): global disable_notifs disable_notifs = not disable_notifs update_menu() update_icon() def exit(): global icon icon.stop() os._exit(0) def update_menu(): global icon icon.menu = generate_menu_items() icon.update_menu() def uninit(): notify2.uninit() def wait(): sleep(TIMEOUT*MINUTE) def send_notification(): if not disable_notifs: notify(MESSAGE) else: print("Notifications are disabled, waiting for an enable...") while disable_notifs: sleep(10) notify(MESSAGE_MISSED) def notify(message): print(message) notification = notify2.Notification( NAME, message, "notification-device-eject") notification.set_timeout(TIMEOUT * MILLISECOND) notification.set_urgency(notify2.URGENCY_LOW) notification.show() play_sound(start_sound) sleep(TIMEOUT) notification.close() play_sound(end_sound) def play_sound(sound): if not mute: playsound(sound) def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("-i", "--invert_icons", action="store_true", default=False, help="Default to the dark icon for light themes") parser.add_argument("-m", "--mute", action="store_true", default=False, help="Mute notification sounds by default") parser.add_argument("-n", "--disable_notifs", action="store_true", default=False, help="Disable notifications by default") parser.add_argument("-ss", "--start_sound", action="store", default=DIR + "/files/ptt-s.wav", help="Custom start notification sound") parser.add_argument("-es", "--end_sound", action="store", default=DIR + "/files/ptt-e.wav", help="Custom end notification sound") args = parser.parse_args() globals().update(args.__dict__) def notification_loop(): init() while 1: wait() send_notification() uninit() def main(): parse_args() notify_thread = threading.Thread( target=notification_loop, name="Notify Thread") notify_thread.start() create_tray_icon() notify_thread.join() if __name__ == '__main__': main()