Skip to content

Commit 8282fad

Browse files
committed
added autocheck options in plugin settings
User can now automatically check for program status by using plugin settings.
1 parent 7c5bf75 commit 8282fad

File tree

3 files changed

+120
-74
lines changed

3 files changed

+120
-74
lines changed

src/entry.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44

5-
__version__ = "1.6"
5+
__version__ = "1.7.0"
66
PLUGIN_ID = "tp.plugin.process_monitor"
77
PLUGIN_NAME = "Process_Monitor"
88

@@ -26,13 +26,17 @@
2626

2727

2828
TP_PLUGIN_SETTINGS = {
29-
#'Color Names': {
30-
# 'name': "Color Names - Can be changed via plugin actions",
31-
# 'type': "text",
32-
# 'default': "Basic",
33-
# 'readOnly': True,
34-
# 'value': None # we can optionally use the settings struct to hold the current value
35-
#}
29+
'Auto Monitor Programs (comma seperated)': {
30+
'name': "The Programs to Monitor (comma separated)",
31+
'type': "text",
32+
'default': "",
33+
'value': None # we can optionally use the settings struct to hold the current value
34+
},
35+
'Auto Monitor Programs - Check every x seconds': {
36+
'name': "Check every x seconds",
37+
'type': "text",
38+
'default': "5"
39+
}
3640
}
3741

3842
TP_PLUGIN_CATEGORIES = {
@@ -58,7 +62,7 @@
5862
'prefix': TP_PLUGIN_CATEGORIES['main']['name'],
5963
'type': "communicate",
6064
'tryInline': True,
61-
'format': "Get Process Status $[1] - Every $[2] Seconds",
65+
'format': "Get Process Status $[1] - Every $[2] Seconds", # | Process must be visible?:$[3]",
6266
'data': {
6367
'text_color': {
6468
'id': PLUGIN_ID + ".act.process_name",
@@ -73,6 +77,14 @@
7377
'default': "0",
7478
'valueChoices': ["0","5", "10", "15", "30", "45", "60", "120", "240"]
7579
},
80+
# 'must be visible': {
81+
# 'id': PLUGIN_ID + ".act.process_name.visible",
82+
# 'type': "choice",
83+
# 'label': "check if window is visible, useful for browsers and other background processes",
84+
# 'default': "False",
85+
# 'valueChoices': ["True","False"]
86+
# },
87+
7688
}
7789
},
7890

src/entry.tp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": 6,
3-
"version": 16,
3+
"version": 170,
44
"name": "Process Monitor",
55
"id": "tp.plugin.process_monitor",
66
"configuration": {
@@ -89,5 +89,18 @@
8989
"connectors": []
9090
}
9191
],
92-
"settings": []
92+
"settings": [
93+
{
94+
"name": "The Programs to Monitor (comma separated)",
95+
"type": "text",
96+
"default": "",
97+
"readOnly": false
98+
},
99+
{
100+
"name": "Check every x seconds",
101+
"type": "text",
102+
"default": "5",
103+
"readOnly": false
104+
}
105+
]
93106
}

src/process_checker.py

Lines changed: 84 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
# Created by @Gitago for TouchPortal
33
# Jan, 2023
44

5-
## If users ever want multiple PIDS for the .exe we can make an action take it as an argument and then when user selects .exe it will show all the PIDs for that .exe and user can select which one they want to monitor
6-
7-
5+
## If users ever want multiple PIDS for the .exe we can make an action take it as an argument and then when user selects .exe it will show all the PIDs for that .exe and user can select which one they want to monito
86
## BUGS
97
## when process is checked with a 0 timer it is still counting as an active monitor although its only checking it once and stopping..
10-
118
## when checking for a process if its NOT loaded, then no states are made or mention that its not running.. but if its runing then you close it updates as expected
129
# when stopping a monitor it doesnt seem to clear the length of total?
1310

@@ -24,11 +21,11 @@
2421
import requests
2522
import base64
2623
import time
27-
24+
from datetime import datetime
2825
PLUGIN_NAME = "Process Monitor"
2926
PLUGIN_ID = "tp.plugin.process_monitor"
3027
GITHUB_URL = "process-monitor-touchportal-plugin"
31-
# DEFAULT_CONFIG_SAVE_PATH = path.join(path.dirname(path.realpath(__file__)), "color_config.json")
28+
3229

3330

3431
class ProcessMonitorData:
@@ -69,7 +66,7 @@ def the_task(self, process_name, the_process):
6966
TPC.TPClient.createState(stateId=PLUGIN_ID + f".state.{self.process_name}.process_info.{x}", description=f"PM | {self.process_name} - {x}", value="", parentGroup=str(self.process_name))
7067

7168
# Updating Status to "Closed" since the process appears to not be running
72-
TPC.TPClient.createState(stateId=PLUGIN_ID + f".state.{self.process_name}.process_info.status", description=f"PM | {self.process_name} - status", value="Closed", parentGroup=str(self.process_name))
69+
TPC.TPClient.createState(stateId=PLUGIN_ID + f".state.{self.process_name}.process_info.status", description=f"PM | {self.process_name} - status", value="closed", parentGroup=str(self.process_name))
7370

7471
if process_checked:
7572
PM.add_to_dict(self.process_name, the_process)
@@ -83,7 +80,7 @@ def the_task(self, process_name, the_process):
8380
TPC.TPClient.choiceUpdate(choiceId=PLUGIN_ID + ".act.process_name.stop", values=the_list)
8481

8582
PM.add_to_choiceList(the_list)
86-
# process_monitor_choiceList = the_list
83+
# process_monitor_choiceList = the_list
8784

8885
## update a state showing how many values are in the list minus the "ALL" value
8986
TPC.TPClient.stateUpdate(stateId=PLUGIN_ID + ".state.process_monitor.count", stateValue=str(len(the_list) - 1))
@@ -96,60 +93,36 @@ def the_task(self, process_name, the_process):
9693
process_checked[x] = round(process_checked[x], 2)
9794

9895
if x == 'create_time':
99-
from datetime import datetime
10096
create_time = process_checked.get('create_time', "None")
10197
if create_time is not None:
10298
create_time_datetime = datetime.fromtimestamp(create_time)
10399
process_checked[x] = create_time_datetime.strftime("%m/%d/%Y [%I:%M:%S %p]")
104-
105-
## use a thread to create sttes as fast as possible
100+
106101
TPC.TPClient.createState(stateId=PLUGIN_ID + f".state.{the_process.process_name}.process_info.{x}", description=f"PM | {the_process.process_name} - {x}", value=str(process_checked.get(x, "None")), parentGroup=str(the_process.process_name))
107102

108103

109-
def is_running(self):
110-
104+
def is_running(self):
111105
for process in psutil.process_iter():
112106
if process.name().lower() == self.process_name.lower():
113107
process_Info = process.as_dict(attrs=['pid', 'name', 'username', 'cpu_percent', 'memory_percent', 'cmdline', 'create_time', 'status'])
114108
# print("Before joiing the cmdline: ", process_checked)
115109
process_Info["cmdline"] = ' '.join(process_Info["cmdline"])
116-
117-
# print("After joiing the cmdline: ", process_checked)
118-
# memory_info = process.memory_info().rss
119-
# memory_mb = memory_info / 1048576
120-
# process_Info['memory_MB'] = memory_mb
121110
return process_Info
122111

123112
return False
124113

125114

126115
def check_continuously(self, interval, process_name, the_process):
127116
while self.should_continue:
128-
TPC.g_log.debug("Checking if " + self.process_name + " is running")
129-
117+
TPC.g_log.debug("Checking if " + self.process_name + " is running")
130118
self.the_task(process_name = process_name, the_process=the_process)
131-
time.sleep(interval)
132-
119+
time.sleep(interval)
133120
return False
134121

135122

136123
def stop(self):
137124
self.should_continue = False
138125

139-
140-
141-
## ### The TP Client
142-
## try:
143-
## TPClient = TP.Client(
144-
## pluginId = PLUGIN_ID,
145-
## sleepPeriod = 0.05,
146-
## autoClose = True,
147-
## checkPluginId = True,
148-
## maxWorkers = 4,
149-
## updateStatesOnBroadcast = False,
150-
## )
151-
## except Exception as e:
152-
## sys.exit(f"Could not create TP Client, exiting. Error was:\n{repr(e)}")
153126
class TPClientClass:
154127
def __init__(self, pluginId, sleepPeriod=0.05, autoClose=True, checkPluginId=True, maxWorkers=4, updateStatesOnBroadcast=False):
155128
self.pluginId = pluginId
@@ -175,11 +148,6 @@ def __init__(self, pluginId, sleepPeriod=0.05, autoClose=True, checkPluginId=Tru
175148

176149
TPC = TPClientClass(PLUGIN_ID)
177150

178-
# Crate the global logger
179-
#g_log = getLogger()
180-
181-
182-
183151
@TPC.TPClient.on(TP.TYPES.onNotificationOptionClicked)
184152
def check_noti(data):
185153
if data['optionId'] == PLUGIN_ID+ '.update.download':
@@ -188,15 +156,32 @@ def check_noti(data):
188156
webbrowser.open(url, new=0, autoraise=True)
189157

190158

191-
192159
@TPC.TPClient.on(TP.TYPES.onConnect)
193160
def onConnect(data):
194161
TPC.g_log.info(f"Connected to TP v{data.get('tpVersionString', '?')}, plugin v{data.get('pluginVersion', '?')}.")
195162
TPC.g_log.debug(f"Connection: {data}")
196163
if settings := data.get('settings'):
197164
handleSettings(settings, True)
198-
165+
166+
if settings:#('Auto Monitor Programs (comma seperated)') != "":
167+
auto_monitor_programs = settings[0]['The Programs to Monitor (comma separated)']
168+
169+
if auto_monitor_programs != "":
170+
auto_monitor_programs = auto_monitor_programs.split(",")
171+
every_X_seconds = 5 ## default to 5 seconds unless specified in settings
172+
if settings[1]['Check every x seconds'] != "":
173+
every_X_seconds = int(settings[1]['Check every x seconds'])
174+
175+
for x in auto_monitor_programs:
176+
TPC.g_log.info(f"Checking every 5 seconds for {x}")
177+
the_process = ProcessChecker(x)
178+
if x not in PM.process_monitor_dict.keys():
179+
th = threading.Thread(target=the_process.check_continuously, args=(every_X_seconds , x, the_process))
180+
th.start()
181+
182+
199183
plugin_update_check(data)
184+
200185
TPC.TPClient.stateUpdate(stateId=PLUGIN_ID + ".state.process_monitor.count", stateValue="0")
201186

202187

@@ -207,14 +192,31 @@ def onSettingUpdate(data):
207192
if (settings := data.get('values')):
208193
handleSettings(settings, False)
209194

195+
196+
if settings:#('Auto Monitor Programs (comma seperated)') != "":
197+
auto_monitor_programs = settings[0]['The Programs to Monitor (comma separated)']
198+
199+
if auto_monitor_programs != "":
200+
auto_monitor_programs = auto_monitor_programs.split(",")
201+
every_X_seconds = 5 ## default to 5 seconds unless specified in settings
202+
if settings[1]['Check every x seconds'] != "":
203+
every_X_seconds = int(settings[1]['Check every x seconds'])
204+
205+
for x in auto_monitor_programs:
206+
TPC.g_log.info(f"Checking every 5 seconds for {x}")
207+
the_process = ProcessChecker(x)
208+
if x not in PM.process_monitor_dict.keys():
209+
th = threading.Thread(target=the_process.check_continuously, args=(every_X_seconds , x, the_process))
210+
th.start()
211+
212+
213+
210214

211215

212216

213217
@TPC.TPClient.on(TP.TYPES.onAction)
214218
def onAction(data):
215219
TPC.g_log.debug(f"Action: {data}")
216-
217-
218220
if not (action_data := data.get('data')) or not (aid := data.get('actionId')):
219221
return
220222

@@ -232,11 +234,8 @@ def onAction(data):
232234
if data['data'][0]['value'] not in PM.process_monitor_dict.keys():
233235

234236
th = threading.Thread(target=the_process.check_continuously, args=(int(data['data'][1]['value']), data['data'][0]['value'], the_process))
235-
th.start()
236-
237-
238-
# process_checked = the_process.check_continuously(int(data['data'][1]['value']), data=data['data'][0]['value'], the_process=the_process)
239-
237+
th.start()
238+
240239

241240
if data['actionId'] == PLUGIN_ID +".act.stop_process.Monitor":
242241
the_process = data['data'][0]['value']
@@ -260,8 +259,6 @@ def onAction(data):
260259
TPC.g_log.error(f"Error stopping the process: {e}")
261260

262261

263-
264-
265262
def handleSettings(settings, on_connect=False):
266263
pass
267264

@@ -296,20 +293,12 @@ def plugin_update_check(data):
296293
TPC.g_log.error("[UPDATE CHECK] Something went wrong checking update", e)
297294

298295

299-
300-
301296
# Shutdown handler
302297
@TPC.TPClient.on(TP.TYPES.onShutdown)
303298
def onShutdown(data):
304299
TPC.g_log.info('Received shutdown event from TP Client.')
305300

306301

307-
308-
309-
310-
311-
312-
## The Main + Logging System
313302
def main():
314303
ret = 0 # sys.exit() value
315304

@@ -370,13 +359,45 @@ def main():
370359
ret = -1
371360
finally:
372361
TPC.TPClient.disconnect()
373-
374362
del TPC.TPClient
375-
376-
# TPC.g_log.info(f"{TP_PLUGIN_INFO['name']} stopped.")
377363
return ret
378364

379365

380366
if __name__ == "__main__":
381367
PM = ProcessMonitorData()
382368
sys.exit(main())
369+
370+
371+
372+
# import win32gui
373+
# import win32process
374+
# import psutil
375+
#
376+
# process_info = None
377+
# ### have to make a special 'check' in the plugin action to see if process is 'visible' rather than running..
378+
# ## this will help people determine if a browser source is actually open or not.. this needs to be optional as if we take out iswindowvisible then it says msedge is open even though its not..
379+
# def callback(hwnd, app_processes):
380+
# global process_info
381+
# if win32gui.IsWindowVisible(hwnd):
382+
# try:
383+
# pid = win32process.GetWindowThreadProcessId(hwnd)[1]
384+
# process = psutil.Process(pid)
385+
# if process.name().lower() in app_processes:
386+
# process_info = process.as_dict(attrs=['num_threads','pid', 'name', 'username', 'cpu_percent', 'memory_percent', 'cmdline', 'create_time', 'status', 'cwd', 'exe'])
387+
# app_processes[process.name().lower()] = True
388+
#
389+
# except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
390+
# pass
391+
#
392+
# # define the process names for different browsers
393+
# app_process = {'msedge.exe': False}
394+
#
395+
# # loop through all the windows and check if any belong to the browser process
396+
# win32gui.EnumWindows(callback, app_process)
397+
#
398+
# # check if any of the browser processes have an open window
399+
# for process_name, has_window in app_process.items():
400+
# if has_window:
401+
# print(f"Yes, a {process_name} window is open")
402+
# else:
403+
# print(f"No, a {process_name} window is not open")

0 commit comments

Comments
 (0)