From 3281667094dda2beecf758cc38451eec20686a91 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Mon, 20 Aug 2018 11:38:30 +0200 Subject: [PATCH 1/9] external_script: show notification with full output on click, refresh only on button_refresh --- py3status/modules/external_script.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index 21a8f385a3..5ad720ee3a 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -10,6 +10,7 @@ The script should not have any parameters, but it could work. Configuration parameters: + button_refresh: button to refresh the module (default 2) cache_timeout: how often we refresh this module in seconds (default 15) format: see placeholders below (default '{output}') @@ -22,6 +23,7 @@ Format placeholders: {output} output of script given by "script_path" + {count_lines} number of lines in the output i3status.conf example: @@ -49,6 +51,7 @@ class Py3status: """ """ # available configuration parameters + button_refresh = 2 cache_timeout = 15 format = '{output}' localize = True @@ -64,8 +67,8 @@ def external_script(self): response = {} response['cached_until'] = self.py3.time_in(self.cache_timeout) try: - output = self.py3.command_output(self.script_path, shell=True, localized=self.localize) - output_lines = output.splitlines() + self.output = self.py3.command_output(self.script_path, shell=True, localized=self.localize) + output_lines = self.output.splitlines() if len(output_lines) > 1: output_color = output_lines[1] if re.search(r'^#[0-9a-fA-F]{6}$', output_color): @@ -97,9 +100,14 @@ def external_script(self): output = '' response['full_text'] = self.py3.safe_format( - self.format, {'output': output}) + self.format, {'output': output, 'count_lines': len(output_lines)}) return response + def on_click(self, event): + button = event["button"] + if button != self.button_refresh: + self.py3.notify_user(self.output) + self.py3.prevent_refresh() if __name__ == "__main__": """ From e007e2795fcdb2fa8c0030bba9872260f7479691 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Mon, 20 Aug 2018 14:17:03 +0200 Subject: [PATCH 2/9] Rename count_lines to line --- py3status/modules/external_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index 5ad720ee3a..073b1a7b1f 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -22,8 +22,8 @@ (default False) Format placeholders: + {line} number of lines in the output {output} output of script given by "script_path" - {count_lines} number of lines in the output i3status.conf example: @@ -100,7 +100,7 @@ def external_script(self): output = '' response['full_text'] = self.py3.safe_format( - self.format, {'output': output, 'count_lines': len(output_lines)}) + self.format, {'output': output, 'line': len(output_lines)}) return response def on_click(self, event): From 1dcd7cf3afd1efc9b5b85962ec14aa91192822c3 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Mon, 20 Aug 2018 14:52:42 +0200 Subject: [PATCH 3/9] Allow disabling notification via format_notifications --- py3status/modules/external_script.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index 073b1a7b1f..a5d74e3914 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -14,6 +14,7 @@ cache_timeout: how often we refresh this module in seconds (default 15) format: see placeholders below (default '{output}') + format_notification: see placeholders below (default '{output}') localize: should script output be localized (if available) (default True) script_path: script you want to show output of (compulsory) @@ -23,7 +24,10 @@ Format placeholders: {line} number of lines in the output - {output} output of script given by "script_path" + {output} first line of the output of script given by "script_path" + +Format notification placeholders: + {output} full output of script given by "script_path" i3status.conf example: @@ -54,6 +58,7 @@ class Py3status: button_refresh = 2 cache_timeout = 15 format = '{output}' + format_notification = '{output}' localize = True script_path = None strip_output = False @@ -106,7 +111,10 @@ def external_script(self): def on_click(self, event): button = event["button"] if button != self.button_refresh: - self.py3.notify_user(self.output) + if self.format_notification: + self.py3.notify_user(self.py3.safe_format( + self.format_notification, {'output': self.output}) + ) self.py3.prevent_refresh() if __name__ == "__main__": From 1698c702011751f9bf10579b7e81cccd28ec0a06 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Mon, 20 Aug 2018 15:05:53 +0200 Subject: [PATCH 4/9] Fight with Travis on issues that I didn't cause --- py3status/modules/external_script.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index a5d74e3914..95c7d01d8b 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -72,7 +72,8 @@ def external_script(self): response = {} response['cached_until'] = self.py3.time_in(self.cache_timeout) try: - self.output = self.py3.command_output(self.script_path, shell=True, localized=self.localize) + self.output = self.py3.command_output( + self.script_path, shell=True, localized=self.localize) output_lines = self.output.splitlines() if len(output_lines) > 1: output_color = output_lines[1] @@ -117,6 +118,7 @@ def on_click(self, event): ) self.py3.prevent_refresh() + if __name__ == "__main__": """ Run module in test mode. From af22971e7cbf4e7d4156da7b7d8a7e9301806c82 Mon Sep 17 00:00:00 2001 From: lasers Date: Mon, 20 Aug 2018 09:54:56 -0500 Subject: [PATCH 5/9] external_script: modifications --- py3status/modules/external_script.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index 95c7d01d8b..9a4a9f0128 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -10,11 +10,10 @@ The script should not have any parameters, but it could work. Configuration parameters: - button_refresh: button to refresh the module (default 2) cache_timeout: how often we refresh this module in seconds (default 15) format: see placeholders below (default '{output}') - format_notification: see placeholders below (default '{output}') + format_notification: see placeholders below (default None) localize: should script output be localized (if available) (default True) script_path: script you want to show output of (compulsory) @@ -27,6 +26,7 @@ {output} first line of the output of script given by "script_path" Format notification placeholders: + {line} number of lines in the output {output} full output of script given by "script_path" i3status.conf example: @@ -55,15 +55,15 @@ class Py3status: """ """ # available configuration parameters - button_refresh = 2 cache_timeout = 15 format = '{output}' - format_notification = '{output}' + format_notification = None localize = True script_path = None strip_output = False def post_config_hook(self): + self.button_refresh = 2 if not self.script_path: raise Exception(STRING_ERROR) @@ -72,9 +72,10 @@ def external_script(self): response = {} response['cached_until'] = self.py3.time_in(self.cache_timeout) try: - self.output = self.py3.command_output( - self.script_path, shell=True, localized=self.localize) - output_lines = self.output.splitlines() + output = self.py3.command_output( + self.script_path, shell=True, localized=self.localize + ) + output_lines = output.splitlines() if len(output_lines) > 1: output_color = output_lines[1] if re.search(r'^#[0-9a-fA-F]{6}$', output_color): @@ -105,8 +106,15 @@ def external_script(self): else: output = '' + self.script_data = { + 'line': len(output_lines), + 'output': output, + 'output_full': ' '.join(output.splitlines()) + } response['full_text'] = self.py3.safe_format( - self.format, {'output': output, 'line': len(output_lines)}) + self.format, self.script_data + ) + self.script_data['output_full'] = output return response def on_click(self, event): @@ -114,7 +122,7 @@ def on_click(self, event): if button != self.button_refresh: if self.format_notification: self.py3.notify_user(self.py3.safe_format( - self.format_notification, {'output': self.output}) + self.format_notification, self.script_data) ) self.py3.prevent_refresh() From 6d4dc12b838dcadc0534889c809b6659d1978480 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Sun, 26 Aug 2018 16:10:55 +0200 Subject: [PATCH 6/9] Fix documentation --- py3status/modules/external_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index 9a4a9f0128..fe21a938e3 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -27,7 +27,7 @@ Format notification placeholders: {line} number of lines in the output - {output} full output of script given by "script_path" + {output} first line of the output of script given by "script_path" i3status.conf example: From 3405dca62afb38e3226123ecc9bb1c298d057324 Mon Sep 17 00:00:00 2001 From: lasers Date: Sun, 9 Sep 2018 23:08:24 -0500 Subject: [PATCH 7/9] external_script: add notification state --- py3status/modules/external_script.py | 66 +++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index fe21a938e3..ee910ad9e1 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -16,6 +16,8 @@ format_notification: see placeholders below (default None) localize: should script output be localized (if available) (default True) + notification_state: specify notification state to use: current, click + (default 'current') script_path: script you want to show output of (compulsory) (default None) strip_output: shall we strip leading and trailing spaces from output @@ -24,18 +26,39 @@ Format placeholders: {line} number of lines in the output {output} first line of the output of script given by "script_path" + {output_full} full output of script given by "script_path" Format notification placeholders: {line} number of lines in the output {output} first line of the output of script given by "script_path" + {output_full} full output of script given by "script_path" -i3status.conf example: - +Examples: ``` +# add a script external_script { format = "my name is {output}" script_path = "/usr/bin/whoami" } + +# always display a notification, no output means no notification +external_script { + format_notification = '{output} + script_path = "~/my_python_script.py" +} + +# display a notification only if output have more than 20 lines +external_script { + format_notification = '\?if=line>20 {output}' + script_path = "~/my_python_script.py" +} + +# display a notification of last full output on click +external_script { + format_notification = '{output_full}' + notification_state = 'click' + script_path = "~/my_python_script.py" +} ``` @author frimdo ztracenastopa@centrum.cz @@ -49,6 +72,7 @@ import re STRING_ERROR = 'missing script_path' +STRING_STATE_ERROR = 'invalid notification state `{}`' class Py3status: @@ -59,23 +83,41 @@ class Py3status: format = '{output}' format_notification = None localize = True + notification_state = 'current' script_path = None strip_output = False def post_config_hook(self): - self.button_refresh = 2 if not self.script_path: raise Exception(STRING_ERROR) + elif self.notification_state not in ['click', 'current']: + raise Exception(STRING_STATE_ERROR.format(self.notification_state)) + + self.button_refresh = 2 + notification_boolean = bool(self.format_notification) + self.init = { + 'notify_click': ( + notification_boolean and self.notification_state == 'click' + ), + 'notify_current': ( + notification_boolean and self.notification_state == 'current' + ), + } + + def _notify_user(self): + self.py3.notify_user( + self.py3.safe_format(self.format_notification, self.script_data) + ) def external_script(self): output_lines = None response = {} response['cached_until'] = self.py3.time_in(self.cache_timeout) try: - output = self.py3.command_output( + output_full = self.py3.command_output( self.script_path, shell=True, localized=self.localize ) - output_lines = output.splitlines() + output_lines = output_full.splitlines() if len(output_lines) > 1: output_color = output_lines[1] if re.search(r'^#[0-9a-fA-F]{6}$', output_color): @@ -109,21 +151,23 @@ def external_script(self): self.script_data = { 'line': len(output_lines), 'output': output, - 'output_full': ' '.join(output.splitlines()) + 'output_full': ' '.join(output_full.splitlines()) } response['full_text'] = self.py3.safe_format( self.format, self.script_data ) - self.script_data['output_full'] = output + self.script_data['output_full'] = output_full + + if self.init['notify_current']: + self._notify_user() + return response def on_click(self, event): button = event["button"] if button != self.button_refresh: - if self.format_notification: - self.py3.notify_user(self.py3.safe_format( - self.format_notification, self.script_data) - ) + if self.init['notify_click']: + self._notify_user() self.py3.prevent_refresh() From 6db3b07692136603daa7efc60ad228326bd353aa Mon Sep 17 00:00:00 2001 From: lasers Date: Sun, 23 Sep 2018 18:26:14 -0500 Subject: [PATCH 8/9] external_script: over-engineered notifications --- py3status/modules/external_script.py | 108 +++++++++++++++++---------- 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index ee910ad9e1..53f1c0b006 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -13,11 +13,11 @@ cache_timeout: how often we refresh this module in seconds (default 15) format: see placeholders below (default '{output}') - format_notification: see placeholders below (default None) localize: should script output be localized (if available) (default True) - notification_state: specify notification state to use: current, click - (default 'current') + notifications: specify a nested dict to send a notification if matched + against the keys, see ``Notifications`` section for more information + (default {}) script_path: script you want to show output of (compulsory) (default None) strip_output: shall we strip leading and trailing spaces from output @@ -28,10 +28,23 @@ {output} first line of the output of script given by "script_path" {output_full} full output of script given by "script_path" -Format notification placeholders: - {line} number of lines in the output - {output} first line of the output of script given by "script_path" - {output_full} full output of script given by "script_path" +Notifications: + Specify a nested dictionary of notification states and options to use. + + Notification states: + 'changed': display a notification only if it is changed + 'click': display a notification of last output on click + 'current': display a current notification regardless + + Notification options: + 'title': notification title + 'msg': notification message + 'level': must be 'info', 'error' or 'warning'. + 'rate_limit': time period in seconds not to be repeated + 'icon': must be an icon path or icon name. + + You can add `format` placeholders in `msg` and `title`. + The `msg` and `title` options will also be formatted. Examples: ``` @@ -41,23 +54,28 @@ script_path = "/usr/bin/whoami" } -# always display a notification, no output means no notification +# display changed notifications, same output means no notification +external_script { + notifications = {'changed': {'msg': '{output}'}} + script_path = "~/my_script.py" +} + +# display current notifications, no output means no notification external_script { - format_notification = '{output} - script_path = "~/my_python_script.py" + notifications = {'current': {'msg': '{output}'}} + script_path = "~/my_script.py" } -# display a notification only if output have more than 20 lines +# display current notification only if output have more than 20 lines external_script { - format_notification = '\?if=line>20 {output}' - script_path = "~/my_python_script.py" + notifications = {'current': {'msg': '\?if=line>20 {output}'}} + script_path = "~/my_script.py" } # display a notification of last full output on click external_script { - format_notification = '{output_full}' - notification_state = 'click' - script_path = "~/my_python_script.py" + notifications = {'click': {'msg': '{output_full}'}} + script_path = "~/my_script.py" } ``` @@ -72,7 +90,6 @@ import re STRING_ERROR = 'missing script_path' -STRING_STATE_ERROR = 'invalid notification state `{}`' class Py3status: @@ -81,33 +98,46 @@ class Py3status: # available configuration parameters cache_timeout = 15 format = '{output}' - format_notification = None localize = True - notification_state = 'current' + notifications = {} script_path = None strip_output = False def post_config_hook(self): if not self.script_path: raise Exception(STRING_ERROR) - elif self.notification_state not in ['click', 'current']: - raise Exception(STRING_STATE_ERROR.format(self.notification_state)) self.button_refresh = 2 - notification_boolean = bool(self.format_notification) - self.init = { - 'notify_click': ( - notification_boolean and self.notification_state == 'click' - ), - 'notify_current': ( - notification_boolean and self.notification_state == 'current' - ), - } - - def _notify_user(self): - self.py3.notify_user( - self.py3.safe_format(self.format_notification, self.script_data) - ) + self.notification = {'normal': []} + if self.notifications: + for x in ['changed', 'click', 'current']: + self.notification[x] = self.notifications.get(x, False) + if self.notification[x]: + if x in ['changed', 'current']: + self.notification['normal'].append(x) + self.last_changed = self.py3.storage_get('changed') + + def _get_notification(self, state): + temporary = self.notification[state].copy() + for x in ['title', 'msg']: + if x in temporary: + temporary[x] = self.py3.get_composite_string( + self.py3.safe_format(temporary[x], self.script_data) + ) + return temporary + + def _notify_user(self, state=None): + if state is None: + if self.notification['changed']: + changed = self._get_notification('changed') + if changed != self.last_changed: + self.last_changed = changed + self.py3.storage_set('changed', changed) + self.py3.notify_user(**changed) + if self.notification['current']: + self.py3.notify_user(**self._get_notification('current')) + elif state == 'click': + self.py3.notify_user(**self._get_notification('click')) def external_script(self): output_lines = None @@ -156,9 +186,9 @@ def external_script(self): response['full_text'] = self.py3.safe_format( self.format, self.script_data ) - self.script_data['output_full'] = output_full - if self.init['notify_current']: + self.script_data['output_full'] = output_full + if self.notification['normal']: self._notify_user() return response @@ -166,8 +196,8 @@ def external_script(self): def on_click(self, event): button = event["button"] if button != self.button_refresh: - if self.init['notify_click']: - self._notify_user() + if self.notification['click']: + self._notify_user('click') self.py3.prevent_refresh() From d2ab7b6d63cc3782c6b47d071ff02bbe641fc351 Mon Sep 17 00:00:00 2001 From: lasers Date: Sat, 10 Nov 2018 14:06:59 -0600 Subject: [PATCH 9/9] external_script: over-engineered notifications (fix) --- py3status/modules/external_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py3status/modules/external_script.py b/py3status/modules/external_script.py index 53f1c0b006..3d2a5ba427 100644 --- a/py3status/modules/external_script.py +++ b/py3status/modules/external_script.py @@ -108,7 +108,7 @@ def post_config_hook(self): raise Exception(STRING_ERROR) self.button_refresh = 2 - self.notification = {'normal': []} + self.notification = {'normal': [], 'click': False} if self.notifications: for x in ['changed', 'click', 'current']: self.notification[x] = self.notifications.get(x, False)