11import ctypes
2- import time
32import datetime
43import subprocess
54import threading
5+
66from loguru import logger
77import pystray
88from PIL import Image , ImageDraw
3434DISABLE_IF_WORKSTATION_LOCKED = config .get ("disable_if_workstation_locked" , False )
3535
3636# --- Sleep control ---
37- running = True
3837working = True # whether we keep the PC awake
3938last_state = None # Track last applied state
39+ stop_event = threading .Event ()
40+ worker_thread = None # global
4041
4142def disable_sleep ():
4243 # Run the command
@@ -74,7 +75,7 @@ def enable_sleep():
7475def keep_awake (icon ):
7576 global last_state
7677 logger .info ("KeepAwake service started" )
77- while running :
78+ while not stop_event . is_set () :
7879 now = datetime .datetime .now ()
7980 weekday = now .weekday () # 0=Monday, 6=Sunday
8081
@@ -95,19 +96,26 @@ def keep_awake(icon):
9596 success = enable_sleep ()
9697 if success :
9798 last_state = desired_state
99+ update_status (icon )
100+
101+ # optical if its in working time and enabled
102+ if desired_state == "awake" and weekday < 5 and START_HOUR <= now .hour < END_HOUR and icon .icon != ICON_WORKTIME :
103+ icon .icon = ICON_WORKTIME
104+ elif desired_state == "normal" or not (weekday < 5 and START_HOUR <= now .hour < END_HOUR ):
105+ icon .icon = ICON_ACTIVE
98106 else :
99- logger .info ("no state change, sleep change call was unsuccessful" )
107+ logger .error ("no state change, sleep change call was unsuccessful" )
100108 else :
101- logger .info (f"No state change, still '{ last_state } '" )
109+ logger .info (f"No state change, still '{ last_state } ' with working: { working } and stop event not set. " )
102110
103- # optical if its in working time and enabled
104- if desired_state == "awake" and weekday < 5 and START_HOUR <= now .hour < END_HOUR and icon .icon != ICON_WORKTIME :
105- icon .icon = ICON_WORKTIME
106- elif desired_state == "normal" or not (weekday < 5 and START_HOUR <= now .hour < END_HOUR ):
107- icon .icon = ICON_ACTIVE
111+ # If icon object exists but tray is gone → restart it
112+ if icon and not icon .visible :
113+ logger .info ("Explorer restarted? → restoring tray icon" )
114+ restart_icon ()
108115
109116 logger .info (f"Sleep { CHECK_INTERVAL } s" )
110- time .sleep (CHECK_INTERVAL ) # check every 5 minutes
117+ # Wait, but allow early exit
118+ stop_event .wait (CHECK_INTERVAL )
111119
112120# --- Icon creation ---
113121def make_icon (color ):
@@ -145,41 +153,65 @@ def is_workstation_locked():
145153# --- Tray menu actions ---
146154def on_start (icon , item ):
147155 global working
156+ global last_state
148157 working = True
149158 now = datetime .datetime .now ()
150159 weekday = now .weekday () # 0=Monday, 6=Sunday
151160 if weekday < 5 and START_HOUR <= now .hour < END_HOUR and icon .icon != ICON_WORKTIME :
152161 icon .icon = ICON_WORKTIME
162+ disable_sleep ()
163+ last_state = "awake"
153164 else :
154165 icon .icon = ICON_ACTIVE
155-
166+ enable_sleep ()
167+ last_state = "normal"
168+ update_status (icon )
156169 logger .info ("KeepAwake activated" )
157- disable_sleep ()
158170
159171def on_force (icon , item ):
160172 global working
173+ global last_state
161174 if working is True :
162175 working = False
163176 disable_sleep ()
177+ last_state = "awake"
164178 icon .icon = ICON_FORCE
179+ update_status (icon )
165180 logger .info ("KeepAwake force activated" )
166181
167182def on_stop (icon , item ):
168183 global working
169184 working = False
170185 icon .icon = ICON_INACTIVE
171- logger .info ("KeepAwake deactivated" )
172186 enable_sleep ()
187+ update_status (icon )
188+ logger .info ("KeepAwake deactivated" )
173189
174190def on_exit (icon , item ):
175- global running
176- running = False
177191 icon .stop ()
192+ stop_event .set () # signal thread to stop immediately
193+ update_status (icon )
178194 logger .info ("Exiting KeepAwake" )
179195
180196
197+ def restart_icon ():
198+ global icon
199+ logger .info ("restart tray icon" )
200+ if icon :
201+ try :
202+ icon .stop ()
203+ except Exception :
204+ pass
205+ run_tray (start_worker = False )
206+
207+
208+ def update_status (icon ):
209+ icon .title = f"KeepAwake - { 'Running' if working else 'Stopped' } { '' if last_state is None else ' - ' + last_state .capitalize ()} "
210+
181211# --- Tray runner ---
182- def run_tray ():
212+ def run_tray (start_worker = True ):
213+ global icon , worker_thread
214+ logger .info ("run_tray()" )
183215 icon = pystray .Icon (
184216 "keep_awake" ,
185217 ICON_ACTIVE if working else ICON_INACTIVE ,
@@ -191,15 +223,31 @@ def run_tray():
191223 pystray .MenuItem ("Exit" , on_exit )
192224 )
193225 )
226+ update_status (icon ) # set initial status
194227
195- # Worker mit Icon starten
196- t = threading .Thread (target = keep_awake , args = (icon ,), daemon = True )
197- t .start ()
198-
228+ # Start worker only once
229+ if start_worker and worker_thread is None :
230+ logger .info ("starting worker thread" )
231+ worker_thread = threading .Thread (target = keep_awake , args = (icon ,), daemon = True )
232+ worker_thread .start ()
199233 icon .run ()
200234
201-
235+ # def explorer_monitor():
236+ # while True:
237+ # # Look for the "Shell_TrayWnd" window (taskbar)
238+ # hwnd = win32gui.FindWindow("Shell_TrayWnd", None)
239+ # if hwnd == 0: # Explorer not running
240+ # time.sleep(2)
241+ # continue
242+ #
243+ # # If icon object exists but tray is gone → restart it
244+ # if icon and not icon.visible:
245+ # logger.info("Explorer restarted → restoring tray icon")
246+ # restart_icon()
247+ #
248+ # time.sleep(5)
202249
203250if __name__ == "__main__" :
204- # Run tray icon
251+ #logger.info("starting explorer monitor")
205252 run_tray ()
253+ #threading.Thread(target=explorer_monitor, daemon=True).start()
0 commit comments