Compare commits

...

1303 commits
legacy ... main

Author SHA1 Message Date
676bbebf4c
bluetooth2: states and styling 2024-04-17 23:05:16 +02:00
617a12f96d
Merge branch 'fix/bluetooth' 2024-04-17 22:53:03 +02:00
5053bb0f1b
themes/awesome-fonts: fix bluetooth icons 2024-04-17 22:47:45 +02:00
21060a10a0
bluetooth2: only show connection count in cases of excessive numbers of connections 2024-04-17 22:46:18 +02:00
a56b86100a
fix updated nerdfonts 2023-12-06 22:17:49 +01:00
fd4b940d58
nic.py: adapting wifi-menu path 2023-12-06 22:01:20 +01:00
2bbac991db
battery: updating nerd font icons 2023-11-10 21:03:43 +01:00
255794cd1c
vpn: updating nerd font icon 2023-11-10 20:51:31 +01:00
2e81eed830
pulsein: replacing muted nerd font icon with slash in the other direction, just to align it with bluetooth2 2023-11-10 20:47:35 +01:00
60bfb19378
pulseout: adding nerd font icons for low, mid, heigh, muted 2023-11-10 20:44:16 +01:00
d7d4603855
wlrotation: adding awesome-fonts icons 2023-11-10 20:03:44 +01:00
cbd0c58b4a
wlrotation: refactored the entire module and integrated external services 2023-11-10 19:44:33 +01:00
bbc26c263c
wlrotation: fix nerd font icon 2023-11-10 16:51:07 +01:00
53de1b524a
bluetooth2: using dbus api, shortening output, some refactoring 2023-11-10 16:50:16 +01:00
c3d4fce74c
nic: fix syntax 2023-11-10 16:48:05 +01:00
8cc7c9de9b
icons: fix nerd font battery/ac 2023-11-10 16:46:38 +01:00
3ed26c62a5
vpn: shorter default label 2023-11-08 22:59:56 +01:00
900a0710c5
nic: add click handler 2023-11-08 22:56:23 +01:00
2e18d71284
battery: better support for pen battery and some formatting 2023-11-08 22:46:58 +01:00
61123eb7a0
fix wlrotation: migrated to new api 2023-11-08 21:52:08 +01:00
27be30263f
Merge branch 'feature/wlrotation' into merge 2023-11-08 20:51:27 +01:00
559517f345
update golor theme and icons 2023-11-08 20:28:49 +01:00
b45ff330c9
wlrotation: init 2023-11-08 00:38:39 +01:00
tobi-wan-kenobi
bfafd93643 fix: remove power from tests installation as a quickfix 2023-10-26 09:19:46 +02:00
tobi-wan-kenobi
c14ed1166d fix: autotest - try to pin down pip versions 2023-10-26 09:11:39 +02:00
tobi-wan-kenobi
9f6c9cc7d2 doc: update readthedocs.yaml 2023-10-26 09:08:43 +02:00
tobi-wan-kenobi
025d3fcb51 fix(module/shell): synchonous invocation was broken
For some (unknown) reason, redrawing while in the update loop breaks the
update (this probably warrants a closer look).

As a quickfix to restore functionality, remove the unnecessary redraw
call and move it into the async codepath, where it is actually needed.

fixes #1001
2023-10-26 09:00:09 +02:00
tobi-wan-kenobi
05622f985a fix(module/shell): expand user directory in command spec
Make sure that ~/ is expanded to the user's home directory in the
command specified for the shell module.

fixes #1002
2023-10-26 08:59:14 +02:00
tobi-wan-kenobi
c4a3f488aa
Merge pull request #1000 from Sigggii/main
Add power-profile module
2023-10-04 06:25:02 +02:00
siggi
68de299763 Merge remote-tracking branch 'origin/main' 2023-10-03 22:36:23 +02:00
siggi
85760926d7 Add Power-Profile module 2023-10-03 22:28:28 +02:00
tobi-wan-kenobi
3bc3c75ff4
Merge pull request #997 from zetxx/patch-2
Update modules.rst
2023-10-02 14:38:16 +02:00
Elin Angelov
d30e5c694e
Update modules.rst 2023-10-02 12:31:15 +03:00
tobi-wan-kenobi
b217ac9c9e docs: update module documentation 2023-10-01 10:07:03 +02:00
tobi-wan-kenobi
fcda13cac0
Merge pull request #994 from zetxx/patch-1
feat: add `disabled` parameter
2023-09-20 14:28:53 +02:00
Elin Angelov
0c2123cfe4
fix: identation 2023-09-20 15:11:24 +03:00
Elin Angelov
9251217bb3
feat: add disabled parameter
it pauses dunst notification on startup
2023-09-20 14:59:24 +03:00
tobi-wan-kenobi
9170785ed5 fix(module-shell): fix syntax error 2023-09-15 15:21:38 +02:00
tobi-wan-kenobi
2e7e75a27c feat(shell): add timeout warning and faster update
see #990
2023-09-15 15:15:31 +02:00
tobi-wan-kenobi
d303b794f3 feat(docs): clarify line continuation
fixes #987
2023-09-15 15:11:23 +02:00
tobi-wan-kenobi
b42323013d fix(config): make config file keys case sensitive
Since the configparser library by default parses keys case insensitive
(all lowercase), certain mappings, especially in the pulseaudio modules,
could fail ("internal" pulseaudio device names are matched against
entries in the configuration).

fixes #992
2023-09-15 15:06:57 +02:00
tobi-wan-kenobi
8583b5123e
Merge pull request #991 from LokiLuciferase/feature/title-change-on-workspace-switch
poll window title also on workspace change (not only window events)
2023-09-13 21:25:16 +02:00
Lukas Lüftinger
b762132037 poll window title also on workspace change (not only window events) 2023-09-13 18:02:26 +02:00
tobi-wan-kenobi
fded39fa81 [modules/pulsectl] make device names case sensitive
A previous change accidentially changed the "pretty" device name mapping
to be required to be in lowercase (rather than the exact name of the
devices. Restore previous functionality.

fixes #989
2023-09-11 09:36:47 +02:00
tobi-wan-kenobi
9c5e30ac61
Merge pull request #988 from TheEdgeOfRage/pulsectl-filter
Add device filter support to pulsectl popup menu
2023-09-07 10:32:54 +02:00
Pavle Portic
9e6e656fa8
Add device filter support to pulsectl popup menu 2023-09-06 22:34:38 +02:00
tobi-wan-kenobi
b9e45ca994
Merge pull request #986 from TheEdgeOfRage/pulsectl-font-size
Allow adjusting the fontsize of the pulsectl default device popup
2023-09-06 16:00:46 +02:00
Pavle Portic
37b5646d65
Allow adjusting the font size of tk popups 2023-09-06 12:11:57 +02:00
tobi-wan-kenobi
d03e6307f5 [contrib/stock] change API to alphavantage.co
Changed the stock module to work again (with an API key), and make it
easier to change the data provider by the user.

fixes #971
2023-07-21 14:18:17 +02:00
tobi-wan-kenobi
1471d8824b
Merge pull request #980 from sazk07/main
Fix: pipewire.py module
2023-07-20 00:35:40 +02:00
Shahan Arshad
04a222f3c8
Fix: pipewire.py module
mousewheel up and down events were initially not working on my bumblebee status bar. After verifying on the command line, it seems that wpctl set-volume command requires sink ID first and percentage change as the second arg.

steps to verify, assuming wireplumber is installed and sink ID is 32

```sh
wpctl set-volume 32 50%
```
will set the volume to 50%

```sh
wpctl set-volume 50% 32
```
will result in `Object '52' not found
2023-07-20 02:16:24 +05:00
tobi-wan-kenobi
868fdbedd3
Merge pull request #979 from sazk07/patch-1
Add: missing mention of pipewire module on website
2023-07-19 20:05:50 +02:00
Shahan Arshad
e1f50f4782
Add: missing mention of pipewire module on website
I was reading the docs on the website(https://bumblebee-status.readthedocs.io/en/latest/modules.html#contrib) and there was no mention of the pipewire module which exists among the contrib modules. This edit will inform users about the pipewire module
2023-07-19 22:10:05 +05:00
tobi-wan-kenobi
f855d5c235 [modules/aur-update] hide if no packages
fixes #978
2023-07-17 12:58:48 +02:00
tobi-wan-kenobi
2546dbae2e [tests/amixer] fix tests 2023-07-17 12:55:06 +02:00
tobi-wan-kenobi
d27986a316 [requirements] remove json requirement - which is a builtin 2023-07-17 12:43:06 +02:00
tobi-wan-kenobi
4df5272164 [workflows] update python versions 2023-07-17 12:36:32 +02:00
tobi-wan-kenobi
839d79e68f
Merge pull request #977 from jebaum/main
allow setting sink ID in pipewire module
2023-07-17 01:41:01 +02:00
James Baumgarten
ee796f6589 allow setting sink ID in pipewire module 2023-07-16 15:06:31 -06:00
tobi-wan-kenobi
9b4944c53f
Merge pull request #976 from lasnikr/main
[modules/usage] A module for "ActivityWatch"
2023-07-10 22:03:02 +02:00
Lasnik
8178919e2c add description 2023-07-10 16:52:22 +02:00
Lasnik
305f9cf491 formatting guidelines 2023-07-10 15:51:00 +02:00
Lasnik
b866ab25b6 create module 2023-07-10 15:42:11 +02:00
tobi-wan-kenobi
775210db08
Merge pull request #974 from hugoeustaquio/main
Adding suport for multiple sound cards, not only devices.
2023-06-30 06:15:10 +02:00
Hugo Eustáquio
2ef6f84df3 Adding suport for multiple sound cards, not only devices. 2023-06-29 14:38:54 -03:00
tobi-wan-kenobi
a97e6f2f7d
Merge pull request #973 from SuperQ/cpu3
[modules/cpu3] Add new CPU module
2023-06-19 14:46:32 +02:00
SuperQ
0b4ff04be5
[modules/cpu3] Add new CPU module
Based on cpu2 module, but use `sensors -j` and some json path walking to
better parse CPU temp and fan speeds. The output of `sensors -u` can
contain many duplicates of the same sensor name, but from different
sensor device paths.

Signed-off-by: SuperQ <superq@gmail.com>
2023-06-19 14:08:16 +02:00
tobi-wan-kenobi
7cda35c1df
Merge pull request #972 from bbernhard/fix_pihole
use API token instead of password hash in pihole module
2023-05-28 18:08:38 +02:00
Bernhard B
b3007dd042 use API token instead of password hash in pihole module
* with newer versions of pi-hole, it is not possible
  anymore to use the password hash for the API authentication.
  Instead, one needs to use the dedicated API token for that.
  In order to stay backwards compatible, and not break existing
  bumblebee_status setups, the 'pwhash' parameter is still supported
  (in case someone runs an outdated pi-hole version).

* the new pi-hole API endpoints do not allow to access the summary
  endpoint without an API token. So, therefore '&auth=<api token>' was
  added.
2023-05-28 17:13:52 +02:00
tobi-wan-kenobi
8967eec44b [module/watson] Add formatting string
Make it possible to customize the watson message in the widget

see #963
2023-05-11 14:28:32 +02:00
tobi-wan-kenobi
14f19c897a [modules/pulsectl] add default device selection
re-enable functionality to add a popup that allows the user to select
the default source/sink.

fixes #965
2023-05-11 08:45:03 +02:00
tobi-wan-kenobi
e9696b2150 [readthedocs] explicitly specify build OS
fixes #970
2023-05-11 08:30:59 +02:00
tobi-wan-kenobi
c0526f2775
Merge pull request #969 from Duarte-Figueiredo/fix-build
Fixed typo in 'today' that is currently breaking the tests
2023-05-06 12:45:19 +02:00
Duarte Figueiredo
6b4898017f Fixed typo in 'today' that is currently breaking the tests 2023-05-06 11:20:45 +01:00
tobi-wan-kenobi
bdfc4fdab4
Merge pull request #968 from Duarte-Figueiredo/main
Updated gitlab module to have state of warning when there is at least 1 notification, just like the github module
2023-05-06 12:01:24 +02:00
Duarte Figueiredo
a6de61b751 Updated gitlab module to have state of warning when there is at least 1 notification, just like the github module 2023-05-06 10:56:17 +01:00
tobi-wan-kenobi
1dd39a4e43
Merge pull request #967 from Duarte-Figueiredo/main
[modules/todoist] - New module that connects to https://api.todoist.com
2023-04-19 14:20:59 +02:00
Duarte Figueiredo
592d08c082 removed Final import because of python3.7 backwards compatibility 2023-04-19 12:01:07 +01:00
Duarte Figueiredo
cad45ecd2c [modules/todoist] - New module that connects to https://api.todoist.com and displays number of tasks due 2023-04-19 11:50:25 +01:00
tobi-wan-kenobi
79081ebb4f
Merge pull request #966 from Duarte-Figueiredo/main
[modules/wakatime] - New module that connects to https://wakatime.com api
2023-04-16 15:24:50 +02:00
Duarte Figueiredo
1b0478edd4 changed icon from normal w to font-awesome clock 2023-04-16 11:38:13 +01:00
Duarte Figueiredo
2b4e2b2c82 rename mock_summaries_api_response test function 2023-04-16 11:29:31 +01:00
Duarte Figueiredo
42e041ce03 [modules/wakatime] - New module that connects to https://wakatime.com and displays coding duration stats 2023-04-16 11:24:51 +01:00
tobi-wan-kenobi
e58afff48a
Merge pull request #964 from dmturner/weather
Change OpenWeatherMap request url from HTTP to HTTPS
2023-04-13 14:26:12 +02:00
dmturner
f34e02d824 Change OpenWeatherMap request url from HTTP to HTTPS 2023-04-13 12:49:39 +01:00
tobi-wan-kenobi
2e1289f778 [core] fix importlib.util error
add explicit import of importlib.util

fixes #962
2023-04-11 12:42:55 +02:00
tobi-wan-kenobi
b750d96a72
Merge pull request #959 from chedge/pipx_compatibility
Added path for themes directory when installed via pipx
2023-03-26 01:30:32 +01:00
C H
61e38c6094 Added path for themes directory when installed via pipx 2023-03-25 15:27:42 -07:00
tobi-wan-kenobi
ad8b1802f5
Merge pull request #957 from LokiLuciferase/fix/playerctl-calls
remove unnecessary `playerctl` subprocess call to determine whether widget should be hidden
2023-03-15 20:03:14 +01:00
Lukas Lüftinger
99bd2a81b6 remove unnecessary playerctl calls to determine whether widgets should be hidden 2023-03-15 19:01:48 +01:00
tobi-wan-kenobi
93f3da1e08
Merge pull request #951 from beckcl/gitlab-module
Add GitLab module
2023-02-19 08:04:37 +01:00
Clemens Beck
7161ef211c [modules/gitlab] add module 2023-02-19 03:43:14 +01:00
tobi-wan-kenobi
f77f5552ae
Merge pull request #950 from jebaum/main
fix bug in pipewire module
2023-02-11 08:22:02 +01:00
James Baumgarten
be332005fa fix bug in pipewire module 2023-02-10 08:52:59 -07:00
tobi-wan-kenobi
cc883d1723
Merge pull request #949 from jebaum/main
add pipewire module
2023-02-04 07:58:45 +01:00
James Baumgarten
30362cb124 add pipewire module 2023-02-03 21:23:34 -07:00
tobi-wan-kenobi
098f03ac52
Merge pull request #947 from arivarton/gcalendar_fixes
Added a max_chars parameter to be able to control the widget width.
2023-01-29 17:04:24 +01:00
arivarton
ae29c1b79f Divided date/time and summary into two widgets and made the summary
widget scrollable.
2023-01-29 13:05:13 +01:00
arivarton
b327162f3b Added a max_chars parameter to be able to control the widget width.
Also moved the try block a bit further up to catch network errors.
2023-01-04 21:34:21 +01:00
tobi-wan-kenobi
8eb2545eed
Merge pull request #943 from pvutov/main
Documentation: Fix the default format string for nvidiagpu
2022-11-30 20:07:58 +01:00
pvutov
f0ce6a1f7f
Documentation: Fix the default format string for nvidiagpu 2022-11-30 21:04:33 +02:00
tobi-wan-kenobi
a6f2e6fc5e [modules/mpd] make mpd port configurable
fixes #941
2022-11-27 17:41:48 +01:00
tobi-wan-kenobi
87a2890b48 [modules/pulsectl] fix case when no devices are available
no devices lead to an exception that completely stopped bumblebee-status
from processing data.

handle this case more gracefully by defaulting to a volume of 0%. if
this proves to be an issue, we can still add error indicators later.

see #940
2022-11-27 12:03:35 +01:00
tobi-wan-kenobi
6a93238bda [core] log exceptions
to enable error investigation, log exceptions.

see #940
2022-11-27 09:46:55 +01:00
tobi-wan-kenobi
79ce2167b0 [autotest] update codeclimate action 2022-11-26 12:09:04 +01:00
tobi-wan-kenobi
1fef60b32c [tests] fix location tests 2022-11-26 12:05:42 +01:00
tobi-wan-kenobi
0bc2c6b8e1 [tests] remove unsupported python version 2022-11-26 10:17:09 +01:00
tobi-wan-kenobi
07e2364f78 [main] fix i3 protocol buf on error messages ("could not parse JSON")
Errors during startup currently cause bumblebee-status to mistakenly
output the first line of output (the "version" line) of the i3 protocol
twice, causing an error message that says "could not parse JSON")

see #940
2022-11-26 10:11:51 +01:00
tobi-wan-kenobi
5412591a0e
Merge pull request #934 from tfwiii/main
publicip - Bug Fix
2022-10-12 09:03:14 +02:00
tfwiii
697c3310a0 publicip - Bug Fix - IP address changes wer being missed if an interface was present but did not have an IPv4 address associated with it. Added exception handling to mitigate this. 2022-10-12 13:52:55 +07:00
tobi-wan-kenobi
1682a47554
Merge pull request #933 from tfwiii/main
publicip module - Fixed bug and minor improvements in output
2022-10-08 06:08:41 +02:00
tfwiii
cace02909e Bug fix improvements to publicip and util.location
Fixed publicip bug arising from last PR review
Simplified ip change detection code
Added pause after location.reset() call to allow completion before query
util.location - change order of information providers as default was not returning geo coords
2022-10-08 10:42:12 +07:00
tfwiii
605b749e22 Removed debugging prints 2022-10-06 14:21:43 +07:00
tfwiii
61fe7f6d3e Handled fail where core.location does not provide values for latitude and longitude. Added handling for coordinates N, S, E, W. 2022-10-06 13:49:37 +07:00
tobi-wan-kenobi
e70402e92c
Merge pull request #932 from ramonsaraiva/add-moonlight-theme
Add moonlight theme (powerline)
2022-09-27 17:05:00 +02:00
Ramon Saraiva
88f24100ff [themes] add moonlight theme (powerline) 2022-09-27 11:10:33 -03:00
tobi-wan-kenobi
a7979e7d66
Merge pull request #930 from benthetechguy/man-fix
Don't install manpages to /usr/usr
2022-09-21 06:32:43 +02:00
Ben Westover
7ae95ad6b6
Don't install manpages to /usr/usr
`data_files` shouldn't have `usr/` in it; this causes the manpages to be installed to `/usr/usr/share/man/man1` instead of `/usr/share/man/man1`.
2022-09-20 23:15:38 -04:00
tobi-wan-kenobi
ccf2fb3fd0
Merge pull request #929 from alonsomoya/docs/network_trafic_dependency
contrib/network_traffic dependency in docs
2022-09-20 15:10:12 +02:00
Jose Javier ALONSO MOYA
7ec3adfa47 contrib/network_traffic dependency in docs 2022-09-20 15:00:14 +02:00
tobi-wan-kenobi
1c19250fe5 [core/output] fix broken output 2022-09-18 16:50:43 +02:00
tobi-wan-kenobi
38d3a6d4c4 [doc] rearrange badges 2022-09-18 09:04:39 +02:00
tobi-wan-kenobi
0151d20451 [doc] update badges 2022-09-18 09:04:08 +02:00
tobi-wan-kenobi
acb387a685 Merge branch '921-scrolling-status-bar' 2022-09-17 17:04:12 +02:00
tobi-wan-kenobi
e5f36053af [workflow] add experimental AUR release workflow
OK, so I don't know how to test this, but *theoretically*, this should
automatically push releases to AUR.
2022-09-17 16:12:29 +02:00
tobi-wan-kenobi
d94ee9416a [doc] various updates 2022-09-17 16:07:05 +02:00
tobi-wan-kenobi
0807bfb5c3
Merge pull request #927 from kellya/main
Correct battery modules handling of critical/warning states
2022-09-17 02:05:52 +02:00
Alex Kelly
c637392bd0 Correct battery modules handling of critical/warning states
The logic for the critical/warning handling on the battery modules was
applied BEFORE the discharging-<pct> logic.  This made it  possible for
those discharging states to get applied over a critical/warning and
allow a theme to override the critical/warning colors with a state of
"discharging-10", for example.

This change moves that logic after the discharging state, so that it
will always "win" if critical or warning states are set.

This also adds the "discharging" battery state to the critical/warning
check so the state will apply if the battery is not on AC power, but
would return normal otherwise.  Meaning, if the battery is "critical"
from a percentage check, but is plugged into power, the critical state
is removed.
2022-09-16 15:36:06 -04:00
tobi-wan-kenobi
4337575557
Merge pull request #926 from kellya/main
[modules/taskwarrior] add state handling
2022-09-15 17:57:36 +02:00
Alex Kelly
a5e0b01e3b [modules/taskwarrior] add state handling
Sets one of two states:
  "stopped" - Default, no running task
  "active"  - When an active task is running.
2022-09-15 11:48:43 -04:00
tobi-wan-kenobi
97a022e452 [modules/pulsectl] add friendly name support
thanks to @anopheles for figuring out this is still missing
2022-09-14 21:12:01 +02:00
tobi-wan-kenobi
7a1022de46 [docs] add logo attribution 2022-09-13 19:02:49 +02:00
tobi-wan-kenobi
01cf02c560 [docs] transparent logo 2022-09-13 19:02:12 +02:00
tobi-wan-kenobi
71d65fafc6 [doc] attempt to use relative paths 2022-09-13 18:56:39 +02:00
tobi-wan-kenobi
a6388aea49
Merge pull request #924 from kellya/main
added logo and favicon to the conf
2022-09-13 18:36:15 +02:00
Alex Kelly
46b379815b added logo and favicon to the conf 2022-09-13 11:58:03 -04:00
tobi-wan-kenobi
7e6ae7c7be [themes] add plain zengarden theme 2022-09-13 12:59:59 +02:00
tobi-wan-kenobi
5ddc0d84a3 [docs] need to figure out how to include into readthedocs 2022-09-13 09:26:08 +02:00
tobi-wan-kenobi
245cacac42 [doc] add logo to readthedocs 2022-09-13 09:19:23 +02:00
tobi-wan-kenobi
d158fbccba [doc] reduce logo size a bit 2022-09-13 09:18:04 +02:00
tobi-wan-kenobi
aa86ac931f [doc] another attempt 2022-09-13 09:17:27 +02:00
tobi-wan-kenobi
700f977c87 [doc] play with formatting 2022-09-13 09:13:52 +02:00
tobi-wan-kenobi
84a3ebf47c [doc] test 2022-09-13 09:13:04 +02:00
tobi-wan-kenobi
9fe4e6c347 [doc] minor reformatting 2022-09-13 09:12:04 +02:00
tobi-wan-kenobi
a07f40d051 [doc] logo as img tag 2022-09-13 09:11:26 +02:00
tobi-wan-kenobi
578f806504 [doc] image not working? 2022-09-13 09:09:45 +02:00
tobi-wan-kenobi
5b864e4924 [doc] add logo - thanks to kellya! 2022-09-13 09:07:49 +02:00
tobi-wan-kenobi
96c9989ad5
Merge pull request #923 from kellya/main
Added task detail display for taskwarrior
2022-09-12 19:50:40 +02:00
Alex Kelly
54d5e83909 [modules/taskwarrior] Update docstring with show.active stuff 2022-09-12 13:43:10 -04:00
Alex Kelly
e76a6e0ba3 [doc] update document to reflec the show_active param for taskwarrior 2022-09-12 13:36:05 -04:00
Alex Kelly
754707379a Add active-task display and scrolling
This adds an option allowing you to specify
"taskwarrior.show_active=true" in your bar configuration and will
display the current, active task id and description on the status bar, but will show the
number of pending tasks if one isn't active.

This also adds the scrolling decorator, since task descriptions can be
quite long.
2022-09-12 13:29:19 -04:00
tobi-wan-kenobi
c40f59f7be [modules/scroll] edge case error 2022-09-11 16:00:35 +02:00
tobi-wan-kenobi
3f97ea6a39 [doc] add scroll menu
see #921
2022-09-11 13:16:06 +02:00
tobi-wan-kenobi
21cbbe685d [modules/scroll] add preliminary version of scrolling module
add a scrolling module that can be used to scroll the whole bar to an
arbitrary number of widgets.

its parameter is "width", which determines the number of widgets to
display.

see #921
2022-09-11 13:12:51 +02:00
tobi-wan-kenobi
910b9a8963 [doc] module doc update 2022-09-10 09:30:43 +02:00
tobi-wan-kenobi
cd46b9c6a6
Merge pull request #920 from tobi-wan-kenobi/917-event-based-pulseaudio
917 event based pulseaudio
2022-09-10 09:17:43 +02:00
tobi-wan-kenobi
2287dcab48 [docs] add note for pulsectl being preferred over pulseaudio 2022-09-10 09:13:55 +02:00
tobi-wan-kenobi
a97f46c087 [modules/pulsectl] add documentation 2022-09-10 09:12:03 +02:00
tobi-wan-kenobi
eb11c279f6 [modules/pulsectl] add device name mapping and display 2022-09-10 09:09:16 +02:00
tobi-wan-kenobi
025b1ec2f2 [modules/pulsectl] add optional bar representation 2022-09-10 09:04:52 +02:00
tobi-wan-kenobi
20bc4b3fa6 [modules/pulsectl] add parameter to set an upper limit 2022-09-10 09:02:13 +02:00
tobi-wan-kenobi
ca6bf2e189 [modules/pulsectl] add option to automatically start pulseaudio 2022-09-10 08:57:00 +02:00
tobi-wan-kenobi
003a6efc8e [modules/pulsectl] figure out default devices
make sure the modules always refer to the default devices
2022-09-09 21:40:03 +02:00
tobi-wan-kenobi
a1d94d4355 [modules/pulsectl] add mouse actions
add toggle mute on click and volume up/down on scroll
2022-09-09 21:34:32 +02:00
tobi-wan-kenobi
cc910f1198 [modules/pulsectl] add preliminary version of event-based pulseaudio
add a new module based on pulsectl, with pulsein for microphone and
pulseout for speakers.

should eventually become a drop-in replacement for pasink and pasource.

see #917
2022-09-09 21:21:09 +02:00
tobi-wan-kenobi
f4bd0fba0b [core] fix concurrency issues
* initialize first line of output earlier (before modules are
  initialized, so that module/thread output cannot interfere)
* make sure that update and draw are protected against concurrent access
2022-09-09 20:58:59 +02:00
tobi-wan-kenobi
28601cf2b7 Revert "Merge branch '917-event-based-pulseaudio'"
This reverts commit 72a888748e, reversing
changes made to d57ef9364a.

This merge causes really high CPU load if using both pasink and
pasource, because those two modules trigger each other, and there's not
a terrible lot I can do about that, unfortunately.
2022-09-09 08:39:05 +02:00
tobi-wan-kenobi
72a888748e Merge branch '917-event-based-pulseaudio'
fixes #917
2022-09-09 08:25:23 +02:00
tobi-wan-kenobi
d57ef9364a
Merge pull request #919 from kellya/main
[doc] fix missing bullet on scrolling.bounce
2022-09-08 18:06:29 +02:00
Alex Kelly
87764dccf0 [doc] fix missing bullet on scrolling.bounce 2022-09-08 11:06:06 -04:00
tobi-wan-kenobi
3c37b666f5
Merge pull request #918 from FraSharp/main
[modules/pamixer] use -t flag to toggle mute instead of setting volume level to 0
2022-09-08 12:55:36 +02:00
FraSharp
8d0f8a4177 [modules/pamixer] use -t flag to toggle mute instead of setting volume level to 0
Signed-off-by: FraSharp <s23265@iisve.it>
2022-09-08 12:11:27 +02:00
tobi-wan-kenobi
ee9885a601 [core] fix concurrency issues
* initialize first line of output earlier (before modules are
  initialized, so that module/thread output cannot interfere)
* make sure that update and draw are protected against concurrent access
2022-09-04 16:22:29 +02:00
tobi-wan-kenobi
ee81f6198e [modules/pulseaudio] rate-limit pulseaudio events
attempting a different tack: Reduce the amount of draws that are being
emitted by the pulseaudio module to max. 2/s. That hopefully increases
reactivity and at the same time keeps flickering to a minimum.

see #917
2022-09-04 09:55:33 +02:00
tobi-wan-kenobi
40041d6080 Revert "[core/output] rate-limit output (see #917)"
(did not fix the issue)

This reverts commit b90346424b.
2022-09-04 09:41:20 +02:00
tobi-wan-kenobi
b90346424b [core/output] rate-limit output (see #917)
according to research (Jakob Nielsen '93), roughly 0.1s is what is
required for the user to feel "instantaneous".

based on this, rate-limit updates to only once per ~0.03s (0.1 felt
really laggy for me, so let's be conservative)
2022-09-01 21:15:14 +02:00
tobi-wan-kenobi
978519e130 [modules/pulseaudio] redraw only
make sure that we only ever redraw see #917
2022-09-01 19:00:06 +02:00
tobi-wan-kenobi
1983408e58 [util] fix location timeouts 2022-08-31 19:12:05 +02:00
tobi-wan-kenobi
0f74b690ca [modules/publicip] add nicer logging 2022-08-31 19:09:04 +02:00
tobi-wan-kenobi
05f0e08493
Merge pull request #916 from tfwiii/main
More robust identification of changes to public IPs
2022-08-31 19:08:34 +02:00
tobi-wan-kenobi
8d2cdebbaf [modules/pulseaudio] fix pactl subscribe thread
calling "draw" too early causes status line to be messed up.

see #917
2022-08-31 19:05:08 +02:00
tobi-wan-kenobi
ae04cc9897 [module/pulseaudio] somewhat experimental immediate update
try to immediately update pulseaudio, if pactl subscribe exists & allows
us to monitor update.

see #913
2022-08-31 07:51:36 +02:00
tobi-wan-kenobi
b1fd18b9af Revert "[module/pulseaudio] somewhat experimental immediate update"
This reverts commit d5d0d6a56c.
2022-08-30 21:50:56 +02:00
tobi-wan-kenobi
d5d0d6a56c [module/pulseaudio] somewhat experimental immediate update
try to immediately update pulseaudio, if pactl subscribe exists & allows
us to monitor update.

see #913
2022-08-30 21:38:48 +02:00
tfwiii
7ee9645437 Added secondary check for potential changes in public IP and a small bug fix arising from reliance on util.location
Added a second check for indications of possible change of public IP address since netifaces does not properly handle all Linux routing tables (ref: http://linux-ip.net/html/routing-tables.html) which can lead to changes being missed if only looking at defauilt route. Consequently an additional check for changes to the network interface names/numbers was added to further help in identifying potentially notable changes. Note that netifaces is no longer being maintained so the underlying issue with its handling of gateways is unlikely to be resolved (https://github.com/al45tair/netifaces). An alternative would be to use direct calls to the OS like 'ip route list table all' (note the difference to 'ip route list all') but this might lead to unpredicatble results between distributions/flavours so probably best to stick with a library for now. all') but this might lead to unpredicatble results between distributions/flavours so probably best to stick with a library for now.

Introduced time.sleep(2) following calls to util.location.reset() since it can take util.location a while to update following a call to .reset() which can cause calls to .location_info() to return unpredicatable/unuseful results.
2022-08-30 02:01:25 +07:00
tobi-wan-kenobi
82e55ec517 [modules/pulseaudio] remove "warning" if "too loud"
Falls in the "meant well, but doesn't really make sense" category: When
the volume exceeds 100%, the widget was shown in "critical" state. Some
headsets, audio cards, etc. do require a high volume setting, however.
And anyhow, it's really up to the user.

fixes #913
2022-08-26 21:14:28 +02:00
tobi-wan-kenobi
fed7a067ba
Merge pull request #911 from diesphink/main
Fix on gcalendar module
2022-08-11 17:37:01 +02:00
Diego Pereyra
2b3b9c0ca0 Undo serialization using picke, added comment on minimum version for google-api-python-client 2022-08-11 12:26:42 -03:00
tobi-wan-kenobi
bb36f98aaa [doc] add redshift.adjust parameter 2022-08-11 16:54:15 +02:00
Diego Pereyra
80efa64614 Support for locale on gcalendar 2022-08-10 12:02:35 -03:00
tobi-wan-kenobi
84dfd85396
Merge pull request #912 from diesphink/regolith-rofication
Added support for regolith fork of rofication
2022-08-09 06:16:39 +02:00
Diego Pereyra
f9cfede0d0 Added support for regolith fork of rofication (differs on the comm protocol: \n after command, receive values on csv) 2022-08-08 23:13:01 -03:00
Diego Pereyra
55f3085c90 Added support for regolith fork of rofication (differs on the comm protocol: \n after command, receive values on csv) 2022-08-08 23:11:05 -03:00
Diego Pereyra
124f13075d Fix for multiple calendars 2022-08-08 21:59:39 -03:00
Diego Pereyra
2ab14d9cd3 Fix save token (using pickle) 2022-08-08 21:59:27 -03:00
tobi-wan-kenobi
470f05150d [module/redshift] allow adjusting the color temperature
add a boolean flag ("adjust") to redshift that allows the user to have
bumblebee-status actually perform the color adjustment (by invoking
redshift in "one-shot" node).

Note that this only updates the color value each time the redshift
module is updated (every 10s, by default), and likely will collide with
any running redshift process.

fixes #908
2022-08-07 10:32:28 +02:00
tobi-wan-kenobi
6af47dc506 [doc] update module documentation
fixes #910
2022-08-06 10:55:04 +02:00
tobi-wan-kenobi
7a47e9e13d [module/publicip] handle netifaces errors
make sure the monitoring thread doesn't terminate when there are
netifaces errors.

see #909
2022-08-06 09:17:49 +02:00
tobi-wan-kenobi
a7dba79664 [modules/publicip] add default route monitor
(re)add a separate thread that monitors the default route and updates
the module immediately, if the default route changes.

fixes #909
2022-08-05 14:30:50 +02:00
tobi-wan-kenobi
062506f467 [doc] fix code climage badge 2022-07-29 14:08:37 +02:00
tobi-wan-kenobi
b752eb8934 [doc] fix badge 2022-07-29 14:08:02 +02:00
tobi-wan-kenobi
38a5f38b43 [doc] add test badge 2022-07-29 14:05:15 +02:00
tobi-wan-kenobi
f59da4c2d8 [autotests] add more python versions 2022-07-29 14:04:04 +02:00
tobi-wan-kenobi
42ef6a3f02 Revert "[doc] remove code climate for now"
This reverts commit 7635fec36f.
2022-07-29 14:03:12 +02:00
tobi-wan-kenobi
921ddb64f0 Revert "[autotest] remove code climate dependency"
This reverts commit 61ebc3aea6.
2022-07-29 14:02:54 +02:00
tobi-wan-kenobi
a78403d3e8 [publicip] fix tests and bugs 2022-07-29 14:00:28 +02:00
tobi-wan-kenobi
61ebc3aea6 [autotest] remove code climate dependency 2022-07-29 13:44:40 +02:00
tobi-wan-kenobi
6bac2b6e34 [autotest] remove environment variable setting 2022-07-29 13:39:06 +02:00
tobi-wan-kenobi
6980d5e5d0 [autotest] use coverage3 explicitly? 2022-07-29 13:35:58 +02:00
tobi-wan-kenobi
db13bd94a1 [autotest] use separate action for code climate 2022-07-29 13:29:50 +02:00
tobi-wan-kenobi
e8c493607f [autotest] add debug flag to code climate tool 2022-07-29 13:27:03 +02:00
tobi-wan-kenobi
00b7929df5 [autotest] fix ubuntu package installation 2022-07-29 13:19:32 +02:00
tobi-wan-kenobi
184762ac57 [autotest] first attempt at a github action that runs tests 2022-07-29 13:18:07 +02:00
tobi-wan-kenobi
7635fec36f [doc] remove code climate for now
since those were triggered by the travis builds, remove code climate as
well (might reintroduce them later)

see #906
2022-07-29 11:41:34 +02:00
tobi-wan-kenobi
a8a4a86350 [all] remove Travis CI
As far as I can tell, Travis CI is *not* free anymore, even for OSS
projects. I assumed the 10k credits were monthly or yearly, but
seemingly, they are a one-time thing.

So, remove Travis CI and start searching for a better replacement.

see #906
2022-07-29 11:29:18 +02:00
tobi-wan-kenobi
a1c4b3c65e [modules/spotify] enable scrolling
this change should enable scrolling for the spotify module

(unfortunately, i am unable to fully test this, as i am not using
spotify)

fixes #903
2022-07-29 11:18:07 +02:00
tobi-wan-kenobi
b5535fcdc1
Merge pull request #905 from br0xpl/main
Pulseaudio bug when chaning sink/source (between mono and stereo devices).
2022-07-25 09:47:19 +02:00
Adamczyk Błażej
8e85eaa018 Fixed a bug in pulseaudio when changing source/sink, e.g. (dis)connecting bluetooth headset. 2022-07-25 09:12:42 +02:00
tobi-wan-kenobi
e2aa039276
Merge pull request #904 from ishaanbhimwal/fix_typos
fix multiple typos
2022-07-21 08:14:13 +02:00
Ishaan Bhimwal
a5fbc73c44 fix typo 2022-07-21 11:37:33 +05:30
tobi-wan-kenobi
39f7ad9a75
Merge pull request #902 from tfwiii/main
Fixed runaway thread creation (faulty indenting)
2022-07-11 19:43:01 +02:00
Tom Watson
2fa7931783 Fixed runaway thread creation (faulty indenting) 2022-07-12 00:19:19 +07:00
tobi-wan-kenobi
b594fd4263
Merge pull request #899 from tfwiii/publicip-update_on_default_route_change
Updated to automatically update on detecting a change to default route
2022-07-10 13:52:28 +02:00
Tom Watson
284662a0ba Ran publicip.py through black 2022-07-07 18:38:43 +07:00
Tom Watson
9a6e61173f Updated publicip to automatically update on detecting a change to default route 2022-07-07 18:08:20 +07:00
tobi-wan-kenobi
05c28c52c7
Merge pull request #897 from tfwiii/updated_publicip_module
Updated contrib/publicip module and util/location
2022-07-06 14:57:08 +02:00
Tom Watson
a97a7fe507 Updates addressing PR comments
Added location_info() to util/location API to return a dict of all location information. Updated modules/contrib/publicip to use that API. Changed modules/contrib/publicip refresh period back to 60 minutes. Changed /util/location API from 'country_name' back to 'name'
2022-07-06 19:37:29 +07:00
Tom Watson
6f137c4927 Update following PR review
Moved to format string handling of parameters. Minor refactoring.
2022-07-06 17:51:19 +07:00
Tom Watson
218bfa2235 Updated contrib/publicip module and util/location
Added another API endpoint, Added options to display country name, country code, city name and lat/long coordinates, attempt to handle failure to fetch info from API endpoints cleanly
2022-07-06 01:05:25 +07:00
tobi-wan-kenobi
6f4f163a7d [core/layout] use python-only layout detection
remove precompiled binary and make the layout module
fall back to the python implementation.

fixes #883
2022-07-03 14:02:38 +02:00
tobi-wan-kenobi
326e2f9318 [core/theme] add support for XDG_DATA_DIRS
read theme information from
$XDG_DATA_DIRS/bumblebee-status/themes

see #821
2022-07-03 13:56:02 +02:00
tobi-wan-kenobi
f462102439
Merge pull request #892 from SamTebbs33/main
Hide battery module if there are no batteries
2022-06-22 11:29:58 +02:00
Sam Tebbs
eabf167c1f Hide battery module if there are no batteries 2022-06-22 09:12:14 +01:00
tobi-wan-kenobi
df9890690a
Merge pull request #890 from anarcat/upower-no-bat
handle missing battery case
2022-06-20 17:37:04 +02:00
Antoine Beaupré
16c4ce2ee6
handle missing battery case
I run the same bumblebee-status configuration on my laptop and my
workstation. On my laptop, the upower module works fine: it says "ac"
when plugged in, charging, all that stuff is great.

But on my workstation, it's completely broken: it thinks there's a
battery (which is a mistake: there is no battery at all, apart maybe
from the CMOS battery, but that's not covered by upower), and it
thinks it's discharged, which makes a very noisy warning in the bar.

Now maybe there's something wrong with dbus, Debian, the kernel,
Linux, or some thing else in the stack. All I know is that
`self.power.get_display_device()` returns something like a valid
dbus object here and from there it confuses the heck out of the
module.

So this just adds a function to check if the actual device we're
talking about is actually present, and bails earlier otherwise.

Before: battery logo and "0% 00:00m!", all marked as critical ("red")

After: "ac" with the plugged in logo, not marked critical ("black")
2022-06-20 11:34:12 -04:00
tobi-wan-kenobi
e6f1939857
Merge pull request #889 from anarcat/thin-space
reduce spacing in awesome fonts iconset
2022-06-15 18:28:25 +02:00
Antoine Beaupré
2d6041be5d
reduce spacing in awesome fonts iconset
This replaces the previous normal spacing character (which is the
usual ASCII 0x20 SPACE) by a *narrower* spacer (which is unicode
U+2009 THIN SPACE).

I found that space thanks to

https://en.wikipedia.org/wiki/Space_(punctuation)#Types_of_spaces

... and specifically:

https://en.wikipedia.org/wiki/Thin_space

... and this actually works, amazingly. Probably because it is pretty
standard as it's part of the SI specification (thousands separator),
Tex (`\thinspace` or `\,`), and HTML (`&thinsp;`)

Closes: #888
2022-06-15 11:50:23 -04:00
tobi-wan-kenobi
b46c295827
Merge pull request #886 from anarcat/patch-1
expand FAQ about fonts
2022-06-08 18:30:13 +02:00
anarcat
9072249d5c
expand FAQ about fonts
This adds a little more information about the font stuff based on the things I learned in https://github.com/tobi-wan-kenobi/bumblebee-status/issues/881#issuecomment-1150034722
2022-06-08 11:51:56 -04:00
tobi-wan-kenobi
3764e758a2
Merge pull request #885 from anarcat/srcery
add srcery theme (closes: #881)
2022-06-08 17:25:49 +02:00
Antoine Beaupré
7b63efee36
add srcery theme (closes: #881) 2022-06-08 11:16:13 -04:00
tobi-wan-kenobi
4afb8d8636 [doc] add man pages
Many thanks to https://github.com/benthetechguy, who wrote man pages for
bumblebee-status and bumblebee-ctl

fixes #882
2022-06-08 08:34:05 +02:00
tobi-wan-kenobi
7dd5914e3f
Merge pull request #884 from benthetechguy/faq
Change nonexistent image to link
2022-06-08 02:52:47 +02:00
benthetechguy
c001f031a1
Change nonexistent image to link 2022-06-07 19:14:51 -04:00
tobi-wan-kenobi
7fc712862c
Merge pull request #877 from FraSharp/main
[modules]: introduce pamixer module
2022-05-26 13:43:02 +02:00
FraSharp
5c166beebf [modules]: introduce pamixer module
Signed-off-by: FraSharp <s23265@iisve.it>
2022-05-26 13:00:40 +02:00
tobi-wan-kenobi
6b2b5217ed
Merge pull request #876 from SamTebbs33/main
Add popup command parameter to the system module
2022-05-20 10:26:10 +02:00
Sam Tebbs
91b1b5e037 Add popup command parameter to the system module 2022-05-20 09:21:26 +01:00
tobi-wan-kenobi
2e8495d5ff
Merge pull request #875 from SamTebbs33/main
Hide progress module if it's inactive
2022-05-13 13:50:08 +02:00
Sam Tebbs
f01179290b Hide progress module if it's inactive 2022-05-13 10:21:31 +01:00
tobi-wan-kenobi
221ea0d22f
Merge pull request #874 from Timoses/patch-1
Fix logout item using hardcoded command
2022-05-11 20:04:18 +02:00
Timoses
a6d2ccc666
Fix logout item using hardcoded command 2022-05-11 19:58:34 +02:00
tobi-wan-kenobi
b7dd47c834
Merge pull request #873 from alexcoder04/main
arch-update: sleep 1 sec before checking
2022-05-01 12:50:52 +02:00
alexcoder04
3da0f08fcb
arch-update: sleep 1 sec before checking
When waking up from suspend, there is sometimes a delay connecting to
the network, so arch-update gives an error
2022-05-01 12:15:29 +02:00
tobi-wan-kenobi
c011cfb6c1
Merge pull request #872 from SamTebbs33/main
Fix missing playback_status
2022-04-24 17:13:50 +02:00
Samuel Tebbs
d20dacb2dc Fix missing playback_status 2022-04-24 16:09:54 +01:00
tobi-wan-kenobi
d3de79e6b4
Merge pull request #871 from SamTebbs33/main
Add hide parameter to playerctl
2022-04-23 16:32:15 +02:00
Samuel Tebbs
771e7482d7 [modules/playerctl] add 'hide' parameter 2022-04-23 14:44:35 +01:00
tobi-wan-kenobi
d1ae8f277f
Merge pull request #870 from arivarton/main
Changed __time_format to self.__time_format.
2022-04-11 20:34:21 +02:00
arivarton
83d910a7ef Changed __time_format to self.__time_format. 2022-04-08 17:09:28 +02:00
tobi-wan-kenobi
f64f71b6e2
Merge pull request #869 from arivarton/main
Google calendar module
2022-04-08 14:27:47 +02:00
arivarton
a48ddbb2c8 Ran black -t py34 2022-04-08 14:23:22 +02:00
arivarton
8501c406af Google calendar module. 2022-04-08 14:15:28 +02:00
tobi-wan-kenobi
2383cadadc
Merge pull request #868 from bbernhard/pulseaudio_extension
Pulseaudio extension
2022-03-25 20:43:57 +01:00
Bernhard B
4d3de3be04 handle util.popup ImportError gracefully
* util.popup requires tkinter to be installed (in order to display
  popups). As most people will probably use the default configuration
  of the pulseaudio module (where the popup is disabled) and in order
  to avoid breaking existing setups, we catch import errors and just log
  them.
2022-03-25 19:41:10 +01:00
Bernhard B
82fa347f2c extended pulseaudio module
* added possibility to show currently selected default device in the
  statusbar (default: off)
* allows to override the left mouse button click with a different
  action (e.g open popup menu to change the current default device)
2022-03-24 21:23:59 +01:00
Bernhard B
7932af712e Merge branch 'main' into pulseaudio_extension 2022-03-22 18:21:33 +01:00
tobi-wan-kenobi
d446a44f06
Merge pull request #866 from ishaanbhimwal/aur-update
Add documentation and screenshot for aur-update module
2022-03-15 13:50:04 +01:00
ishaan
9b61eee725 add better screenshots 2022-03-15 17:32:45 +05:30
ishaan
1fc4139b7c add documentation and screenshot for aur-update 2022-03-15 15:56:42 +05:30
tobi-wan-kenobi
69edfc42ae
Merge pull request #865 from ishaanbhimwal/aur-update
Fix aur-update showing wrong update number
2022-03-14 21:20:53 +01:00
ishaan
f513582d44 fix aur-update showing wrong update number 2022-03-15 01:17:57 +05:30
tobi-wan-kenobi
2d99bce987
Merge pull request #864 from ishaanbhimwal/aur-update
Add module aur-update
2022-03-14 18:41:20 +01:00
ishaan
879744e19c add aur-update 2022-03-14 22:28:00 +05:30
tobi-wan-kenobi
a8fabce14e
Merge pull request #863 from Hame-daani/main
refactor contrib.persian_date module
2022-03-10 09:51:47 +01:00
Sadegh Hamedani
c228ca3b12
[contrib/persian_date] refactor using core.datetime module as parent 2022-03-10 11:53:19 +03:30
Sadegh Hamedani
82ca97c65f
[core/datetime] added 'dtlibrary' attribute and 'set_locale' method 2022-03-10 11:52:08 +03:30
tobi-wan-kenobi
fd1eb6e790
Merge pull request #846 from DTan13/main
added module
2022-03-10 02:52:51 +01:00
tobi-wan-kenobi
b31dea19cc
Merge pull request #861 from bbernhard/pactl_revert
Pactl revert
2022-03-07 21:23:26 +01:00
Bernhard B
d52f713063 Revert "Merge pull request #857 from bbernhard/pactl"
This reverts commit eb51a3c1c7, reversing
changes made to c57daf65ce.

Instead of creating a separate module, the changes will be integrated
into the pulseaudio module.
2022-03-07 20:53:11 +01:00
Bernhard B
0bf91c2f15 Merge branch 'main' into pactl_revert 2022-03-07 20:52:31 +01:00
Dhananjay Tanpure
3de6f9f4b9
Merge branch 'tobi-wan-kenobi:main' into main 2022-03-07 08:58:13 +05:30
Dhananjay Tanpure
e5cdabcc0f
mpd and title 2022-03-07 08:50:30 +05:30
tobi-wan-kenobi
fbe70607be
Merge pull request #860 from Hame-daani/main
[contrib] added module persian_date
2022-03-04 17:22:57 +01:00
Sadegh Hamedani
9cbc39e462
[contrib] added module persian_date 2022-03-04 18:28:13 +03:30
tobi-wan-kenobi
bf7aac4e6e
Merge pull request #859 from tobi-wan-kenobi/858
[core/input] methods can be event callbacks
2022-03-04 09:37:01 +01:00
tobi-wan-kenobi
7d33171749 [core/input] methods can be event callbacks
When registering an event (especially mouse events), if the parameter
is a valid method in the Module, execute that with the event as
parameter.

Add this in the core.spacer module as an example.

fixes #858
see #857
2022-03-04 09:35:43 +01:00
tobi-wan-kenobi
eb51a3c1c7
Merge pull request #857 from bbernhard/pactl
Pactl
2022-03-03 20:31:51 +01:00
tobi-wan-kenobi
c57daf65ce [themes/zengarden] add key colors 2022-03-03 14:31:47 +01:00
tobi-wan-kenobi
9a2e7637c9 [themes/zengarden] lighter accents 2022-03-02 16:16:24 +01:00
tobi-wan-kenobi
07200c466b [themes] add zengarden light (powerline) 2022-03-02 12:33:21 +01:00
Bernhard B
33d22c2637 removed debug log from 'pactl' module 2022-02-28 19:20:19 +01:00
Bernhard B
950931e1b9 added new module 'pactl'
* added new module 'pactl' which displays the current default sink and allows to
  select a different default sink from the popup menu.
2022-02-28 19:14:21 +01:00
tobi-wan-kenobi
a17356ee9d
Merge pull request #856 from LokiLuciferase/bugfix/redshift-spelling
fix case of Kelvin SI unit in redshift widget
2022-02-20 12:57:42 +01:00
Lukas Lüftinger
928f8258aa fix case of Kelvin SI unit in redshift widget 2022-02-20 12:29:49 +01:00
tobi-wan-kenobi
03731136b6 [modules/nic] fix missing check for None 2022-02-15 16:34:02 +01:00
tobi-wan-kenobi
8897c1bde5
Merge pull request #854 from mihaimorariu/fix/publicip-exception
Fix publicip
2022-02-14 15:19:58 +01:00
Mihai Morariu
283d47ff65 Merge branch 'main' into fix/publicip-exception 2022-02-14 16:19:43 +02:00
tobi-wan-kenobi
3aadab5628 [modules/publicip] handle missing public ip more gracefully
If location does not throw, but reports an empty public IP, return
"n/a".

Since this caused a bug, also add a test for it.

fixes #853
2022-02-14 14:58:01 +01:00
Mihai Morariu
2a77e3a85c Fix exception in location.py. 2022-02-14 15:36:24 +02:00
tobi-wan-kenobi
b1f49f6a1e
Merge pull request #852 from piyueh/patch-1
typo in nic.py: minium -> minimum
2022-02-12 20:07:46 +01:00
Pi-Yueh Chuang
4784be4076
typo in nic.py: minium -> minimum 2022-02-12 12:54:31 -05:00
tobi-wan-kenobi
a1ae6d4f34
Merge pull request #851 from deadbeef2000/nic_strength
[modules/nic] Added strength indicator for wifi signals
2022-02-12 11:23:49 +01:00
Christopher Kepes
5c390be25c [modules/nic] Added strength indicator for wifi signals 2022-02-12 11:06:10 +01:00
tobi-wan-kenobi
4f9553f7ea [modules/rss] fix insecure use of tempfile
fixes #850
2022-02-11 13:44:10 +01:00
tobi-wan-kenobi
8458eef1e6 [doc] add codeql badge 2022-02-09 21:24:35 +01:00
tobi-wan-kenobi
4c08cd812e
Create codeql-analysis.yml
Trying out CodeQL
2022-02-09 21:15:08 +01:00
tobi-wan-kenobi
80493d3bea
Merge pull request #848 from logan-connolly/feature/add_rose_pine_theme
feat(theme): add rose pine theme
2022-01-25 18:29:01 +01:00
Logan Connolly
c40a174463 feat(theme): add rose pine theme 2022-01-25 17:50:25 +01:00
Dhananjay Tanpure
8867f4f188
added module for blugon 2022-01-20 21:06:17 +00:00
tobi-wan-kenobi
08b5386140 [util/popup] fix endless loop on "close on leave"
When closing a popup window when the mouse leave the area (default
behaviour, unfortunately), the main "show()" got stuck in an infinite
loop.

Fix that by setting running to False when exiting.

fixes #844
2022-01-14 13:39:04 +01:00
tobi-wan-kenobi
8bde6378d4 [modules/arandr] handle case of "no layouts exist
To ensure that arandr works also if no layouts are available, add some
(very simplistic) exception handling.

see #844
2022-01-14 13:29:29 +01:00
tobi-wan-kenobi
1089792bc6
Merge pull request #845 from donfranio/apt-view-update-on-click
add updating view on apt-cache on click
2022-01-13 11:37:36 +01:00
Frank Scherrer
30dd0f2efb add updating view on apt-cache on click 2022-01-13 11:05:01 +01:00
tobi-wan-kenobi
c019c4f382
Merge pull request #843 from fedeliallalinea/readme-gentoo-ebuild
Added ebuild link on README.md
2022-01-06 10:30:55 +01:00
Marco Genasci
5d1059ba63 Added ebuild link on README.md
Signed-off-by: Marco Genasci <fedeliallalinea@gmail.com>
2022-01-06 10:13:40 +01:00
tobi-wan-kenobi
a89bc096ef
Merge pull request #842 from fedeliallalinea/emerge-status-module
New emerge status module
2022-01-06 09:03:52 +01:00
Marco Genasci
f4ca5eaa3b Added documentation and screenshot for emerge_status module
Signed-off-by: Marco Genasci <fedeliallalinea@gmail.com>
2022-01-06 08:07:49 +01:00
Marco Genasci
8a50eb6f81 New module emerge_status
Display information about the currently running emerge process.

Signed-off-by: Marco Genasci <fedeliallalinea@gmail.com>
2022-01-06 08:06:57 +01:00
tobi-wan-kenobi
441d4f0275
Merge pull request #841 from fedeliallalinea/fixes-some-qa
Fixes some QA
2022-01-05 13:12:47 +01:00
Marco Genasci
51c3805f7f Change deprecated dash-separated with underscore in setup.cfg
Signed-off-by: Marco Genasci <fedeliallalinea@gmail.com>
2022-01-05 09:34:38 +01:00
Marco Genasci
d430f90434 Excluding the tests folder from the installation
Signed-off-by: Marco Genasci <fedeliallalinea@gmail.com>
2022-01-05 09:34:27 +01:00
tobi-wan-kenobi
4b7a6a18d5
Merge pull request #840 from LokiLuciferase/feature/silence-spotify-exceptions
Silence exceptions in the spotify module
2021-12-28 11:33:43 +01:00
Lukas Lüftinger
8991bba90e Silence exceptions in the spotify module which may write large amounts of logs to ~/.xsession-errors 2021-12-28 00:34:10 +01:00
tobi-wan-kenobi
e590a3cf3f
Merge pull request #838 from ouuan/fix-playerctl-log
[contrib/playerctl]: don't log when no player is found
2021-12-17 11:18:27 +01:00
Yufan You
973dd6117e
[contrib/playerctl]: don't log when no player is found
`playerctl status` returns 1 when no player is found, which caused
contrib/playerctl to log many times when there's no player.
2021-12-17 18:07:45 +08:00
tobi-wan-kenobi
6ce761695a [doc] remove "master" branch 2021-11-07 13:50:44 +01:00
tobi-wan-kenobi
a84b4f9a65 [docs] fix docs build
pin docutils to < 0.18 as per https://github.com/readthedocs/readthedocs.org/issues/8616#issuecomment-952034858
2021-11-07 13:46:52 +01:00
tobi-wan-kenobi
26e4bdd7eb [modules/progress] improved autohide functionality
Simplify the previous autohide functionality by adding a flag that lets
a module (e.g. progress) indicate that the current state should be
"revealed" (not auto-hidden).

This vastly simplifies the implementation.

see #835
2021-11-06 08:21:08 +01:00
tobi-wan-kenobi
5ad211f862 Revert "[contrib/progress] allow hiding of inactive state"
This reverts commit cbd989309d.
2021-11-06 08:17:18 +01:00
tobi-wan-kenobi
6a3e4761bf Revert "[core/output] fix logic error when using "autohide""
This reverts commit 74ecbb6ca8.
2021-11-06 08:17:11 +01:00
tobi-wan-kenobi
74ecbb6ca8 [core/output] fix logic error when using "autohide"
- when state is critical or warning -> *show* the module
- when state is mayhide -> *hide* the module

see #835
2021-11-05 19:11:45 +01:00
tobi-wan-kenobi
cbd989309d [contrib/progress] allow hiding of inactive state
Add a new "hide-able" state "mayhide" that can be utilized by modules
without warning state. This state indicates that the module *may* be
hidden by autohide, if the user configures it like this.

see #835
2021-11-05 14:00:34 +01:00
tobi-wan-kenobi
f0ab3ef03a [core/config] fix autohide parameter from configuration file
Need to parse the config parameter "autohide" into a list and actually
check whether the current module is in the list.

see #835
2021-11-05 13:57:05 +01:00
tobi-wan-kenobi
c7f58ae2a4 [doc] fix typo (autohid vs. autohide)
see #835
2021-11-05 08:43:33 +01:00
tobi-wan-kenobi
4bbe25d195
Merge pull request #830 from kushagraa-j/nord-theme-colorful
added new theme nord-colorful
2021-11-03 20:36:14 +01:00
tobi-wan-kenobi
d94d12897d
Merge pull request #832 from cambid/solaar-module
new module solaar.py for logitech's unifying devices
2021-10-26 22:16:19 +02:00
Jan Fader
2cb72fcc30
add tests for solaar.py 2021-10-26 21:52:15 +02:00
Jan Fader
dced20bf89
refactor code to decrease cognitive complexity in update 2021-10-26 19:52:42 +02:00
Jan Fader
fdc9b78967
add new solaar.py for logitech's unifying devices 2021-10-26 19:27:02 +02:00
Kushagra Jain
d8216a5e2c added new theme 2021-10-26 13:54:35 +05:30
tobi-wan-kenobi
0fe89e13a0
Merge pull request #829 from izn/fix/codeclimate-coverage-report
fix: generate coverage xml before cc report
2021-10-23 16:10:50 +02:00
Thaynã Moretti
7542a47dbc fix: generate coverage xml before cc report 2021-10-23 11:02:46 -03:00
tobi-wan-kenobi
d89c6b1bc1
Merge pull request #828 from izn/docs/fix-travis-ci-url
docs: update travisci url
2021-10-23 15:30:29 +02:00
Thaynã Moretti
99bb5e99aa Fix Travis CI build URL 2021-10-23 10:24:02 -03:00
tobi-wan-kenobi
876774ce40 [travis] removing python 3.4 and 3.5
Since 3.4 and 3.5 are both not supported anymore, do not do unit tests
for them.
2021-10-23 13:43:08 +02:00
tobi-wan-kenobi
e6bb787e01
Merge pull request #826 from alexcoder04/main
[modules/sensors] use util.format.asbool()
2021-10-22 18:26:21 +02:00
alexcoder04
6b31cdb698 [modules/sensors] use util.format.asbool() + auto-check only if no path is specified 2021-10-21 14:43:15 +02:00
tobi-wan-kenobi
9106ec9c8f
Merge pull request #825 from alexcoder04/main
[modules/sensors] auto-determine the correct thermal zone
2021-10-21 14:07:42 +02:00
alexcoder04
0dc6a95ac2 [modules/sensors] auto-determine the correct thermal zone 2021-10-21 13:45:40 +02:00
tobi-wan-kenobi
618a22c122
Merge pull request #824 from izn/chore/add-libvirtvms-tests
Create libvirtvms tests
2021-10-12 17:06:04 +02:00
Thaynã Moretti
4007517e45 chore: create libvirtvms tests 2021-10-12 11:34:20 -03:00
tobi-wan-kenobi
d67232d8cf
Merge pull request #823 from izn/chore/add-dunst-tests
Create dunst tests
2021-10-12 03:22:32 +02:00
Thaynã Moretti
1580951474 chore: create missing dunstctl tests 2021-10-11 19:17:55 -03:00
Thaynã Moretti
40de07ba2e chore: create dunst tests 2021-10-11 19:16:37 -03:00
Thaynã Moretti
b5395fe764 chore: public toggle method 2021-10-11 19:16:14 -03:00
tobi-wan-kenobi
c96d119b0e [core/config] add autohide to configuration file
make it possible to configure a list of automatically hidden modules in
the configuration file (+ add documentation for it).

fixes #822
2021-10-07 15:39:29 +02:00
tobi-wan-kenobi
ed5a4e61e4 [modules/bluetooth] Add more error checking
Do not kill the bar when the dbus-send command fails.

see #818
2021-09-10 12:45:11 +02:00
tobi-wan-kenobi
9c463fc2b7
Merge pull request #813 from tomsaleeba/tomsaleeba/nvidiagpu-additions-2
fix: correct mem usage to be mem *io* usage
2021-08-16 07:36:36 +02:00
Tom Saleeba
d4339f6e43 fix: correct mem usage to be mem *io* usage 2021-08-15 22:27:44 -06:00
tobi-wan-kenobi
27194a92c8
Merge pull request #812 from soykan/patch-1
Small bug fix on todo module.
2021-08-14 19:28:08 +02:00
Soykan Ertürk
05f76c0d9a
Update todo.py 2021-08-14 20:16:45 +03:00
Soykan Ertürk
5a1addec7f
Fixing a small bug on todo module
todo counts new lines (blank lines) as todo and increments todo count. After my fix todo doesn't counts blank lines.
2021-08-14 20:14:54 +03:00
tobi-wan-kenobi
439b140916
Merge pull request #811 from soykan/patch-2
system module requirement
2021-08-12 22:16:42 +02:00
Soykan Ertürk
8be9f1a05c
system module requirement
added tkinter as requirement
2021-08-12 23:15:09 +03:00
tobi-wan-kenobi
b00c0ae47c
Merge pull request #810 from soykan/patch-1
Improving docs
2021-08-12 22:01:24 +02:00
Soykan Ertürk
473d2fbd14
Improving docs
I added tkinter as dependency in requirements.
2021-08-12 22:54:34 +03:00
tobi-wan-kenobi
5c65a8fef4
Merge pull request #807 from soykan/patch-2
Dependency for powerline themes
2021-08-10 19:28:22 +02:00
Soykan Ertürk
f98053371e
Dependency for powerline themes 2021-08-10 20:19:30 +03:00
tobi-wan-kenobi
f6dd17b383
Merge pull request #806 from tomsaleeba/tomsaleeba/nvidiagpu-additions
feat: add GPU usage % and GPU memory usage % to nvidiagpu
2021-07-25 06:54:52 +02:00
Tom Saleeba
98c92bb78f feat: add GPU usage % and GPU memory usage % to nvidiagpu 2021-07-24 15:18:04 -06:00
tobi-wan-kenobi
4d422ffc84
Merge pull request #801 from nepoz/NetworkTestFix
Removed dependency on pytest-socket for the Network module's unit tests.
2021-07-09 08:28:16 +02:00
nepoz
5a2dfc226b Removed dependency on pytest-socket for the Network module's unit tests. 2021-07-09 00:58:09 -05:00
tobi-wan-kenobi
a678241a70
Merge pull request #800 from nepoz/network
Network
2021-07-09 07:32:59 +02:00
nepoz
5d80a5a1a0 Slight refactoring to try and break apart networkmethod 2021-07-09 00:28:00 -05:00
nepoz
48501fa534 Updated docstring 2021-07-08 23:00:57 -05:00
nepoz
f9017c3a38 Added more tests and exception handling 2021-07-08 22:55:23 -05:00
nepoz
2100a7cfdb Set up initial testing framework for network module 2021-07-08 12:10:46 -05:00
nepoz
3f524ab371 Refactoring, making use of netifaces 2021-07-08 09:04:40 -05:00
nepoz
911230c659 first complete implementation of the network module 2021-07-05 13:54:28 -05:00
nepoz
c7df1926dc Formatting fixes, fixed state management and added some icons 2021-07-05 13:09:17 -05:00
nepoz
448ab6de83 Functional display for wireless connection 2021-07-05 12:34:42 -05:00
nepoz
4987c7d3e2 added stateful behavior 2021-07-05 11:26:46 -05:00
nepoz
f141b95d8f Basic functionaly for dealingn with signal strength 2021-07-05 10:29:37 -05:00
nepoz
1232c4d960 Initial commit -- give basic message about interface being used 2021-07-05 07:55:47 -05:00
tobi-wan-kenobi
80663bdbc8
Merge pull request #798 from sayansil/main
Use the existing util.cli module in contrib/optman
2021-06-30 08:09:26 +02:00
Sayan Sil
4485b65722
Use the existing util.cli module 2021-06-30 11:31:42 +05:30
tobi-wan-kenobi
3ff2e49e5f
Merge pull request #796 from ouuan/playerctl
[modules/playerctl]: support the stopped status
2021-06-26 13:30:01 +02:00
Yufan You
37ccbd7f4a
[modules/playerctl]: support the stopped status 2021-06-26 18:19:24 +08:00
tobi-wan-kenobi
447d094fe2
Merge pull request #795 from sayansil/main
Add active gpu module using optimus-manager
2021-06-25 15:49:59 +02:00
Sayan
e5007a5729 Add active gpu module using optimus-manager 2021-06-24 23:17:35 +05:30
tobi-wan-kenobi
ec71d7fbbe
Merge pull request #793 from ouuan/playerctl
[modules/playerctl]: use `playerctl -f` and add `playerctl.args`
2021-06-11 12:56:35 +02:00
Yufan You
c4046d0cd2
[doc]: link to the README instead of manpage 2021-06-11 18:12:13 +08:00
Yufan You
51f68addcd
[modules/playerctl]: BREAKING: use playerctl -f and add playerctl.args
1. Use `playerctl -f` to format, which is more powerful. This also fixes
   #767, which is caused by missing a few fields of the metadata.
2. Add `playerctl.args`, so that users can choose a specific player,
   etc.
3. Display nothing when there's no running player.

This is a breaking change. Users need to change `{title}` to
`{{title}}`.
2021-06-11 17:38:46 +08:00
tobi-wan-kenobi
4b6b4b9052 [core] add custom minimizer capability
Add a new set of parameters to allow modules to be customly minimized.

It works like this: If a module has the parameter "minimize" set to a
true value, it will *not* use the built-in minimizer, and instead look
for "minimized" parameters (e.g. if date has the "format" parameter, it
would look for "minimized.format" when in minimized state). This allows
the user to have different parametrization for different states.

Also, using the "start-minimized" parameter allows for modules to start
minimized.

Note: This is hinging off the *module*, not the *widget* (the current,
hard-coded hiding is per-widget). This means that modules using this
method will only show a single widget - the first one - when in
minimized state. The module author has to account for that.

see #791
2021-05-24 12:56:02 +02:00
tobi-wan-kenobi
dfd23a44de [modules/layout] add a new - generic - layout module
Add a new module "layout" that will eventually evolve into the only
keyboard layout module.

Right now, it uses an external binary (get-kbd-layout) to determine the
layout of a keyboard device (because I did not manage to call libX11
with ctypes correctly).

see #788
see #790
2021-05-16 21:09:58 +02:00
tobi-wan-kenobi
902288f30d [modules/sensors] do not truncate temperature
use strip() instead of a sub-list to get the value for the temperature.

fixes #787
2021-05-11 11:23:06 +02:00
tobi-wan-kenobi
7f03c9ce2d [doc] update module documentation 2021-05-10 17:48:23 +00:00
tobi-wan-kenobi
9e20b48cee
Merge pull request #789 from fredj/sun_param_fix
Fix parameters name for the sun module
2021-05-10 17:46:24 +00:00
Frederic Junod
046b950b8a Fix parameters name for the sun module 2021-05-10 14:35:34 +02:00
tobi-wan-kenobi
afeb30e40e
Merge pull request #786 from solar-core/AddManualParam
Update modules.rst
2021-04-28 20:11:11 +02:00
Edward
9553bec7db
Update modules.rst 2021-04-28 20:48:49 +03:00
tobi-wan-kenobi
1e13798c95 [core/input] add pseudo-event "update" to selectively update modules
to trigger an update of a module (without actually triggering a mouse
interaction), use the special event "update":

bumblebee-ctl -m <module> -b update

see #784
2021-04-28 12:41:04 +02:00
tobi-wan-kenobi
028932a560 [tests/cpu] adapt tests and add per-cpu tests
fixes #785
2021-04-27 17:17:55 +02:00
tobi-wan-kenobi
fb6be007e5 [core/output] fix minimum width with padding
when calculating the minimum width of a widget, also take the padding
into consideration.

see #785
2021-04-27 17:17:28 +02:00
tobi-wan-kenobi
10c169af8a [modules/core/cpu] optionally add per-cpu widget
if the parameter "percpu" is set to true, create one widget per cpu, and
also handle warning/error state on a per-widget basis.

see #785
2021-04-27 17:17:13 +02:00
tobi-wan-kenobi
8001ed3ada [modules/cpu] Add per-cpu mode
By explicitly setting "cpu.percpu=True" as parameter, it is now possible
to get the CPU utilization on a per-cpu basis.

Future extension: One widget per CPU (add ID of CPU and add
warning/error state on a per CPU basis)

see #785
2021-04-27 17:16:54 +02:00
tobi-wan-kenobi
4a6be622a8 [modules/rotation] fix widget creation
each iteration of the rotation module created new/duplicate widgets,
causing a status bar of infinite length.

fixes #782
2021-04-03 19:29:40 +00:00
tobi-wan-kenobi
0410ac9c6b [doc/shortcut] better example for shortcut module 2021-04-03 19:24:01 +00:00
tobi-wan-kenobi
527d1706c2 [core/module] add fallback for module loading
Looks like some Python versions work with find_spec(), others with
spec_from_file_location(), so add find_spec() as fallback.

fixes #779
2021-04-02 03:30:09 +00:00
tobi-wan-kenobi
7f2ce7d76e
Merge pull request #778 from c05m4r/main
[themes] add albiceleste-powerline and rastafari-powerline
2021-03-27 20:30:55 +00:00
Marcos Gabriel Miller
abcf861fcb [themes] add rastafari-powerline 2021-03-27 17:22:35 -03:00
Marcos Gabriel Miller
10c9321c24 [themes] add albiceleste-powerline 2021-03-27 17:22:10 -03:00
tobi-wan-kenobi
3d809eb590
Merge pull request #777 from JaroslawSlabik/main
Adding the ability to change the editor to module todo
2021-03-20 11:38:01 +01:00
jslabik
7756eaaa31 Adding the ability to change the editor to module todo 2021-03-20 01:18:46 +01:00
tobi-wan-kenobi
e560649531 [modules/shell] remove obsolete event handlers
modules are now automatically updated when clicked.

fixes #776
2021-03-18 15:30:03 +01:00
tobi-wan-kenobi
4187bddad6 [modules/shell] do not default to "makewide"
to avoid unnecessarily wide shell modules, set "makewide" to false, if
it is not set at all.

fixes #775
2021-03-18 15:29:06 +01:00
tobi-wan-kenobi
65da1e2246 [doc] migrate to travis.com 2021-03-13 20:45:56 +01:00
tobi-wan-kenobi
38613495f2 [tests] Adjust for widget hiding 2021-03-13 20:44:36 +01:00
tobi-wan-kenobi
9f89e3a657 [core] make bumblebee more reactive
- set default delay to 0
- split input reading into 2 threads
- get rid of polling
2021-03-13 14:10:30 +01:00
tobi-wan-kenobi
868502d62e [modules/keys] add missing modules
forgot to add in the previous commit
2021-03-13 14:04:42 +01:00
tobi-wan-kenobi
8d88b23947 [modules] add a module "keys" that shows whether a key is pressed
also, add backend functionality to hide individual widgets of a module.
2021-03-13 13:17:20 +01:00
tobi-wan-kenobi
7d0d1455c8 [core/module] Add fallback for user module loading
If importlib.machinery is not present, fall back to importlib.util to
load the module by its absolute name.

hopefully fixes #763
2021-03-09 19:12:59 +01:00
tobi-wan-kenobi
6d1536ca80
Merge pull request #773 from fredj/stock_doc
[doc] Remove requests dependency in stock module
2021-03-02 18:31:40 +01:00
Frederic Junod
0ff49ac7d5 [doc] Remove requests dependency in stock module
The module is using `urllib.request`
2021-03-02 17:00:14 +01:00
tobi-wan-kenobi
32eef6b204 [doc] fix typos/wrong grammar
fixes #769
2021-02-27 13:09:32 +01:00
tobi-wan-kenobi
da7734d81f
Merge pull request #766 from michalroxorpl/enable_scroll_in_shell
Add code to enable scrolling of shell module output
2021-02-26 18:12:18 +01:00
Michal Cieslicki
6d7934f0fe Add code to enable scrolling of shell module output 2021-02-26 18:02:47 +01:00
tobi-wan-kenobi
d4425039b9
Merge pull request #764 from michalroxorpl/external_configuration_file
Add parameter to specify a configuration file
2021-02-20 14:49:45 +01:00
Michal Cieslicki
618ebbeccc Add parameter to specify a configuration file 2021-02-20 13:50:31 +01:00
tobi-wan-kenobi
31f1f99102 [doc] regenerate to fix typos 2021-02-12 09:31:32 +01:00
tobi-wan-kenobi
6e3caa6f14 [modules/shortcut] fix typo
fixes #760
2021-02-12 09:31:09 +01:00
tobi-wan-kenobi
0734c970b0 [modules/hddtemp] fix typo
fixes #761
2021-02-12 09:30:24 +01:00
tobi-wan-kenobi
406eadeac7 [modules/time] update once per second
fixes #676
2021-01-17 15:35:39 +01:00
tobi-wan-kenobi
a27c284869 [core/module] fix failing unit test
wrong error handling again
2021-01-17 15:29:44 +01:00
tobi-wan-kenobi
beca26c2bf [core/config] Allow modules to be hidden when in critical/error state
When a module is in critical state, the user can now hide the module
(e.g. if pulseaudio fails to load).

fixes #746
2021-01-17 15:21:40 +01:00
tobi-wan-kenobi
45c0a382c9 [core/module] fix load error when no user module exists 2021-01-17 15:17:14 +01:00
tobi-wan-kenobi
21ded8f640 [core] Allow module loading from user directory
If a module fails to load from both core and contrib, fall back to
loading by file name from "~/.config/bumblebee-status/modules/<name>.py"

fixes #757
2021-01-17 14:18:58 +01:00
tobi-wan-kenobi
413abdcae7 [doc] add license badge 2020-12-31 13:22:40 +01:00
tobi-wan-kenobi
5e790b7496 [doc] Add reference to slackbuild
fixes #755
2020-12-31 13:21:35 +01:00
tobi-wan-kenobi
0e37d6cbf2
Merge pull request #754 from gkeep/playerctl-improvements
[modules/playerctl] Add format and layout parameters
2020-12-27 17:58:33 +01:00
gkeep
b74ebce702 [modules/playerctl] Small fix 2020-12-27 19:12:36 +03:00
gkeep
436cea8f37 [modules/playerctl] Add format and layout parameters 2020-12-27 19:02:45 +03:00
tobi-wan-kenobi
af95b2e276 [tests] fix syntax error 2020-12-27 14:52:45 +01:00
tobi-wan-kenobi
960792b2e5 [tests] fix module load test for python 3.6 and further 2020-12-27 14:49:12 +01:00
tobi-wan-kenobi
73b071edb0 [doc] clarify interval handling further
fixes #751
2020-12-20 15:11:35 +01:00
tobi-wan-kenobi
7b1659a1b5 [core/theme] add /usr/share as theme directory
add a theme directory /usr/share/bumblebee-status/themes for system-wide
theme installation.

fixes #753
2020-12-20 10:23:06 +01:00
tobi-wan-kenobi
7fd4f710a1 [doc] improve interval documentation
Add information about global vs. per-module intervals.

see #751
2020-12-19 13:17:05 +01:00
tobi-wan-kenobi
a94114dd94 [core/module] better error reporting for failed module loads
if a module fails to load, explicitly log errors for core and contrib in
the error log, but be a bit less verbose (and less confusing) in the
module error message itself.

fixes #747
2020-12-19 13:07:29 +01:00
tobi-wan-kenobi
64355e5314
Merge pull request #752 from gkeep/spotifyd-support
[modules/spotify] Add initial spotifyd compatibility
2020-12-18 12:49:48 +01:00
gkeep
601b2115ce Add initial spotifyd compatibility 2020-12-18 13:56:26 +03:00
tobi-wan-kenobi
3644acce76 [pip] adjust removed dependencies 2020-12-07 10:00:20 +01:00
tobi-wan-kenobi
fa66873582 [core/theme] add xresources support
add support for reading foreground and background, and colors, from
xresources.py

many thanks to @Aliuakbar for the addition!

fixes #731
2020-12-07 09:01:08 +01:00
tobi-wan-kenobi
13b06793da
Merge pull request #745 from jayvdb/libvirt
.travis.yml setup
2020-12-05 13:43:04 +01:00
John Vandenberg
9e9dff7658 rss.txt: Unpin feedparser
And install feedparser<6 on Travis for Python 3.4-5.
2020-12-05 17:11:30 +07:00
John Vandenberg
e872897346 .travis.yml: Pre-install libvirt-dev
Also install libvirt-python<6.3 for Python 3.4 support.
2020-12-05 15:01:07 +07:00
John Vandenberg
a018343af6 .travis.yml: Add libgit2-dev
And pre-install pygit2<1 for Python 3.4 and 3.5 support.
2020-12-05 14:59:46 +07:00
John Vandenberg
d0e309ad0f test_format: Allow 2.00TiB 2020-12-05 14:57:52 +07:00
John Vandenberg
b598869450 .travis.yml: Add taskwarrior
Taskwarrior package requires task to be installed.
2020-12-05 14:57:50 +07:00
tobi-wan-kenobi
5ff6263986 [travis] forgotten in previous commit 2020-12-04 18:55:21 +01:00
tobi-wan-kenobi
3937e73e7d [travis] hopefully fixed test build
locally tested with a xenial image, and this should work.

Unfortunately, cannot get the pygit2 to build against the provided
libgit2-dev, so using the packaged version instead.
2020-12-04 18:55:03 +01:00
tobi-wan-kenobi
e0fc98bfb0 [travis] temporarily exclude dbus and libvirt from requirements
since they cause issues in the automated tests, do not install the dbus
and libvirt python libraries.
2020-12-03 20:48:06 +01:00
tobi-wan-kenobi
47a640e610
Merge pull request #744 from jayvdb/travis-pip-install
.travis.yml: Install declared PyPI dependencies
2020-12-03 07:15:49 +01:00
John Vandenberg
a58454de97 .travis.yml: Allow building dbus-python 2020-12-03 12:54:44 +07:00
John Vandenberg
21268d7d86 Use dbus-python PyPI package 2020-12-03 10:55:23 +07:00
John Vandenberg
071a69b2d8 .travis.yml: Install declared PyPI dependencies 2020-12-03 08:02:41 +07:00
John Vandenberg
659a0147da system.txt: Replace tk with Pillow
Related to https://github.com/tobi-wan-kenobi/bumblebee-status/issues/741
2020-12-03 08:02:12 +07:00
John Vandenberg
38c4b46351 libvirtvms.txt: Fix PyPI dependency name
Related to https://github.com/tobi-wan-kenobi/bumblebee-status/issues/741
2020-12-03 07:56:36 +07:00
tobi-wan-kenobi
cd851340e2 [pip] updated/fixed dependencies
many thanks to @jayvdb for pointing those out!

fixes #741
2020-12-02 21:21:34 +01:00
tobi-wan-kenobi
1d8b261057
Merge pull request #742 from cdbrkfxrpt/main
Add TiB to disk units, add SI unit option for disk space
2020-12-02 21:14:18 +01:00
tobi-wan-kenobi
efebc8d049 [travis] add sudo (accidentially removed) 2020-12-02 20:53:43 +01:00
Florian Eich
3c08eafa4a Add TiB to disk units, add SI unit option for disk space 2020-12-02 19:08:45 +01:00
tobi-wan-kenobi
2de39be731 [travis/code climate] update to new reporter + small fixes 2020-12-02 11:07:19 +01:00
tobi-wan-kenobi
a8d1254e06 [modules/nic] Make regex for SSID a raw string 2020-12-01 15:58:55 +01:00
tobi-wan-kenobi
5fb19b66da
Merge pull request #740 from cdbrkfxrpt/main
Change iw call in module nic from link to info
2020-12-01 03:32:10 +01:00
Florian Eich
de01d96b91 Change iw call in module nic from link to info 2020-12-01 00:19:17 +01:00
tobi-wan-kenobi
f5e6bc12db [doc] update doc 2020-11-30 17:22:42 +01:00
tobi-wan-kenobi
d0ee1b06e4 [modules/nic] make exclude list regular expression capable
Allow the usage of regexps in the exclude list, but keep the "this is a
prefix" logic for backwards compatibility.

should address (see #738)
2020-11-30 17:21:17 +01:00
tobi-wan-kenobi
a8b28cd0bf
Merge pull request #739 from eumiro/py39
add python 3.9 support
2020-11-30 07:06:27 +01:00
Miroslav Šedivý
681bba4f12 add python 3.9 support 2020-11-29 22:14:33 +01:00
tobi-wan-kenobi
6c08336154
Merge pull request #736 from jebaum/main
add rofication module
2020-11-24 07:03:56 +01:00
James Baumgarten
02465ea0c2 add rofication module 2020-11-23 20:44:17 -08:00
Tobias Witek
08ef42834e [modules/nic] update documentation to include iwgetid
fixes #734
2020-11-13 14:56:31 +01:00
tobi-wan-kenobi
0fc1782e0b
Merge pull request #733 from martindoublem/main
[module] Improved smartstatus with combined_singles
2020-11-06 12:36:33 +01:00
Martin Morlot
a811c9c886 [module] Improved smartstatus with combined_singles
Added combined_singles as way to detect the drives that are permanently inside your machine and not plugged via USB.

As USB flash drives without smartstatus sometime caused the module to crash.
2020-11-06 12:14:56 +01:00
tobi-wan-kenobi
c5f13746b3
Merge pull request #730 from donfranio/fix-for-missing-python2
update python name in shebang to respect PEP-0394
2020-10-26 09:03:51 +01:00
Frank Scherrer
5e96b63c60
update python name in shebang to respect PEP-0394 2020-10-26 08:28:55 +01:00
tobi-wan-kenobi
d1e9e7e8bb
Merge pull request #729 from spxtr/patch-1
Fix arch-updates off-by-one.
2020-10-26 03:08:30 +01:00
Joe Finney
9b82e736a0
Fix arch-updates off-by-one.
There's a newline in the output so this overcounts by one.
2020-10-25 17:29:00 -07:00
tobi-wan-kenobi
45125f39af
Merge pull request #728 from joachimmathes/module_dunstctl
Provide alternative dunstctl implementation
2020-10-18 22:06:35 +02:00
Joachim Mathes
3c0499ba56 Provide alternative dunstctl implementation 2020-10-18 21:53:47 +02:00
tobi-wan-kenobi
68bd6f8ef8
Merge pull request #727 from w1kl4s/master
Fix Python 3.9 compatibility
2020-10-14 18:30:56 +02:00
w1kl4s
1a7ae9ecc6 Fix Python 3.9 compatibility
Replaced threading.Thread.isAlive() with threading.Thread.is_alive()
2020-10-14 18:07:29 +02:00
tobi-wan-kenobi
49edf97b89
Merge pull request #723 from martindoublem/main
[Bluetooth2] fixed the execution of the toggle state
2020-10-09 11:14:59 +02:00
Martin Morlot
1912f3053d [Bluetooth2] fixed the execution of the toggle state 2020-10-09 10:59:59 +02:00
tobi-wan-kenobi
bfbd94232d
Merge pull request #722 from izn/add-layout-xkbswitch-tests
Create layout-xkbswitch tests
2020-10-08 04:58:16 +02:00
Thaynã Moretti
04a2ea438b Create layout-xkbswitch tests 2020-10-07 17:37:04 -03:00
tobi-wan-kenobi
270b3e13bf
Merge pull request #721 from izn/add-dunstctl-tests
Create dunstctl tests
2020-10-06 06:37:26 +02:00
Thaynã Moretti
180a87e0c3 Create dunstctl tests 2020-10-05 20:10:01 -03:00
tobi-wan-kenobi
f7feb3e674
Merge pull request #720 from izn/add-arch-update-tests
Add arch-update tests
2020-10-04 21:06:18 +02:00
Thaynã Moretti
0a7a4150e0 Create arch-update tests 2020-10-04 15:35:13 -03:00
tobi-wan-kenobi
1751e4afa2
Merge pull request #718 from joshbarrass/spotify
Add "concise controls" to Spotify module
2020-10-04 16:54:42 +02:00
tobi-wan-kenobi
284ef6b260
Merge pull request #717 from joshbarrass/main
Add org-mode TODO module
2020-10-04 16:52:11 +02:00
Joshua Barrass
fbe5764313
Add "concise controls" to spotify module 2020-10-04 15:41:15 +01:00
Joshua Barrass
823a57d261
Add org-mode TODO module 2020-10-04 14:44:27 +01:00
tobi-wan-kenobi
5ec4b04a64
Merge pull request #716 from Niladri29/main
Some minor
2020-10-02 21:27:44 +02:00
Niladri Bhattacharjee
130bfc0f0b
Some minor 2020-10-03 00:53:04 +05:30
tobi-wan-kenobi
f2153b95a5 [doc] add scrolling parameters
see #710
2020-10-02 09:34:53 +02:00
tobi-wan-kenobi
57f669a5e7
Merge pull request #714 from izn/create-symbolic-links
Create symbolic links
2020-10-02 08:09:05 +02:00
Thaynã Moretti
a253e70328 Update documentation 2020-10-01 19:18:49 -03:00
Thaynã Moretti
4df495601a Create symbolic links 2020-10-01 19:10:46 -03:00
tobi-wan-kenobi
96f8e92822
Merge pull request #712 from izn/add-amixer-tests
Add amixer tests
2020-09-29 05:07:19 +02:00
tobi-wan-kenobi
1bdaedb481
Merge pull request #711 from izn/fix/force-feedparser-package-version
Force feedparser to 6.0.0b1
2020-09-29 05:06:24 +02:00
Thaynã Moretti
7d85ba87d5 Force feedparser to 6.0.0b1 2020-09-28 19:45:34 -03:00
Thaynã Moretti
9e80d4d907 Add amixer tests 2020-09-28 19:24:45 -03:00
tobi-wan-kenobi
1759c33cf3 [bumblebee] make input delay configurable
make the delay before updating the bar after an input event
configurable as engine.input_delay.

see #702
2020-09-16 09:13:48 +02:00
tobi-wan-kenobi
806b97895e [bumblebee] add small sleep before update
in some situations/modules (spotify, playerctl, etc.), it is possible
that a state change (e.g. play/pause) takes a small time to actually
propagate into the whole system (e.g. until the next "update" retrieves
the correct value).

To alleviate that, add a very small delay to the update.
2020-09-16 09:09:49 +02:00
tobi-wan-kenobi
8a4fc40947 Revert "[modules/spotify] properly initialize widgets"
This reverts commit aa6238a5c6.
2020-09-15 20:27:50 +02:00
Tobias Witek
e42f09b27e [bumblebee] Make update lock on input non-blocking
for consistent lock behaviour, make the input loop lock non-blocking.

see #702
2020-09-13 11:21:39 +02:00
Tobias Witek
aa6238a5c6 [modules/spotify] properly initialize widgets
see #702
2020-09-13 11:19:05 +02:00
Tobias Witek
fcbb89db90 [modules/spotify] make global dbus object
instead of creating a new dbus instance during each update interval,
reuse one dbus instance.

see #702
2020-09-13 11:17:21 +02:00
tobi-wan-kenobi
f37eb31f94
Merge pull request #709 from cristianmiranda/missing_screenshots_for_dunstctl_and_thunderbird
Docs; Missing screenshots for dunstctl and thunderbird
2020-09-11 06:20:11 +02:00
Cristian Miranda
4621de95b5 docs[thunderbird]: Added missing screenshot 2020-09-10 17:42:41 -03:00
Cristian Miranda
2390dfa946 docs[dunstctl]: Added missing screenshot 2020-09-10 17:42:19 -03:00
tobi-wan-kenobi
070fe865dd [modules/spotify] update in the background
to rule out issues in the dbus communication, update the spotify module
in the background.

see #702
2020-09-09 14:15:16 +02:00
tobi-wan-kenobi
b79c19b616 [modules/spotify] fix song not shown issue
the previous commit accidentially removed the song display. re-add that
and also add a bit of logging for troubleshooting.
2020-09-06 14:27:17 +02:00
tobi-wan-kenobi
3a5365a2c3 [doc] add testing guidelines, general improvements 2020-09-06 14:09:18 +02:00
tobi-wan-kenobi
965e7b2453 [modules/spotify] improve update mechanism
instead of updating the widget list during each update, create the list
of widgets during initialization, and later only update the widget
states.

see #702
2020-09-06 14:00:32 +02:00
tobi-wan-kenobi
7a4d4d5ab6
Merge pull request #706 from cristianmiranda/thunderbird
[modules/thunderbird]: Thunderbird's unread email counts by inbox
2020-09-06 07:13:46 +02:00
tobi-wan-kenobi
f180c7cc58
Merge pull request #705 from cristianmiranda/dunstctl
[modules/dunstctl]: Toggle dunst v1.5.0+ notifications using dunstctl
2020-09-06 07:10:58 +02:00
Cristian Miranda
100568206a [modules/thunderbird]: Thunderbird's unread email counts by inbox 2020-09-05 19:22:40 -03:00
Cristian Miranda
d568ef3622 [modules/dunstctl]: Toggle dunst v1.5.0+ notifications using dunstctl 2020-09-04 17:30:46 -03:00
tobi-wan-kenobi
ecf1825c00
Merge pull request #704 from izn/add-uptime-and-public-ip-tests
Add uptime and public ip tests
2020-09-03 03:42:38 +02:00
Thaynã Moretti
e7b853c667 Remove useless mock side effect 2020-09-02 22:16:58 -03:00
Thaynã Moretti
710a2cef98 Create uptime and public ip tests 2020-09-02 22:10:09 -03:00
tobi-wan-kenobi
7e7327c374
Merge pull request #703 from izn/add-load-module-tests
Add load module tests
2020-09-02 19:38:06 +02:00
Thaynã Moretti
6c0930cfae Add load module tests 2020-09-02 14:33:07 -03:00
tobi-wan-kenobi
1638180b6d
Merge pull request #700 from izn/add-date-and-time-module-tests
Add date and time module tests
2020-09-02 07:15:56 +02:00
tobi-wan-kenobi
7b57299cdb
Merge pull request #701 from izn/improve-network-traffic-tests
Improve network traffic module tests
2020-09-02 07:14:53 +02:00
Thaynã Moretti
04f6a6a08f Add time module tests 2020-09-01 22:16:57 -03:00
Thaynã Moretti
213c28992c Add date tests 2020-09-01 22:16:57 -03:00
Thaynã Moretti
032a651efa Improve network traffic module tests 2020-09-01 22:12:54 -03:00
tobi-wan-kenobi
1b66e17780
Merge pull request #699 from izn/add-cpu-module-tests
Add CPU module tests
2020-09-02 02:20:47 +02:00
tobi-wan-kenobi
6db8470c2b
Merge pull request #698 from izn/add-datetime-tests
Add datetime tests + freezegun dependency
2020-09-02 02:17:11 +02:00
Thaynã Moretti
07f0b7e34a Add CPU module tests 2020-09-01 20:54:10 -03:00
Thaynã Moretti
b1501a8159 Remove useless import 2020-09-01 20:29:04 -03:00
Thaynã Moretti
0776592da6 Add freezegun to Travis dependencies 2020-09-01 20:12:31 -03:00
Thaynã Moretti
f22095e978 Add datetime module tests 2020-08-31 17:48:19 -03:00
tobi-wan-kenobi
ebeb084da0
Merge pull request #696 from izn/fix-memory-module-tests
Fix memory module tests
2020-08-30 18:06:14 +02:00
Thaynã Moretti
0c8d682d62 Add unit tests 2020-08-30 12:49:33 -03:00
Thaynã Moretti
49de0e520b Reduce code cognitive complexity 2020-08-30 12:37:58 -03:00
Thaynã Moretti
6f6f3cedd9 Improve meminfo parse logic 2020-08-30 12:28:48 -03:00
Thaynã Moretti
820598b1b8 Fix memory module tests 2020-08-30 12:11:48 -03:00
tobi-wan-kenobi
780c5bf3d0
Merge pull request #695 from izn/add-memory-module-tests
Add memory module tests
2020-08-30 16:25:08 +02:00
Thaynã Moretti
945838dc74 Create memory module tests 2020-08-30 11:15:23 -03:00
tobi-wan-kenobi
9d5035fa10
Merge pull request #694 from bbernhard/octoprint_long_error
small improvements in octoprint plugin
2020-08-30 09:21:04 +02:00
Bernhard B
c339a16365 small improvements in octoprint plugin
* octoprint sometimes returns additional information a the 3d printer is offline.
  so, it's better to check if the octoprint job state starts with "Offline".

* in case the returned job state is really long, truncate it.
2020-08-30 09:03:34 +02:00
tobi-wan-kenobi
58bbcf2ac5
Merge pull request #693 from izn/tests/network-traffic-module
Add Network Traffic module tests
2020-08-30 04:55:59 +02:00
Thaynã Moretti
22ddcf42bd Improve tests 2020-08-29 21:49:41 -03:00
Thaynã Moretti
dff187252a Fix RECV/SENT start values 2020-08-29 21:09:59 -03:00
Thaynã Moretti
2c8dafec70 Add Network Traffic module tests 2020-08-29 20:17:39 -03:00
tobi-wan-kenobi
905f71fa52 [core] fix broken "sparse" updates
c77f3aa accidentially broke "sparse" updates (i.e. updates that do not
trigger during each update interval).

Introduce a new update parameter, "force", to model the use case "update
everything on SIGUSR1".

fixes #692
2020-08-28 17:14:05 +02:00
tobi-wan-kenobi
362d1a5f6f [modules/contrib/dnf] fix undefined "widget" error
while refactoring, i overlooked that the variable "widget" doesn't exist
anymore.

see #692
2020-08-28 14:03:34 +02:00
tobi-wan-kenobi
db41792afb [dnf] simplify threading
use framework threading to simplify the dnf module

see #692
2020-08-27 20:07:10 +02:00
tobi-wan-kenobi
38e54a7c81
Merge pull request #691 from cristianmiranda/github_remove_error_print_on_exception
[github]: Remove error print on exception catch
2020-08-21 16:50:53 +02:00
Cristian Miranda
9b5477675c [github]: Remove error printing if exception caught
I experienced that when an exception is caught and we print it
I get an ugly error on the whole bar making it unusable. This
fixes that problem.
2020-08-20 10:47:56 -03:00
tobi-wan-kenobi
738a846853 Merge branch 'master' into main 2020-08-11 20:54:23 +02:00
tobi-wan-kenobi
d759ed5051
Merge pull request #690 from joshbarrass/deadbeef-patch
Quotes around the deadbeef string to prevent parsing errors by deadbeef
2020-08-11 06:16:58 +02:00
tobi-wan-kenobi
ad91b1c025
Merge pull request #689 from joshbarrass/usr1
Force status bar to update early using USR1 signal
2020-08-11 06:16:04 +02:00
Joshua Barrass
9e04e0a27b
Quotes around the deadbeef string to prevent parsing errors by deadbeef 2020-08-11 00:54:35 +01:00
Joshua Barrass
c77f3aa3bc
Force update using USR1 signal 2020-08-11 00:45:13 +01:00
tobi-wan-kenobi
b368cb49da
Merge pull request #688 from alexsr/master
Fix wrong usage of asbool in layout-xkb
2020-08-06 16:40:37 +02:00
Alexander Scheid-Rehder
5959d73cde Fix wrong usage of asbool in layout-xkb 2020-08-06 14:49:41 +02:00
tobi-wan-kenobi
f8d035c079
Merge pull request #687 from anlif/main
[modules/contrib/stock] handle urllib request exception
2020-08-01 10:36:23 +02:00
Andreas Lindahl Flåten
5de616ff89 [modules/contrib/stock] handle urllib request exception
Handle exception that is raised when e.g. your network connection is
down.
2020-07-31 15:26:49 +02:00
tobi-wan-kenobi
72d255f0ae [doc] document additional config file parameters
see #678
2020-07-27 07:04:47 +02:00
tobi-wan-kenobi
969378846f
Merge pull request #686 from nayaverdier/main
[xrandr] Add autotoggle behavior to xrandr
2020-07-27 06:55:15 +02:00
Naya Verdier
13a851a636
[xrandr-tests] Test disconnecting excluded active display 2020-07-26 15:52:47 -07:00
Naya Verdier
06afb03807
[xrandr] add tests for dis/connecting without autotoggle 2020-07-26 13:29:14 -07:00
Naya Verdier
06d6739da4
[xrandr] Add autotoggle behavior to xrandr
Also makes i3 subscription events and widget click live-update
2020-07-26 13:21:49 -07:00
tobi-wan-kenobi
3fd8c5aaa0
Merge pull request #684 from nayaverdier/main
[xrandr] unit tests, exclude parameter, and final display safeguard
2020-07-26 11:25:07 +02:00
Naya Verdier
b1adc382aa
[vault] Turns out, <Leave> is triggered when going into a submenu 2020-07-25 12:38:28 -07:00
Naya Verdier
647ad44b31
ModuleNotFoundError incompatible with python 3.4, 3.5 2020-07-25 11:50:13 -07:00
Naya Verdier
22d85d6d1d
[xrandr-test] Catch all exceptions if i3 Subscription fails 2020-07-25 11:33:42 -07:00
Naya Verdier
d05ff39f02
[xrandr] Add unit tests 2020-07-25 10:53:44 -07:00
Naya Verdier
b14eae4d6c
[xrandr] add safeguard to prevent turning off the only display 2020-07-25 10:52:57 -07:00
Naya Verdier
5874850bd5
[xrandr] Fix neighbor functionality when some displays are excluded 2020-07-25 10:52:10 -07:00
Naya Verdier
8f57bb952d
[xrandr] add exclude parameter to ignore certain display prefixes 2020-07-25 08:47:31 -07:00
Naya Verdier
5f2857ad9a
[vault] add leave_menu parameter to auto-close the menu 2020-07-25 08:46:57 -07:00
Naya Verdier
4aff0499f0
[util.popup] Deduplicate code, "close" button only if leave=False 2020-07-25 08:43:26 -07:00
tobi-wan-kenobi
d358790c6a [travis] make unit tests work again 2020-07-21 09:04:50 +02:00
tobi-wan-kenobi
b39dba8867 [travis] remove webbrowser 2020-07-21 09:02:51 +02:00
tobi-wan-kenobi
c692a776b6 [travis] remove tkinter 2020-07-21 09:00:59 +02:00
tobi-wan-kenobi
bee2586ed4 [travis] remove tempfile 2020-07-21 08:58:36 +02:00
tobi-wan-kenobi
632eb0c450 [travis] remove speedtest 2020-07-21 08:55:54 +02:00
tobi-wan-kenobi
6870c3ba84 [travis] removed a couple more modules 2020-07-21 08:53:41 +02:00
tobi-wan-kenobi
993be61eec [travis] remove pygit2 2020-07-21 08:43:08 +02:00
tobi-wan-kenobi
84f2fdd419 [travis] removed random 2020-07-21 08:27:23 +02:00
tobi-wan-kenobi
b7083aacce [travis] remove multiprocessing
doesn't install :(
2020-07-21 08:25:18 +02:00
tobi-wan-kenobi
6c7737cdd5 [travis] remove libvirt-python
requires at least python3.5
2020-07-21 08:23:17 +02:00
tobi-wan-kenobi
550b594c86 [tests] fix some test prerequisites 2020-07-21 08:20:55 +02:00
tobi-wan-kenobi
4fe56fc00d [travis] remove math module 2020-07-21 08:19:51 +02:00
tobi-wan-kenobi
6c9e89627a [travis] fix package name 2020-07-21 08:17:25 +02:00
tobi-wan-kenobi
bf1cae2399 [travis] forgot sudo 2020-07-21 08:15:47 +02:00
tobi-wan-kenobi
463bc9665b [travis] fix python-dbus 2020-07-21 08:14:21 +02:00
tobi-wan-kenobi
a008ce3e58 [travis] add all dependencies
to make all unit tests run, and none skipped (hopefully), add a list of
dependencies
2020-07-21 08:08:22 +02:00
tobi-wan-kenobi
54a2fc3a41 [generate-tests] black 2020-07-20 13:58:46 +02:00
tobi-wan-kenobi
548ccc5e94 [tests] add somewhat experimental import-time tests
add auto-generated tests that check that a given module can be imported,
if all prerequisites are followed.

see #641
2020-07-20 13:58:24 +02:00
tobi-wan-kenobi
9be1331e1b [tests] add placeholder for core tests 2020-07-20 13:17:35 +02:00
tobi-wan-kenobi
dc70527797 [util] add a script to generate basic autotests
Add a small utility that can generate a basic "import this unless you
are missing dependencies" test for all modules.
2020-07-20 13:17:15 +02:00
tobi-wan-kenobi
6025fcd2da [doc] update as per PR #683 2020-07-18 08:23:44 +02:00
tobi-wan-kenobi
7c8ddc9c87 [doc] update docstrings as per PR #683 2020-07-18 08:23:28 +02:00
tobi-wan-kenobi
72966ee37d [modules/{cpu,load,memory}] Add gnome-system-monitor dependency to doc 2020-07-18 08:16:57 +02:00
tobi-wan-kenobi
df29627983 [core/doc] add autogenerated warning to modules.rst 2020-07-18 08:14:23 +02:00
tobi-wan-kenobi
83a4be3bc0 [doc] add module portage_status (plus attribution) 2020-07-12 19:31:28 +02:00
tobi-wan-kenobi
088309df23
Merge pull request #682 from andrewreisner/main
Add simple Gentoo portage status module.
2020-07-12 19:29:48 +02:00
Andrew Reisner
b1bb0fe690
Run formatter. 2020-07-12 11:01:00 -06:00
Andrew Reisner
0785202860
Add simple portage status module.
This adds a simple module to display the status of Gentoo portage
operations by reading its logfile.
2020-07-12 10:52:05 -06:00
tobi-wan-kenobi
5ea2708f45
Merge pull request #681 from bbernhard/messagereceiver
reworked messagereceiver module
2020-07-11 19:33:42 +02:00
Bernhard B
a4a622252b reworked messagereceiver module
* use bumblebee's internal threading capabilities
* various small code improvements (pylint)
2020-07-11 18:07:57 +02:00
tobi-wan-kenobi
2b888325a6 [doc] add messagereceiver (+ attribution) 2020-07-10 20:19:43 +02:00
tobi-wan-kenobi
2d6cedd8ac
Merge pull request #680 from bbernhard/messagereceiver
Messagereceiver
2020-07-10 20:13:24 +02:00
Bernhard B
01cde70e14 improved documentation of messagereceiver module 2020-07-10 17:35:40 +02:00
Bernhard B
adbba9bf9a fixed small bug in messagereceiver
* wrong logging syntax
2020-07-10 17:17:16 +02:00
Bernhard B
0067ce83f0 added new module messagereceiver
* binds to unix sockets and listens for incoming messages. The message
  will then be displayed in the status bar.
2020-07-10 17:11:50 +02:00
tobi-wan-kenobi
72045b2318 [core/config] make configurable module list work
configparser doesn't seem to have direct array support,
so use format.aslist() to get a list of modules

fixes #678
2020-07-09 07:04:45 +02:00
tobi-wan-kenobi
a9f50f1b51 [core/config] add "core" config section
move theme and modules into a "core" config section
2020-07-09 07:04:22 +02:00
tobi-wan-kenobi
057f894d52 [core/config] change preferred theme source
now, it works like this:

- if present, use what's on the CLI
- if not, use what's present in the config
- fallback is "default"

see #679
2020-07-09 06:56:43 +02:00
tobi-wan-kenobi
5c207df6ae
Merge pull request #679 from fenilgandhi/main
Updating config parser
2020-07-09 06:52:53 +02:00
ColdFire
aac2316d74
Updating config parser 2020-07-06 14:20:41 +05:30
tobi-wan-kenobi
6b63b87c42
Merge pull request #677 from andrewreisner/main
Small fix to xrandr module.
2020-07-06 07:00:52 +02:00
Andrew Reisner
edb8eaf410
Small fix to xrandr module. 2020-07-05 12:35:44 -06:00
tobi-wan-kenobi
b4fc88b57e
Merge pull request #675 from xsteadfastx/iw
[modules/nic] Using `iw` to find out whats the SSID name
2020-06-30 20:20:17 +02:00
tobi-wan-kenobi
8f0fdd6b39
Merge pull request #674 from gkeep/media-states-to-actions
[modules/media] Convert icon states to actions
2020-06-30 20:19:32 +02:00
Marvin Steadfast
6d9d325eca [modules/nic] Using iw to find out whats the SSID name 2020-06-30 14:49:43 +02:00
gkeep
7579a08615 Use icon actions instead of states 2020-06-30 13:58:10 +03:00
tobi-wan-kenobi
f75f1a9f26 [modules/spotify] fix layout parameter 2020-06-30 07:20:54 +02:00
tobi-wan-kenobi
227a23fdb5 [core/theme] fix mergeing of iconsets
iconsets should only overwrite parameters that are *not* set in the main
theme file.

fixes #666
2020-06-29 20:07:24 +02:00
tobi-wan-kenobi
e006344dcc [doc] add how to create icon-only widgets
fixes #669
2020-06-29 07:51:35 +02:00
tobi-wan-kenobi
9136ebd321 [core/input] clear previous input registrations
make sure that for a given event (widget/object/module, whatever), only
a *single* input event per button can be registered at one time.

the problem otherwise is with modules that re-register their widgets
with the same IDs (cmus, spotify, etc.): Each time the widget is
re-created (each intervall, typically), it re-registers an input event,
creating an always longer list of callbacks being executed when the
button is clicked (not speaking of the memory leak this introduces).

fixes #668
2020-06-29 07:44:22 +02:00
tobi-wan-kenobi
6839e81342
Merge pull request #667 from gkeep/spotify-improvements
[modules/spotify] Icon improvements
2020-06-29 07:38:04 +02:00
gkeep
134bbbd743 Use icons depending on widget state and icon set 2020-06-29 00:57:01 +03:00
gkeep
810daac16b Add icons for different states 2020-06-29 00:48:09 +03:00
tobi-wan-kenobi
6b09be1993 Revert "[core/theme] Make theme iconsets *not* override settings"
This reverts commit 320bba97d0.
2020-06-28 20:09:55 +02:00
tobi-wan-kenobi
320bba97d0 [core/theme] Make theme iconsets *not* override settings
Make sure that iconsets used as part of a theme do *not* override
anything already existing inside the theme.

Only iconsets that are manually specified can override settings in the
theme now (because those, you typically specify on the CLI).

TODO: Write unit test for this

fixes #666
2020-06-28 20:01:08 +02:00
tobi-wan-kenobi
8f3d48c0e6 [modules/brightness] re-enable reading brightness from ACPI
to enable reading the brightness from ACPF, set the device path and -
other than previously - explicitly enable this by setting the parameter
"brightness.use_acpi" to "true".

fixes #665
2020-06-28 11:00:51 +02:00
tobi-wan-kenobi
81c5e75624 [tests] fix location tests
a previous commit that switched the primary and secondary location
provider broke the unit tests - fix that.
2020-06-28 10:45:22 +02:00
tobi-wan-kenobi
fc8783ee13 [util/cli] fix CLI invokation for sway
in sway/wayland, make sure that the wayland socket is cleared, which
seems to cause issue for some unspecified reason.

also, while at it, improve code so that the environment dict that is
passed in is *not* modified.

fixes #628
2020-06-28 10:44:30 +02:00
tobi-wan-kenobi
350648b0cd [util/location] reverse location providers
seems that ipapi gives better results wrt. location
2020-06-27 15:05:57 +02:00
tobi-wan-kenobi
954d7545e3 [util/format] make temperature metric case insensitive
see #664
2020-06-27 15:02:55 +02:00
tobi-wan-kenobi
1484109fe0 [doc] removing user contributions
as somospocos decided to delete his github account, remove the broken
links.

again, somospocos, many thanks for the ways in which you improved
bumblebee-status!
2020-06-26 09:43:04 +02:00
tobi-wan-kenobi
4925e09995 [modules/speedtest] no autostart, improve icons
do not start a speedtest automatically during startup, and improve the
icons a bit.
2020-06-26 09:40:35 +02:00
tobi-wan-kenobi
4ac8c2ef7a [modules/speedtest] small fix for retrigger 2020-06-25 20:46:13 +02:00
tobi-wan-kenobi
44b3df5827 [modules/speedtest] make speedtest re-triggerable 2020-06-25 20:44:55 +02:00
tobi-wan-kenobi
7a6788dc1c [doc] add speedtest module 2020-06-25 20:35:00 +02:00
tobi-wan-kenobi
72deb7eaf8 [modules] add speed test module 2020-06-25 20:34:35 +02:00
tobi-wan-kenobi
e9b917c214 [doc] update spotify 2020-06-25 20:00:57 +02:00
tobi-wan-kenobi
542d841622 [modules/spotify] add contribution 2020-06-25 20:00:05 +02:00
tobi-wan-kenobi
54f6035e14
Merge pull request #663 from LtPeriwinkle/spotify-buttons-module
Spotify buttons module
2020-06-25 19:56:21 +02:00
LtPeriwinkle
4cbe04f0b0 remove -buttons, move getting song out of update() 2020-06-25 10:53:53 -07:00
LtPeriwinkle
057faa5577 replace original spotify with buttons version 2020-06-25 10:23:45 -07:00
LtPeriwinkle
6a8d830281 util.format.aslist() for layout 2020-06-25 10:20:02 -07:00
Tobias Witek
bdc38e7934 [doc] add shield for bumblebee-status-git AUR package 2020-06-25 15:43:25 +02:00
LtPeriwinkle
4b54d1981c fix grammar 2020-06-24 13:53:02 -07:00
LtPeriwinkle
7215a11ffe black -t py34 2020-06-24 12:22:03 -07:00
LtPeriwinkle
e1a9782458 modify comments 2020-06-24 12:18:28 -07:00
LtPeriwinkle
0c32d13e6f fix playback status 2020-06-24 12:15:32 -07:00
LtPeriwinkle
2eee4c390c put text on buttons 2020-06-24 12:14:26 -07:00
Tobias Witek
92ab1a3e00 [util/cli] make sure language is set to "C"
to make parsing of CLI output reliable (i.e. always in english), set
LC_ALL accordingly.

see #662
2020-06-24 07:47:31 +02:00
Tobias Witek
582c828deb [core] guard against concurrent updates
when a "regular" update (once per interval) and a input-triggered update
(e.g. mouse click on a widget) collide, this can cause the theme colors
to be interleaved wrongly.

fixes #661
2020-06-23 20:20:36 +02:00
Tobias Witek
441e7d5041 [core] fix minimize for all modules (nic, traffic, etc.)
make it possible to toggle the display state of a widget between
"displayed" and "minimized" also for modules that re-create their
widgets during each iteration.

see #661
2020-06-23 20:03:17 +02:00
Tobias Witek
34dadadf90 [core] re-enable minimize of widgets
by default, allow toggling the minimized state of a widget via the
middle mouse and draw a single unicode char instead of the actual
widget, maintaining all states.

fixes #661
2020-06-23 15:51:14 +02:00
tobi-wan-kenobi
45d8ed23c9
Merge pull request #660 from thannaske/main
Do not execute iwgetid if the interface is recognized as tunnel
2020-06-22 11:43:16 +02:00
tobias.hannaske
3558176044 Do not execute iwgetid if the interface is recognized as tunnel 2020-06-22 10:33:49 +02:00
LtPeriwinkle
ca62f68906 create + map widgets for buttons 2020-06-21 12:31:11 -07:00
tobi-wan-kenobi
df9a1f9a4f
Merge pull request #658 from es80/shell-fix
fix a regression for shell subprocess
2020-06-21 19:42:37 +02:00
LtPeriwinkle
f67ef9d64a add spotify-buttons 2020-06-21 10:28:37 -07:00
es80
e17ef8614f
fix a regression for shell subprocess 2020-06-21 17:56:52 +01:00
Tobias Witek
79fb28512f [tests] black -t py34 2020-06-20 15:11:53 +02:00
Tobias Witek
e9e67ae375 [tests/mpd] fix python3.5 for real now, hopefully 2020-06-20 15:07:16 +02:00
Tobias Witek
b22747818b [tests] fix tests for python3.5 2020-06-20 15:04:10 +02:00
Tobias Witek
a56b3db813 [tests] fix store tests 2020-06-20 14:58:49 +02:00
Tobias Witek
aac604f94c [travis] add missing dependency pytest-mock 2020-06-20 14:55:21 +02:00
Tobias Witek
39fa7788b4 [tests] switch to pytest 2020-06-20 14:53:44 +02:00
Tobias Witek
b2e92d816d [tests] add pytest for core.output
see #642
2020-06-20 14:51:49 +02:00
Tobias Witek
ef6ddad256 [tests] add pytest for core.theme
see #642
2020-06-20 14:32:05 +02:00
Tobias Witek
d7c016ba40 [tests] add pytest for core.widget
see #642
2020-06-20 13:55:19 +02:00
Tobias Witek
542d235c1a [modules/traffic] ignore all errors during update
to investigate whether the assumption that a stray exception causes
issues is correct, temporarily catch all errors.

see #655
2020-06-20 13:52:55 +02:00
Tobias Witek
3beb86bb38 [pytests/mpd] ensure that mpc is called 2020-06-20 13:38:00 +02:00
Tobias Witek
d7370307f2 [pytests/mpd] provide alternative host check
see #641
2020-06-20 13:36:21 +02:00
Tobias Witek
74de020980 [tests] add pytest for core.module
see #642
2020-06-19 20:13:33 +02:00
Tobias Witek
381869ef65 [doc] fix misleading (=wrong) quote usage 2020-06-19 19:50:58 +02:00
tobi-wan-kenobi
a1d2e1d8f9
Merge pull request #657 from rosalogia/main
More tests for mpd module
2020-06-19 19:48:37 +02:00
Ambika Nair
6271a0b91d Added mpd module tests for update and layout 2020-06-19 06:35:44 -04:00
Ambika Nair
52f5315ec5 Added more tests for mpd module 2020-06-19 06:12:30 -04:00
tobi-wan-kenobi
639c373578
Merge pull request #656 from rosalogia/main
Added super simple test for mpd module
2020-06-19 12:02:31 +02:00
Ambika Nair
f4a5d05c1e Got state function test for shuffle working 2020-06-19 05:00:56 -04:00
Ambika Nair
0574775995 Added super simple test for mpd module 2020-06-19 04:13:52 -04:00
Tobias Witek
ea1e1694bf [tests] add pytest for core.input
see #642
2020-06-19 10:10:02 +02:00
Tobias Witek
53258a9ffd [tests] add pytest for util.store
see #642
2020-06-19 09:48:14 +02:00
Tobias Witek
08240c2e82 [pytests/test_kernel] apply black -t py34 2020-06-19 09:35:00 +02:00
Tobias Witek
9cc26c8a95 [tests] add pytest for util.location
see #642
2020-06-19 09:34:21 +02:00
Tobias Witek
8258ff1bab [doc] rename master -> main 2020-06-19 09:16:29 +02:00
tobi-wan-kenobi
e4520d19bf
Merge pull request #653 from rosalogia/master
Replicated kernel module unittest with pytest
2020-06-19 09:08:46 +02:00
tobi-wan-kenobi
88a3350b78
Merge pull request #654 from rosalogia/patch-1
Fixed minor typo in unit testing guidelines
2020-06-19 09:06:56 +02:00
Ambika Nair
0bdc019a0d
Fixed minor typo in unit testing guidelines 2020-06-19 03:04:41 -04:00
Ambika Nair
19c3975301 Style Fix: changed double quotes to single quotes 2020-06-18 17:27:28 -04:00
Ambika Nair
900988e5ce Replicated kernel module unittest with pytest 2020-06-18 17:22:00 -04:00
Tobias Witek
3461272611 [tests] add pytest for util.format
see #642
2020-06-18 20:18:40 +02:00
Tobias Witek
058089d453 [tests] add pytest for util.cli
see #642
2020-06-18 20:01:45 +02:00
Tobias Witek
b9754a5b3e [docs] update 2020-06-18 07:14:10 +02:00
tobi-wan-kenobi
e139c4211c [doc/dev] module: fix misleading text
fixes #651
2020-06-10 20:25:05 +02:00
tobi-wan-kenobi
1ebbfbae14 [doc] add dev documentation on widget states
fixes #651
2020-06-09 20:45:54 +02:00
tobi-wan-kenobi
16269ff01e [core/input] add variables to custom cli calls
when specifying event bindings via CLI (e.g. disk.left-click=thunar),
allow for variables ({instance} and {name}, in particular).

fixes #650
2020-06-09 20:29:51 +02:00
tobi-wan-kenobi
60cdbab76e [tests] add pytest for algorithm
see #642
2020-06-07 16:15:18 +02:00
tobi-wan-kenobi
a394469c0f
Merge pull request #646 from smitajit/master
[module] playerctl to displays information about the current song in …
2020-06-06 16:01:05 +02:00
tobi-wan-kenobi
ba82f5a50f [core/theme] Fix loading of iconsets
* First, make iconsets override anything already present in the "base"
configuration
* Second, make sure that CLI provided iconsets have higher priority than
"built-in" ones

see #648
2020-06-06 15:59:19 +02:00
smitajit
2dff5e2de7 migrated playerctl script to new bumblebee_status APIs 2020-06-06 14:39:50 +02:00
tobi-wan-kenobi
034e8eefb9 [core/theme] make iconsets take precedence over "main" theme
see #648
2020-06-05 20:10:50 +02:00
tobi-wan-kenobi
cb9a60668a [core/theme] Fix detection of "best matching theme"
Previous code accepted the "first" hit in a theme - particularly, if a
module is called "A" and a *different* module "B" uses "A" as state, a
widget of module B with state A would be themed as *module* A, wrongly.

Essentially, made sure that the last (most specific) themeing "wins".

fixes #647
2020-06-04 20:56:31 +02:00
tobi-wan-kenobi
747e67e1be [bumblebee-status] add python 2.x warning
explicitly report that Python 2.x is not supported

see #645
2020-06-04 20:30:48 +02:00
tobi-wan-kenobi
ffae9c2e26 [docs] add version badges for AUR and PyPI 2020-06-04 08:04:11 +02:00
tobi-wan-kenobi
b7723c7fa3 [doc] re-add user contribution section
this was accidentally removed during the merge :(
2020-06-04 07:57:53 +02:00
Smitajit Biswal
4912fcc57b [module] playerctl to displays information about the current song in vlc, audacious, bmp, xmms2, spotify and others 2020-06-03 21:50:36 +02:00
tobi-wan-kenobi
19fa783997 [tests] add pytest for decorators 2020-06-03 20:27:24 +02:00
tobi-wan-kenobi
23d7222576 [core/theme] take alias into consideration when themeing
see #643
2020-06-03 07:59:09 +02:00
tobi-wan-kenobi
20866ca7c0 [modules/arch-update] perform update in background 2020-06-03 07:51:44 +02:00
tobi-wan-kenobi
cb30b7175d [tests] update test_config for pytests
see #642
2020-06-02 20:34:49 +02:00
tobi-wan-kenobi
800d603c1f
Merge pull request #644 from ammgws/patch-1
contrib/apt: amend typo
2020-06-02 20:15:32 +02:00
tobi-wan-kenobi
e73b361f62 [tests] add pytest for config
see #642
2020-06-02 20:14:06 +02:00
tobi-wan-kenobi
5e40dfb28a [all] small fixed picked up by pytest
- unicode stuff
- make all regexps regex strings
2020-06-02 20:13:39 +02:00
Jason
cd0d97791e
contrib/apt: amend typo 2020-06-02 23:20:20 +09:00
tobi-wan-kenobi
9cadcee844 [core/events] simplify args/kwargs detection 2020-06-01 11:35:26 +02:00
tobi-wan-kenobi
86fb4c38ea [tests] add pytest for events
see #642
2020-06-01 11:35:21 +02:00
tobi-wan-kenobi
d38a21983c [modules/ping] Use framework background update functionality
see #640
2020-05-30 17:25:47 +02:00
tobi-wan-kenobi
547874dafd [core/module] allow modules to perform updates in background
a module can now set `self.background = True` in its `__init__()` method
to make sure its update method is invoked in a separate thread.

also, do a PoC implementation of this for the github module.

TODO: add this to dev doc

see #640
2020-05-30 17:22:02 +02:00
tobi-wan-kenobi
a13898cbae [bumblebee-status] fix double "version" line 2020-05-30 17:20:13 +02:00
tobi-wan-kenobi
55121c86d4 [util/popup] add generic "close" on root menu
add a "close" entry for the root menu of all popup menus (if they are
not automatically destroyed when leaving the menu).

fixes #633
2020-05-30 15:19:31 +02:00
tobi-wan-kenobi
cb13fbf917 [doc/README] update links to point to readthedocs 2020-05-30 10:08:41 +02:00
tobi-wan-kenobi
1c04068f89 [util/popup] replace mainloop with custom loop
add a custom event loop for popups that closes the menu on mouse button
release events and - most importantly - propagates this event back to
any parent menu.

fixes #633
2020-05-30 10:02:12 +02:00
tobi-wan-kenobi
b9a30ae235 [doc] update module doc 2020-05-29 11:12:40 +02:00
tobi-wan-kenobi
68b1156d15 [modules/github] Add updated screenshot 2020-05-29 11:12:11 +02:00
tobi-wan-kenobi
7f7f464193 Merge remote-tracking branch 'origin/development' 2020-05-29 11:11:56 +02:00
tobi-wan-kenobi
590d92102e
Merge pull request #637 from cristianmiranda/github2
[modules] GitHub unread notifications module with modular reason support
2020-05-29 11:10:36 +02:00
tobi-wan-kenobi
c2ec379c52
Merge pull request #638 from ardadem/pr-revert
Revert 'New amixer module with input support' for icons
2020-05-29 11:07:40 +02:00
Arda Demir
13717d096c Revert 'New amixer module with input support' for icons 2020-05-29 01:21:58 +03:00
Cristian Miranda
c2d869278a [modules/github] - New module supporting unread notifications count by reason with backward compatibility 2020-05-28 17:45:57 -03:00
tobi-wan-kenobi
4f2b283a55 [modules/vault] Emit leave events for submenus
this seems to at least partially fix #633
2020-05-28 21:06:22 +02:00
tobi-wan-kenobi
ebd98663c6 [modules/vault] fix copy of wrong password
- fix bad errors (missing "show" in pass command)
- pass in whole environment (otherwise clipboard doesn't work)
- generate lambda to avoid late binding

fixes #593
2020-05-28 21:05:58 +02:00
Cristian Miranda
f7e37ef7d7 [modules/github] - Removed legacy module 2020-05-28 15:59:39 -03:00
tobi-wan-kenobi
8cdc84871f [modules] move amixer2 to amixer 2020-05-28 08:23:47 +02:00
tobi-wan-kenobi
006a1d05a9
Merge pull request #635 from ardadem/pr-amixer2
[modules] New amixer module with input support
2020-05-28 08:20:28 +02:00
tobi-wan-kenobi
caf2fea04b [modules/battery] gracefully handle "no batteries" case
if no batteries are found, assume the machine is running on AC.

fixes #636
2020-05-28 08:17:35 +02:00
Arda Demir
3df0b7047f [modules] New amixer module with input support 2020-05-26 18:23:58 +03:00
tobi-wan-kenobi
be008c0a6f [doc] update README 2020-05-26 08:53:14 +02:00
Tobias Witek
f23566f5d0 [modules/arch-update] fix log error 2020-05-26 08:15:15 +02:00
Tobias Witek
f1ce5e162a [theme] load custom iconset first
the "merge" algorithm only fills in missing elements - i.e. the most
important pieces of a data structure must be filled in first. since the
iconset specified on the CLI takes precedence over anything present in
the config, load the CLI-provided iconset *first*.

hopefully fixes #634
2020-05-26 07:56:04 +02:00
tobi-wan-kenobi
6e7340fcb1 [codeclimate] fix wrong config 2020-05-25 07:38:25 +02:00
tobi-wan-kenobi
f11cea8ab4 [codeclimate] enable duplication checks 2020-05-25 07:35:02 +02:00
tobi-wan-kenobi
47177a802f [codeclimate] update config 2020-05-25 07:33:43 +02:00
tobi-wan-kenobi
6af724d1d5 [codeclimate] update config 2020-05-24 17:06:46 +02:00
tobi-wan-kenobi
1a69213a78 [codeclimate] add some excludes 2020-05-24 13:05:53 +02:00
tobi-wan-kenobi
3c73c4d343 [tests] coverage: include files that are not covered at all 2020-05-24 13:05:03 +02:00
tobi-wan-kenobi
3d14931cb7 [docs] add new module 2020-05-23 16:55:24 +02:00
tobi-wan-kenobi
20f9ac3ae0 [modules/arandr] add contribution 2020-05-23 16:53:10 +02:00
tobi-wan-kenobi
d393da47ed Merge remote-tracking branch 'origin/development' 2020-05-23 16:52:34 +02:00
tobi-wan-kenobi
1158c57f85
Merge pull request #632 from zerorust/arandr
Adds a new contributed module `arandr` to complement `xrandr` with fast layout access
2020-05-23 16:52:05 +02:00
Zero Rust
f944d7c489 Merge branch 'development' into arandr 2020-05-23 08:03:34 -04:00
Zero Rust
8c8fef61eb linting 2020-05-23 07:58:50 -04:00
Zero Rust
3921bab32e added display toggling to the arandr module 2020-05-23 07:53:28 -04:00
Zero Rust
90fbc249af cleaner way to activate a layout 2020-05-23 07:28:00 -04:00
Zero Rust
ffd04e9710 full menu and command for arandr scripts 2020-05-23 07:13:47 -04:00
Zero Rust
de00f9bdce getting display and layout state for populating menu 2020-05-22 23:53:38 -04:00
Zero Rust
1b34993fa9 adding separator addition to popup 2020-05-22 23:53:21 -04:00
Zero Rust
f5c9272291 first go at popup functionality for arandr 2020-05-22 22:44:38 -04:00
tobi-wan-kenobi
5dec4fd371 [docs] add psutil dependency to cpu module 2020-05-21 13:10:53 +02:00
tobi-wan-kenobi
9a380d584f [README] fix link that accidentially was an image 2020-05-21 13:09:12 +02:00
tobi-wan-kenobi
8552240568 [README] update links 2020-05-21 08:52:20 +02:00
tobi-wan-kenobi
4d1210a207 [docs] FAQ: update commit ID for stable AUR 2020-05-21 08:49:24 +02:00
tobi-wan-kenobi
590ea58196 [readme] switch to master branch 2020-05-20 18:26:13 +02:00
tobi-wan-kenobi
ec0b3c377b [doc] update FAQ 2020-05-20 17:53:42 +02:00
tobi-wan-kenobi
239e457d93 [travis] remove python 3.8, because it fails strangely 2020-05-20 17:47:02 +02:00
tobi-wan-kenobi
83e65e77c4 [travis] try to fix tests 2020-05-20 17:43:17 +02:00
tobi-wan-kenobi
fd43e6cdd7 [travis] add codeclimate 2020-05-20 17:35:14 +02:00
tobi-wan-kenobi
093da246b6 Merge branch 'master' into development 2020-05-20 17:33:07 +02:00
tobi-wan-kenobi
0dc4e6c709 [docs] add downgrade instructions 2020-05-20 17:28:41 +02:00
tobi-wan-kenobi
922575fcda [core/widget] make arbitrary modules scrollable
Use a boolean parameter "scrolling" that's applied generically to
modules to allow arbitrary modules to scroll.

see #606

TODO: Add to documentation
2020-05-18 13:13:06 +02:00
tobi-wan-kenobi
e359c75603 [core/widget] remove module from widget initializer 2020-05-18 12:59:47 +02:00
Zero Rust
2fcd277cf9 Merge branch 'development' into arandr 2020-05-18 06:58:07 -04:00
tobi-wan-kenobi
fbbd95b7a8 [modules/network_traffic] Minor refactoring 2020-05-18 12:56:41 +02:00
tobi-wan-kenobi
172781d2c4
Merge pull request #631 from zerorust/fix_network_traffic
Fix network_traffic mobule on development
2020-05-17 14:15:00 +02:00
Zero Rust
f5d296df80 adding first version of arandr module to contrib 2020-05-17 08:03:03 -04:00
Zero Rust
e339158aed updating all icon sets for arandr icon 2020-05-17 07:56:58 -04:00
Zero Rust
a16536ad3c adding prefix icon to arandr 2020-05-17 07:52:32 -04:00
Zero Rust
6d89cef600 cleaning up to match style 2020-05-17 07:16:33 -04:00
tobi-wan-kenobi
1355a7e5d3 [core/decorators] add docstrings 2020-05-16 15:28:15 +02:00
tobi-wan-kenobi
a697500491 [core] add debugging around click events
log commandline outputs and errors. allow input handlers to be
configured as "waiting" for debugging purposes.

see #628
2020-05-16 15:16:23 +02:00
Zero Rust
f2d6f2f319 moving the module setter to after module init 2020-05-16 07:24:14 -04:00
tobi-wan-kenobi
02a80840a1 [core] more black'ing 2020-05-16 12:00:55 +02:00
tobi-wan-kenobi
ef4d87af8e [CI] update CI config 2020-05-16 12:00:48 +02:00
tobi-wan-kenobi
7584366adc [tests] add more tests 2020-05-16 11:57:27 +02:00
tobi-wan-kenobi
eea3c758de [core/input] Invoke commands in a subshell
add shell capability to util.cli and make sure that the input module
uses that to reliably spawn whatever command the user wants to run.

see #628
2020-05-16 11:43:43 +02:00
tobi-wan-kenobi
06fa453d71
Merge pull request #630 from TheEdgeOfRage/development
Handle n+1 characters in the scrolling decorator
2020-05-16 11:43:29 +02:00
Pavle Portic
a8df4a5f9d
Handle n+1 characters in the scrolling decorator 2020-05-15 20:31:37 +02:00
tobi-wan-kenobi
3bb2fb8247 [core/module] add missing registration of input events via CLI
see #628
2020-05-15 19:59:13 +02:00
tobi-wan-kenobi
bfe5d8f493 [module/battery] exception typo
see #628
2020-05-15 19:48:10 +02:00
tobi-wan-kenobi
835ed070e5 [core/module] Add docstrings 2020-05-15 10:22:10 +02:00
tobi-wan-kenobi
1b53e7ecf2 [util/popup] add docstrings 2020-05-15 10:07:07 +02:00
tobi-wan-kenobi
bc382e67d3 [general] black code some more 2020-05-14 20:36:01 +02:00
tobi-wan-kenobi
526560ea54 [core/decorators] use difflib to make scrolling reset detection fuzzy
when scrolling text that is subject to *slight* changes (e.g. a song
that contains the current position within the song), allow for slight
variations in the displayed text.

fixes #629
2020-05-14 20:35:09 +02:00
tobi-wan-kenobi
0238d59f8b [docs] add night-powerline 2020-05-12 07:14:18 +02:00
tobi-wan-kenobi
bb24d4371c
Merge pull request #627 from LtPeriwinkle/night-powerline-theme
Night powerline theme
2020-05-12 07:12:40 +02:00
LtPeriwinkle
583d76f220 Create night-powerline.png
screenshot for night-powerline theme
2020-05-11 16:03:22 -07:00
LtPeriwinkle
0123bb83f9 Create night-powerline.json
similar to greyish-powerline but made the good/warning/bad colors less obtrusive and added a third cycle color
2020-05-11 15:54:54 -07:00
LtPeriwinkle
a964abaa11 Revert "Create night-powerline.json"
This reverts commit eef2ab3141.
2020-05-11 15:50:52 -07:00
LtPeriwinkle
eef2ab3141 Create night-powerline.json
similar to grayish theme, but muted the good/warning/bad colors so they are less distracting and added a third cycle
2020-05-11 15:49:45 -07:00
tobi-wan-kenobi
3fdd2d2be6 [modules/arch-update] Gracefully handle exit code 2
fixes #624
2020-05-11 15:10:20 +02:00
tobi-wan-kenobi
f2aa006130 [github] re-enable pip publish workflow 2020-05-10 13:26:52 +02:00
tobi-wan-kenobi
b4f8870a95 [doc] update API docs 2020-05-10 13:25:13 +02:00
tobi-wan-kenobi
8ca036bc4c [util/graph] Add API documentation 2020-05-10 13:15:47 +02:00
tobi-wan-kenobi
106bb9e8c3 [util/graph] Remove docstrings in prep for more complete documentation 2020-05-10 13:07:08 +02:00
tobi-wan-kenobi
560083ce0c [docs] Update API doc to new package structure 2020-05-10 13:05:20 +02:00
tobi-wan-kenobi
f78981b226 [packaging/pip] Add test_suite
Hope it works like this...
2020-05-10 13:01:40 +02:00
tobi-wan-kenobi
b803f385a1 [tests] Fix failing tests 2020-05-10 12:56:30 +02:00
tobi-wan-kenobi
c560c07890 [packaging/pip] Make binary utility tools functional 2020-05-10 12:52:20 +02:00
tobi-wan-kenobi
92577e7d26 [all] black code :) 2020-05-09 21:24:28 +02:00
tobi-wan-kenobi
320827d577 [core] restructure to allow PIP packaging
OK - so I have to admit I *hate* the fact that PIP seems to require a
subdirectory named like the library.

But since the PIP package is something really nifty to have (thanks to
@tony again!!!), I updated the codebase to hopefully conform with what
PIP expects. Testruns so far look promising...
2020-05-09 21:22:00 +02:00
tobi-wan-kenobi
1d25be2059 [doc] update module documentation 2020-05-09 15:31:25 +02:00
tobi-wan-kenobi
3ce8e2f278 [doc] Add content to "writing a theme" 2020-05-09 15:16:25 +02:00
tobi-wan-kenobi
b3200abb23 [doc] update notes 2020-05-09 10:58:49 +02:00
tobi-wan-kenobi
99e3571eee [modules] remove setting widgets via "self.widgets(new list)"
use self.add_widget() for all modules
2020-05-09 10:57:44 +02:00
tobi-wan-kenobi
85ac953111 [modules/arch-update] Better handling of errors in checkupdates
see #624
2020-05-09 10:22:26 +02:00
tobi-wan-kenobi
a8f4d3f9c6 [modules/octoprint] Add missing contribution 2020-05-09 10:19:11 +02:00
tobi-wan-kenobi
9ef826b5fd [core] fix tests 2020-05-08 21:01:45 +02:00
tobi-wan-kenobi
dd4294ecfa [modules] move some modules into contrib, as appropriate 2020-05-08 20:59:38 +02:00
tobi-wan-kenobi
21049b9b03 [modules] Add contributions to (hopefully!) all modules 2020-05-08 20:58:35 +02:00
tobi-wan-kenobi
5dcf9f9c9d [modules/arch-update] fix typo 2020-05-08 16:24:10 +02:00
tobi-wan-kenobi
3033c6646c [util/xrandr] Add binaries for rewriting i3 config 2020-05-08 16:18:32 +02:00
tobi-wan-kenobi
41f8f31dd8 [modules/redshift] Use boolean format, rename parameter 2020-05-08 16:14:07 +02:00
tobi-wan-kenobi
7d8b659a55 [modules/smartstatus] Use common boolean format 2020-05-08 16:12:34 +02:00
tobi-wan-kenobi
7d7324f6ed [modules/docker_ps] upper -> lowercase (personal preference) 2020-05-08 16:10:27 +02:00
tobi-wan-kenobi
e4f7019f5b [modules/arch-update] log exceptions 2020-05-08 16:09:51 +02:00
tobi-wan-kenobi
fde51b3f79
Merge pull request #625 from martindoublem/martin-development
Improved three modules in showing more compacted info with new parameters
2020-05-08 16:08:54 +02:00
tobi-wan-kenobi
e7eef7f028 [modules/arch-update] catch more error
see #624
2020-05-07 20:10:58 +02:00
tobi-wan-kenobi
b3e4c7503b [doc] add note about module RST documentation 2020-05-07 20:07:02 +02:00
Martin
879d00775b Shorter off message for docker_ps 2020-05-07 07:06:27 +02:00
Martin
478ee68f89 Improved reshdift 2020-05-07 06:44:27 +02:00
Martin
45d893aaba improved smartstatus 2020-05-07 06:18:17 +02:00
tobi-wan-kenobi
f49300e568 [doc] update docs as per #606 2020-05-06 12:57:38 +02:00
tobi-wan-kenobi
806258c2ed [themes] add nord-powerline 2020-05-06 12:57:29 +02:00
tobi-wan-kenobi
60a2d82e1c [modules] rename __pulseaudio to pulseaudio 2020-05-06 12:57:16 +02:00
tobi-wan-kenobi
5e7624b619 [README] Add doc status 2020-05-05 21:01:41 +02:00
tobi-wan-kenobi
39f70a9fd1 [themes] Update default-separators
see #623
2020-05-05 21:00:16 +02:00
tobi-wan-kenobi
5012e76d07 [doc] small updates 2020-05-05 20:56:27 +02:00
tobi-wan-kenobi
c25583236b [doc] fix link 2020-05-05 20:54:47 +02:00
tobi-wan-kenobi
79a52c9f42 [doc] Update list of modules 2020-05-05 20:53:04 +02:00
tobi-wan-kenobi
8f357e4402 [doc] set master doc 2020-05-05 20:45:31 +02:00
tobi-wan-kenobi
1c5a785003 [doc] add code-block type 2020-05-05 20:43:10 +02:00
tobi-wan-kenobi
d2f796baa9 [util/store] add docstrings 2020-05-05 20:37:00 +02:00
tobi-wan-kenobi
5da0819610 [doc] Update list of modules to produce RST output
Also, adapt some module docstrings to conform with RST
2020-05-05 20:33:27 +02:00
tobi-wan-kenobi
456a3ec4a4 [util] add docstrings 2020-05-05 19:55:37 +02:00
tobi-wan-kenobi
0b0780b213 [docs] some more updates 2020-05-05 19:55:19 +02:00
tobi-wan-kenobi
0b7f61b62e [doc] Add API documentation
for reference:

sphinx-apidoc -o src/ ../ ../bumblebee-status ../bumblebee-ctl ../tests
2020-05-04 20:27:11 +02:00
tobi-wan-kenobi
7aa201dd30 [all] re-apply black 2020-05-04 20:12:02 +02:00
tobi-wan-kenobi
98dd8ca5f7 [core/decorators] reset scrolling if content changes
see #622
2020-05-04 20:11:10 +02:00
tobi-wan-kenobi
49c1465b65 [docs] significant revamp - add sphinx support
make it possible to compile documentation via sphinx - main change is to
use RST instead of MarkDown for most files.
2020-05-04 20:02:48 +02:00
tobi-wan-kenobi
fdd239d234 [doc] Update feature doc (include config files) 2020-05-03 19:51:10 +02:00
tobi-wan-kenobi
c22f803caf [doc] update README 2020-05-03 19:45:57 +02:00
tobi-wan-kenobi
cec1d3cbda [modules/octoprint] small refactoring
see PR #621
2020-05-03 19:39:40 +02:00
tobi-wan-kenobi
05b3fcf863 [doc/modules] Add more space between text and screenshot 2020-05-03 16:59:38 +02:00
tobi-wan-kenobi
2f98240eed [doc/modules] small bugfix for display of images 2020-05-03 16:57:22 +02:00
tobi-wan-kenobi
577dd9b438 [doc/modules] Add screenshots 2020-05-03 16:56:23 +02:00
tobi-wan-kenobi
0a7212e10d [doc/modules] Add octoprint 2020-05-03 16:52:41 +02:00
tobi-wan-kenobi
e25d878097 [doc] updated README.md 2020-05-03 16:51:52 +02:00
tobi-wan-kenobi
e612ec11ac [doc] minor restructuring 2020-05-03 16:47:53 +02:00
tobi-wan-kenobi
3c2ed218cf [doc] Add some more documentation 2020-05-03 11:42:14 +02:00
tobi-wan-kenobi
5fd9501ed2 [core/output] Add parameter "align"
Also, add some documentation
2020-05-03 11:41:30 +02:00
tobi-wan-kenobi
6fc8042899 [main] always show errors in status line
try to completely get rid of the occasional ("exited with status N") in
the status bar by catching exceptions and showing a very very minimal
error message.
2020-05-03 11:32:38 +02:00
tobi-wan-kenobi
421d365d8d [doc] slightly extend HOWTO_MODULE 2020-05-03 11:21:11 +02:00
tobi-wan-kenobi
368d5de171 [doc] some updates 2020-05-03 11:19:21 +02:00
tobi-wan-kenobi
30c1f712a6 [formatting] reformat using "black -t py34"
getting rid of thinking about consistent formatting...
2020-05-03 11:15:52 +02:00
tobi-wan-kenobi
fa98bcbdd1 [doc] update notes 2020-05-02 20:27:55 +02:00
tobi-wan-kenobi
a34ab6cc20
Merge pull request #620 from bbernhard/octoprint
Octoprint
2020-05-02 19:53:25 +02:00
Bernhard B
9dd0eb58eb [modules/octoprint] changed refresh interval to 5 secs. 2020-05-02 19:03:17 +02:00
Bernhard B
5402a0b16f [modules/octoprint] added new octoprint module
* shows printer bed + tools temperature in status bar
* opens popup on left click to show temperature details and webcam
stream (if enabled)
2020-05-02 18:56:53 +02:00
tobi-wan-kenobi
4bd23950c6 [tests] add core/widget tests 2020-05-02 14:14:12 +02:00
tobi-wan-kenobi
5ee97dbb09 [tests] add more core/config tests 2020-05-02 14:09:30 +02:00
tobi-wan-kenobi
b3d70c61f1 [core/config] ast is part of the PSL 2020-05-02 13:57:13 +02:00
tobi-wan-kenobi
ba24ba13db [core/input] add default workspace wrap bindings (mouse wheel up/down) 2020-05-02 13:54:45 +02:00
tobi-wan-kenobi
d0bb0f9b7a [core/config] Add support for configuration files
Load configuration files from a number of places and, if they exist, use
the settings there as baseline settings that can be overwritten by more
specific source (the modules themselves and the CLI `--parameter`
option).

TODO: document this
2020-05-02 13:43:17 +02:00
tobi-wan-kenobi
36faceef68 [doc/THEMES] make links relative 2020-05-02 13:35:23 +02:00
tobi-wan-kenobi
eb63318ca5 [doc] Add iceberg-contrast theme (and screenshots) 2020-05-02 13:34:04 +02:00
tobi-wan-kenobi
bac38ece5d [core/module] only log an error when a module cannot be loaded
improve logging of import errors

see #606
2020-05-02 13:29:20 +02:00
tobi-wan-kenobi
9b63345b44 [modules/nvidiagpu] Fix double "not found" check
refactor error: it should be *either* "not found" in string *OR*
string.startswith("something"), not both.

fixes #617
2020-05-02 09:49:28 +02:00
tobi-wan-kenobi
4838cf862f [doc] update notes 2020-05-02 09:42:36 +02:00
tobi-wan-kenobi
af80f4c620 [ctl] re-add bumblebee-ctl
add a utility that allows the user to programmatically trigger mouse
events for i3
2020-05-02 09:41:41 +02:00
tobi-wan-kenobi
972ada0697 [core/widget] allow to override widget IDs
to uniquely identify a widget, allow the user to specify an ID on the
CLI

TODO: document this
2020-05-02 09:41:08 +02:00
tobi-wan-kenobi
8e71201030 [core/module] name of a widget now equals the module name
to make it easier to trigger events programmatically, the IDs for
modules are not auto-generated UUIDs anymore, but rather the module name
(which has to be unique, anyhow).
2020-05-02 09:40:09 +02:00
tobi-wan-kenobi
a8ab4f9509 [core/output] allow specification of per-widget modified
make it possible to specify the minimum width of all widgets via:

`-m cpu -p cpu.theme.minwidth=20`

or

`-m cpu -p cpu.theme.minwidth=aaaaaa`

multiple widgets are comma-separated (missing entries will have no
minwidth)

`-m sensors2 -p sensors2.theme.minwidth=10,20,10`

see #606

TODO add to documentation
2020-05-01 20:25:10 +02:00
tobi-wan-kenobi
87915a34e9 [core] get output for list of themes/modules 2020-05-01 20:15:39 +02:00
tobi-wan-kenobi
2ab575d190 [core] do not scroll errors
to make errors easier to diagnose, do not scroll them, but instead fully
show them.

also, re-introduce the supplementary fields for the dwm bridge, kudos to
@somospocos
2020-05-01 15:34:29 +02:00
tobi-wan-kenobi
4daae88a7c [core] Add right-to-left output 2020-05-01 15:34:29 +02:00
tobi-wan-kenobi
f087dbcee4
Merge pull request #616 from martindoublem/bis
Added a new theme and missing icons
2020-05-01 15:00:44 +02:00
tobi-wan-kenobi
802f6c0342
Merge pull request #615 from martindoublem/development
[modules/smartstatus] Add module reference to widgets
2020-05-01 15:00:06 +02:00
Martin
e7466292d2 Added a new theme and missing icons 2020-05-01 13:41:28 +02:00
Bernhard B
cb0c987531 [modules/vpn] fixed typo in vpn module
* due to a typo in the vpn module it wasn't possible anymore to
disconnect an established vpn connection.
2020-05-01 13:41:28 +02:00
Martin
9a0505f5f2 [modules/smartstatus] Add module reference to widgets
fixes related to #612
2020-05-01 13:23:12 +02:00
tobi-wan-kenobi
aee4b37fb5
Merge pull request #613 from bbernhard/fix_vpn_disconnect
[modules/vpn] fixed typo in vpn module
2020-05-01 10:05:44 +02:00
tobi-wan-kenobi
2a663deb7b [core/modules] Add widget producer for module
simplifies and debugs widget creation
2020-05-01 10:03:50 +02:00
tobi-wan-kenobi
859a387676 [modules/rss] update to latest API 2020-05-01 09:54:21 +02:00
tobi-wan-kenobi
9c4c712931 [modules] re-enable rss 2020-05-01 09:46:21 +02:00
tobi-wan-kenobi
7956b28463 [doc] update notes 2020-05-01 09:46:14 +02:00
tobi-wan-kenobi
13626f6b3e [modules/sensors2] Add module reference to widgets
fixes #612
2020-05-01 09:45:02 +02:00
tobi-wan-kenobi
595778f7c3 [core/config] enable logging into a log file
Re-enable the -f | --logfile parameter, but still default to logging to
stderr.

Also, add a "debug" module that is automatically displayed if a bar is
run in debug mode (thanks to @bbernhard for the excellent suggestion)

fixes #614
2020-05-01 09:42:26 +02:00
Bernhard B
17983d4ecc [modules/vpn] fixed typo in vpn module
* due to a typo in the vpn module it wasn't possible anymore to
disconnect an established vpn connection.
2020-05-01 08:51:05 +02:00
Tobias Witek
650942a3e3 [modules/arch-update] Fix missing check_updates()
refactoring broke the arch-update module - fix it.

fixes #610
2020-04-30 21:13:41 +02:00
tobi-wan-kenobi
289a4bad41 [modules/deadbeef] Update to latest API 2020-04-30 20:34:34 +02:00
tobi-wan-kenobi
06f229a339 [modules/deadbeef] quotes 2020-04-30 20:25:59 +02:00
tobi-wan-kenobi
dbbc1284e8 [doc] update notes 2020-04-30 20:25:53 +02:00
tobi-wan-kenobi
f846023fbe [modules] re-enable deadbeef 2020-04-30 20:25:31 +02:00
tobi-wan-kenobi
39d139a51b [core/output] Use string format for interval
see #609
2020-04-30 20:18:42 +02:00
tobi-wan-kenobi
a8e01f0df9 [modules/docker] Exception when daemon is not running
When the docker daemon is not running, only the call to
containers.list() actually throws an exception, so move that into the
exception guard.

fixes #608
2020-04-30 20:13:23 +02:00
tobi-wan-kenobi
88e56c4c47 [core] Add '-d' as shorthand for '--debug'
see #606
2020-04-30 12:52:09 +02:00
tobi-wan-kenobi
1e4955d4b8 [core/module] Make name and module_name attributes 2020-04-30 12:50:24 +02:00
tobi-wan-kenobi
b27956071f [core/widget] Make module an attribute
Instead of having an artifical getter/setter method, just make module a
plain attribute.
2020-04-30 12:46:57 +02:00
tobi-wan-kenobi
9cd9ff626d [core] make widget name an attribute
first, this fixes #607

also, i think it slightly simplifies code to make "simple" stuff like
names, etc. attributes instead of methods all the time.

so, expect this to be extended to other components, as well.
2020-04-30 12:42:34 +02:00
tobi-wan-kenobi
2cc463eb1a [modules/mpd] update to latest API 2020-04-29 20:29:51 +02:00
tobi-wan-kenobi
dc7577069f [modules/mpd] quotes 2020-04-29 20:23:53 +02:00
tobi-wan-kenobi
2ffd4bca1d [modules] re-enable mpd 2020-04-29 20:23:37 +02:00
tobi-wan-kenobi
b16579447a [modules/bluetooth2] Update to latest API 2020-04-29 20:23:10 +02:00
tobi-wan-kenobi
2028d51e15 [modules/bluetooth2] quotes 2020-04-29 20:20:04 +02:00
tobi-wan-kenobi
6e5d60d3ae [modules] re-enable bluetooth2 2020-04-29 20:19:45 +02:00
tobi-wan-kenobi
5aba8a6a36 [modules/bluetooth] update to new API 2020-04-29 20:19:18 +02:00
tobi-wan-kenobi
359effdd85 [modules/bluetooth] quotes 2020-04-29 20:16:19 +02:00
tobi-wan-kenobi
c195b0398e [modules] re-enable bluetooth 2020-04-29 20:16:05 +02:00
tobi-wan-kenobi
cab87896f9 [doc] update 2020-04-29 20:15:44 +02:00
tobi-wan-kenobi
98a57c3d3b [modules] re-enable network_traffic 2020-04-29 16:20:20 +02:00
tobi-wan-kenobi
611bed2007 [core/output] Always hide modules that use "hidden()" 2020-04-29 14:39:12 +02:00
tobi-wan-kenobi
32444ed49f [modules/cmus] Update to latest API 2020-04-28 20:25:28 +02:00
tobi-wan-kenobi
a943e65ed7 [modules/cmus] quotes 2020-04-28 20:16:29 +02:00
tobi-wan-kenobi
2314a7b27a [modules] re-enable cmus 2020-04-28 20:16:15 +02:00
tobi-wan-kenobi
65faf73e8d [modules/nvidiagpu] Hide if not found 2020-04-28 20:15:25 +02:00
tobi-wan-kenobi
f1341772e5 [core/output] Evaluate "hidden()" callback of modules 2020-04-28 20:13:31 +02:00
tobi-wan-kenobi
f2e0d3c0ee [modules/sensors] update to latest API 2020-04-26 16:45:19 +02:00
tobi-wan-kenobi
fd92b249de [modules] re-enable sensors 2020-04-26 16:41:50 +02:00
tobi-wan-kenobi
7867253fdd [doc] constructors have been updated 2020-04-26 16:41:16 +02:00
tobi-wan-kenobi
28bdf4355b [core] Add theme to modules 2020-04-26 16:40:48 +02:00
tobi-wan-kenobi
f2d411276d [modules/all] Update constructore to include theme 2020-04-26 16:39:24 +02:00
tobi-wan-kenobi
1f94eab927 [core] Add themes to module constructor 2020-04-26 16:39:13 +02:00
tobi-wan-kenobi
354d723b7c [modules/vpn] Update to latest API 2020-04-26 10:42:19 +02:00
tobi-wan-kenobi
f44ff3fd34 [modules] re-enable vpn 2020-04-26 10:38:40 +02:00
tobi-wan-kenobi
cfa1a5c896 [modules/cpu2] Update to latest API 2020-04-26 10:34:25 +02:00
tobi-wan-kenobi
b34178ee71 [modules/cpu2] quotes 2020-04-26 10:08:17 +02:00
tobi-wan-kenobi
ed9278cd39 [modules] re-enable cpu2 2020-04-26 10:08:01 +02:00
tobi-wan-kenobi
ec2b2a8a4b [modules/smartstatus] Updated to latest API 2020-04-26 10:07:17 +02:00
tobi-wan-kenobi
8e29bf8f9e [modules/smartstatus] quotes 2020-04-26 10:02:05 +02:00
tobi-wan-kenobi
965d2844b0 [modules] re-enable smartstatus 2020-04-26 10:01:51 +02:00
tobi-wan-kenobi
50449bcee4 [modules/spaceapi] Update to latest API 2020-04-25 16:35:00 +02:00
tobi-wan-kenobi
081186d97d [modules/spaceapi] quotes 2020-04-25 16:28:19 +02:00
tobi-wan-kenobi
da1556f9dc [modules] re-enable spaceapi 2020-04-25 16:28:04 +02:00
tobi-wan-kenobi
aabf269742 [modules/system] Update to latest API 2020-04-25 16:26:57 +02:00
tobi-wan-kenobi
e104579fe1 [modules/system] quotes 2020-04-25 16:20:18 +02:00
tobi-wan-kenobi
8cefafcd6d [modules] re-enable system 2020-04-25 16:20:00 +02:00
tobi-wan-kenobi
e63f267803 [modules/pomodoro] update to latest API 2020-04-25 16:19:08 +02:00
tobi-wan-kenobi
b36b37d94f [modules/pomodoro] quotes 2020-04-25 16:16:20 +02:00
tobi-wan-kenobi
37f7492d75 [modules] re-enable pomodoro 2020-04-25 16:15:46 +02:00
tobi-wan-kenobi
0f368cfdf6 [modules/spotify] Update to latest API 2020-04-25 10:51:40 +02:00
tobi-wan-kenobi
48dcd6a5ae [modules/spotify] quotes 2020-04-25 10:49:25 +02:00
tobi-wan-kenobi
838ba7cd69 [modules] re-enable spotify 2020-04-25 10:49:02 +02:00
tobi-wan-kenobi
2f86ef3b45 [modules/datetimetz] Update to latest API 2020-04-25 10:48:36 +02:00
tobi-wan-kenobi
80fed80847 [modules/datetimetz] quotes 2020-04-25 10:41:09 +02:00
tobi-wan-kenobi
99aadc9188 [modules] re-enable datetimetz 2020-04-25 10:40:50 +02:00
tobi-wan-kenobi
806d08ad4c [modules/title] Update to latest API 2020-04-25 10:40:11 +02:00
tobi-wan-kenobi
efbd9588f7 [modules/title] quotes 2020-04-25 10:32:27 +02:00
tobi-wan-kenobi
fd322aac6e [modules] re-enable title 2020-04-25 10:32:13 +02:00
tobi-wan-kenobi
b64b5365c9 [modules/zpool] Updated to latest API version 2020-04-25 10:31:32 +02:00
tobi-wan-kenobi
dcc06307d9 [modules/zpool] quotes 2020-04-25 10:27:27 +02:00
tobi-wan-kenobi
2048ac8706 [modules] re-enable zpool 2020-04-25 10:27:06 +02:00
tobi-wan-kenobi
c1ce545bcd [modules/pacman] Update to latest API 2020-04-24 16:46:31 +02:00
tobi-wan-kenobi
2586e4e78c [modules/pacman] quotes 2020-04-24 16:40:39 +02:00
tobi-wan-kenobi
543def72ee [modules] re-enable pacman 2020-04-24 16:40:21 +02:00
tobi-wan-kenobi
b85412fb2d [modules/git] Update to latest API 2020-04-24 16:31:37 +02:00
tobi-wan-kenobi
9a1aedab62 [modules] re-enable git 2020-04-24 16:28:01 +02:00
tobi-wan-kenobi
7ffb8f1e7a [modules/dnf] Update to latest API 2020-04-24 16:27:14 +02:00
tobi-wan-kenobi
9a6e044218 [modules/dnf] quotes 2020-04-24 16:22:25 +02:00
tobi-wan-kenobi
940b577225 [modules] re-enable dnf 2020-04-24 16:22:10 +02:00
tobi-wan-kenobi
8d9798c8e1 [modules/deezer] Update to latest API 2020-04-24 16:21:14 +02:00
tobi-wan-kenobi
d047e19d08 [modules/deezer] quotes 2020-04-24 16:19:04 +02:00
tobi-wan-kenobi
3a031abcc2 [modules] re-enable deezer 2020-04-24 16:18:46 +02:00
tobi-wan-kenobi
985ad2edc8 [modules/layout] Update to latest API 2020-04-24 16:18:19 +02:00
tobi-wan-kenobi
20cab432b9 [modules/layout] quotes 2020-04-24 16:16:14 +02:00
tobi-wan-kenobi
810c0b68e0 [modules] re-enable layout 2020-04-24 16:15:42 +02:00
tobi-wan-kenobi
8e9fba36c4 [modules/nvidiagpu] Update to latest API 2020-04-24 16:15:00 +02:00
tobi-wan-kenobi
97866362d5 [modules/nvidiagpu] quotes 2020-04-24 16:09:12 +02:00
tobi-wan-kenobi
53afade88d [modules] re-enable nvidiagpu 2020-04-24 16:08:53 +02:00
tobi-wan-kenobi
f573407b11 [modules/github] Use xdg-open, if available 2020-04-23 12:59:08 +02:00
tobi-wan-kenobi
3bb7cf51ec [modules/redshift] Try to improve transition removal
For some reason, the transition state info sticks around even at night
2020-04-21 20:43:34 +02:00
tobi-wan-kenobi
771eaf331e [modules/shortcut] update to latest API 2020-04-21 20:37:54 +02:00
tobi-wan-kenobi
007b097077 [modules/shortcut] quotes 2020-04-21 20:34:27 +02:00
tobi-wan-kenobi
95d9057973 [modules] re-enable shortcut 2020-04-21 20:34:10 +02:00
tobi-wan-kenobi
cf32da4375 [modules/prime] update to latest API 2020-04-21 20:33:34 +02:00
tobi-wan-kenobi
8278e7c5a1 [modules/prime] quotes 2020-04-21 20:30:44 +02:00
tobi-wan-kenobi
e5131cc787 [modules] re-enable prime 2020-04-21 20:30:29 +02:00
tobi-wan-kenobi
04487d0764 [modules/caffeine] update to latest API 2020-04-21 20:27:50 +02:00
tobi-wan-kenobi
6ff8932c99 [modules/caffeine] quotes 2020-04-21 20:23:08 +02:00
tobi-wan-kenobi
55a94ac770 [modules] re-enable caffeine 2020-04-21 20:22:49 +02:00
tobi-wan-kenobi
39f2e6bfea [modules/http_status] Update to latest API 2020-04-19 14:42:40 +02:00
tobi-wan-kenobi
967ec5892b [modules/http_status] quotes 2020-04-19 14:39:21 +02:00
tobi-wan-kenobi
f4d382eb19 [modules] re-enable http_status 2020-04-19 14:39:00 +02:00
tobi-wan-kenobi
0ea37b7b62 [modules/mocp] Update to latest API 2020-04-19 14:37:16 +02:00
tobi-wan-kenobi
b942c617c5 [modules/mocp] quotes 2020-04-19 14:33:51 +02:00
tobi-wan-kenobi
c0166e6763 [modules] re-enable mocp 2020-04-19 14:33:31 +02:00
tobi-wan-kenobi
9617a59d48 [modules/upower] Update to latest API 2020-04-19 14:32:51 +02:00
tobi-wan-kenobi
3f392be4a4 [modules] re-enable battery-upower 2020-04-19 14:27:56 +02:00
tobi-wan-kenobi
d2417411f3 [modules/watson] Update to latest API 2020-04-19 14:27:11 +02:00
tobi-wan-kenobi
a9fd33f94f [modules/watson] quotes 2020-04-19 14:24:00 +02:00
tobi-wan-kenobi
2c1b3daec9 [modules] re-enable watson 2020-04-19 14:23:41 +02:00
tobi-wan-kenobi
1dbe4f6fcc [modules/battery] fold battery-all into battery
Make battery-all a submodule of battery by adding a special parameter
("compact-devices") that compacts all devices into a single widget.
2020-04-19 14:21:50 +02:00
tobi-wan-kenobi
fae06da446 [modules/battery] Rewrite for bigger reuse 2020-04-19 14:07:15 +02:00
tobi-wan-kenobi
3e79b95df4 [modules/battery] quotes 2020-04-19 13:42:16 +02:00
tobi-wan-kenobi
8738ced325 [modules/getcrypto] Update to latest API 2020-04-19 10:38:09 +02:00
tobi-wan-kenobi
317f1f60b5 [modules/getcrypto] quotes 2020-04-19 10:35:27 +02:00
tobi-wan-kenobi
be63fdc059 [modules] re-enable getcrypto 2020-04-19 10:35:09 +02:00
tobi-wan-kenobi
369af17b56 [modules/rotation] Update to latest API 2020-04-19 10:33:29 +02:00
tobi-wan-kenobi
0984c5502c [modules/rotation] quotes 2020-04-19 10:30:44 +02:00
tobi-wan-kenobi
ad14ff81a7 [modules] re-enable rotation 2020-04-19 10:30:22 +02:00
tobi-wan-kenobi
c591505977 [modules/gpmdp] Update to latest API 2020-04-19 10:24:06 +02:00
tobi-wan-kenobi
10a1bc18ad [modules/gpmdp] quotes 2020-04-19 10:14:56 +02:00
tobi-wan-kenobi
2782b13dd2 [modules] re-enable gpmdp 2020-04-19 10:14:34 +02:00
tobi-wan-kenobi
756c4a113a [modules/layout-xkb] Updated to latest API 2020-04-19 10:13:44 +02:00
tobi-wan-kenobi
60de268850 [modules/sun] Minor refactoring 2020-04-19 10:07:22 +02:00
tobi-wan-kenobi
87fb82965f [modules/sun] Update to latest API 2020-04-19 10:04:01 +02:00
tobi-wan-kenobi
bd27c05fc7 [modules/layout-xkb] quotes 2020-04-19 09:57:54 +02:00
tobi-wan-kenobi
deae6ac98d [modules] re-enable layout-xkb 2020-04-19 09:57:33 +02:00
tobi-wan-kenobi
635373e9a0 [modules/sun] quotes 2020-04-19 09:50:18 +02:00
tobi-wan-kenobi
b29c0594c9 [modules] re-enable sun 2020-04-19 09:50:04 +02:00
tobi-wan-kenobi
63a2881be8 [modules/stock] update to latest API 2020-04-19 09:49:06 +02:00
tobi-wan-kenobi
6b0e73db9b [modules] re-enable stock 2020-04-19 09:44:25 +02:00
tobi-wan-kenobi
968df75945 [modules/sensors2] incorporate latest changes 2020-04-19 09:43:44 +02:00
tobi-wan-kenobi
0a6f914232 [modules/github] Update to latest API 2020-04-18 17:05:19 +02:00
tobi-wan-kenobi
2c4af853a5 [modules/github] quotes 2020-04-18 17:00:17 +02:00
tobi-wan-kenobi
ed67f5515e [modules] re-enable github 2020-04-18 16:59:50 +02:00
tobi-wan-kenobi
ad3e3704ec [modules/notmuch_count] Update to latest API 2020-04-18 16:41:30 +02:00
tobi-wan-kenobi
fc3e5a8bb2 [modules/notmuch_count] quotes 2020-04-18 16:36:14 +02:00
tobi-wan-kenobi
7b26bd9bbc [modules] re-enable notmuch_count 2020-04-18 16:35:45 +02:00
tobi-wan-kenobi
aa3d84f8ec [modules/indicator] Update to latest API 2020-04-18 16:34:27 +02:00
tobi-wan-kenobi
626ed97fcd [modules] re-enable indicator 2020-04-18 16:24:26 +02:00
tobi-wan-kenobi
69c57836df [modules] layout-xkbswitch: Update to new API 2020-04-18 16:24:02 +02:00
tobi-wan-kenobi
d1b0d8b123 [modules] re-enable layout-xkbswitch 2020-04-18 16:19:30 +02:00
tobi-wan-kenobi
206cff1fb5 [modules/todo] Implement improvement from PR #598 2020-04-17 12:53:46 +02:00
tobi-wan-kenobi
8bb47c14aa [modules] Use central location libary instead of individual 2020-04-15 13:26:08 +02:00
tobi-wan-kenobi
74c25ba2ee [util] Add central geolocation info library 2020-04-15 13:25:51 +02:00
tobi-wan-kenobi
0ef6a165e4 [modules/currency] Update to new API 2020-04-13 19:40:50 +02:00
tobi-wan-kenobi
3a0b4c8b38 [modules/currency] quotes 2020-04-13 19:35:27 +02:00
tobi-wan-kenobi
92d30efec7 [modules] re-enable currency 2020-04-13 19:35:06 +02:00
tobi-wan-kenobi
7bdbdce087 [modules/taskwarrior] update to latest API 2020-04-13 14:22:31 +02:00
tobi-wan-kenobi
cb4c561091 [modules/taskwarrior] quotes 2020-04-13 14:20:03 +02:00
tobi-wan-kenobi
c00fe79226 [modules] re-enable taskwarrior 2020-04-13 14:19:36 +02:00
tobi-wan-kenobi
f43ef579d8 [modules/arch-update] Update to latest API 2020-04-13 14:07:39 +02:00
tobi-wan-kenobi
7ff95446ee [modules/arch-update] quotes 2020-04-13 13:59:40 +02:00
tobi-wan-kenobi
2d12c083c9 [modules] re-enable arch-update 2020-04-13 13:58:58 +02:00
tobi-wan-kenobi
05a3c5d8f6 [modules/todo] Update to latest API 2020-04-13 13:57:24 +02:00
tobi-wan-kenobi
89b69369c6 [modules] re-enable todo 2020-04-13 13:53:54 +02:00
tobi-wan-kenobi
d683b02b40 [modules/libvirtvms] Update to latest API 2020-04-13 13:52:15 +02:00
tobi-wan-kenobi
99d88e1e30 [modules] re-enable libvirtvms 2020-04-13 13:49:38 +02:00
tobi-wan-kenobi
580bb20a9f [tests/theme] Fix WAL mocked tests
Until now, the WAL tests depended on the pyWAL cache file being actually
present on the system.
2020-04-13 13:47:50 +02:00
tobi-wan-kenobi
dc5bfd1fc6 [tests/output] Improve pango tests 2020-04-13 13:43:07 +02:00
tobi-wan-kenobi
d2a35f7d02 [modules/docker_ps] Update to latest API 2020-04-13 13:38:05 +02:00
tobi-wan-kenobi
e270ec55ef [modules/docker_ps] quotes 2020-04-13 13:31:48 +02:00
tobi-wan-kenobi
15ae56d2ee [modules] Re-enable docker_ps 2020-04-13 13:31:30 +02:00
tobi-wan-kenobi
95d046d5e8 [modules/twmn] Update to latest API 2020-04-13 13:30:35 +02:00
tobi-wan-kenobi
7edaadffdf [modules] re-enable twmn 2020-04-13 13:27:15 +02:00
tobi-wan-kenobi
82502c5651 [modules/apt] Update to new API
While at it, remove dependency on "parse" module and replace with more
standard "re" dependency.
2020-04-13 13:25:08 +02:00
tobi-wan-kenobi
964407534f [modules] re-enable apt 2020-04-13 13:12:41 +02:00
tobi-wan-kenobi
9c8672a01c [doc] Update contributing 2020-04-13 13:06:50 +02:00
tobi-wan-kenobi
d0a264da21 [modules/yubikey] Update less frequently 2020-04-13 09:44:06 +02:00
tobi-wan-kenobi
b87dcad9c2 [core/module] For errors, always set update interval to 1 2020-04-13 09:43:45 +02:00
tobi-wan-kenobi
bfc3832428 [modules/yubikey] Update to new API 2020-04-13 09:40:24 +02:00
tobi-wan-kenobi
acc36a41ae [modules] re-enable yubikey 2020-04-13 09:32:41 +02:00
tobi-wan-kenobi
e7b9a0e2f8 [modules/uptime] Update to new API 2020-04-13 09:32:29 +02:00
tobi-wan-kenobi
6fd0dbd19d [modules] re-enable uptime 2020-04-13 09:29:16 +02:00
tobi-wan-kenobi
94ef496ee6 [core] Add a decorator for never updating a module 2020-04-13 09:27:03 +02:00
tobi-wan-kenobi
06ca5024dc [modules] re-enable xkcd 2020-04-13 09:20:17 +02:00
tobi-wan-kenobi
f2a62ca7c6 [modules/amixer] update to new API 2020-04-13 09:19:18 +02:00
tobi-wan-kenobi
f5c1c4975f [modules] re-enable amixer 2020-04-13 09:14:22 +02:00
tobi-wan-kenobi
53e2510a4c [doc] Updates and restructuring 2020-04-13 09:11:19 +02:00
tobi-wan-kenobi
0306760eb3 [modules/redshift] No digits for transition anymore
Having 2 digits *after* the comma for transitions seems excessive -
truncate value at the digit sign.
2020-04-12 20:36:32 +02:00
tobi-wan-kenobi
d87b18a824 [modules/redshift] Simplify structure 2020-04-12 20:28:11 +02:00
tobi-wan-kenobi
e36694c03d [modules/ping] Simplify update logic 2020-04-12 20:13:18 +02:00
tobi-wan-kenobi
9e32cdef5d [core] Rename events for more clarity 2020-04-12 20:11:02 +02:00
tobi-wan-kenobi
b5b62c8827 [doc] Add more notes 2020-04-12 14:45:06 +02:00
tobi-wan-kenobi
c2eea913e8 [modules/ping] Update to new API 2020-04-12 14:44:44 +02:00
tobi-wan-kenobi
f965c6b664 [main] Update status bar via event 2020-04-12 14:44:34 +02:00
tobi-wan-kenobi
07ca5cf383 [core/output] Make it possible to update modules via events
Add an event callback "update-modules" that allows various pieces of
bumblebee-status to update the status bar.
2020-04-12 14:44:02 +02:00
tobi-wan-kenobi
b9b1e65176 [modules/ping] quotes 2020-04-12 14:21:59 +02:00
tobi-wan-kenobi
8ff7af2b24 [modules] re-add ping 2020-04-12 14:21:44 +02:00
tobi-wan-kenobi
f7ee2e87bb [modules/spacer] update to latest api 2020-04-12 14:20:43 +02:00
tobi-wan-kenobi
e4151685ff [modules] re-add spacer 2020-04-12 14:19:24 +02:00
tobi-wan-kenobi
4b641c08c6 [modules/hostname] Update to new API 2020-04-12 14:18:03 +02:00
tobi-wan-kenobi
c23670adab [modules] Re-add hostname 2020-04-12 14:15:51 +02:00
tobi-wan-kenobi
0f0a745957 [modules/pihole] Update to new API 2020-04-12 14:15:03 +02:00
tobi-wan-kenobi
cf16cecfec [modules/pihole] quotes 2020-04-12 14:05:55 +02:00
tobi-wan-kenobi
5f2455ac79 [modules] Re-add pihole module 2020-04-12 14:05:30 +02:00
tobi-wan-kenobi
4cd6444bbf [core/theme] Improve items that are lists
Until now, using a list as prefix/suffix didn't work as expected,
because the corresponding method for retrieving the value was called
multiple times, and each time, the next icon would be retrieved.

Changed the logic inside the theme to only update the indices every time
that an actual update was happening.
2020-04-12 14:01:51 +02:00
tobi-wan-kenobi
4b402438cc [modules/vault] Update to new API 2020-04-11 13:35:12 +02:00
tobi-wan-kenobi
464a165c8b [util] Add popup utility 2020-04-11 13:35:04 +02:00
tobi-wan-kenobi
2837a78502 [modules/error] Minor refactoring 2020-04-11 13:21:41 +02:00
tobi-wan-kenobi
1a5a324498 [core/module] improved handling of import errors
Now, the error message includes the name of the module that was not
imported, and there's checks to ensure the "more specific" error (i.e. a
failing import *inside* the module) "wins".
2020-04-11 12:59:39 +02:00
tobi-wan-kenobi
801eceddd2 [modules/vault] quotes 2020-04-11 12:49:25 +02:00
tobi-wan-kenobi
de42edc6b7 [modules] re-add vault 2020-04-11 12:49:04 +02:00
tobi-wan-kenobi
d2c3a41db8 [doc] update notes 2020-04-11 09:26:28 +02:00
tobi-wan-kenobi
22c91e352e [modules/publicip] Update to new API 2020-04-11 09:20:19 +02:00
tobi-wan-kenobi
818c0edbc4 [modules/publicip] quotes 2020-04-11 09:15:51 +02:00
tobi-wan-kenobi
a2865f941d [modules] Re-add publicip module 2020-04-11 09:15:29 +02:00
tobi-wan-kenobi
c998af5649 [modules/traffic] Update to new API 2020-04-11 09:11:24 +02:00
tobi-wan-kenobi
f46796c653 [util/graph] Re-enable braille charts 2020-04-11 09:11:15 +02:00
tobi-wan-kenobi
008f0dc4f2 [modules/traffic] update quotes 2020-04-11 09:02:47 +02:00
tobi-wan-kenobi
605b3bc20b [modules] Re-enable traffic module 2020-04-11 09:02:11 +02:00
tobi-wan-kenobi
6b20d09d60 [doc] update notes 2020-04-11 08:59:11 +02:00
tobi-wan-kenobi
bda153720f [modules/brightness] Use tools also for reading backlight info 2020-04-11 08:58:34 +02:00
tobi-wan-kenobi
ab55cd6514 [doc] Improve theme howto 2020-04-11 08:46:09 +02:00
tobi-wan-kenobi
9b50e96b21 [modules/progress] Update to latest API 2020-04-10 17:11:06 +02:00
tobi-wan-kenobi
55c8f6af28 [modules/progress] double to single quotes 2020-04-10 16:58:54 +02:00
tobi-wan-kenobi
8875e75734 [modules] Re-add "progress" 2020-04-10 16:58:19 +02:00
tobi-wan-kenobi
1cab99175b [core/theme] slight improvements for clarity 2020-04-10 16:56:15 +02:00
tobi-wan-kenobi
99a6a11e3a [core/output] Fix drawing of separator backgrounds 2020-04-08 14:13:18 +02:00
tobi-wan-kenobi
20044762af [core/config] Ignore malformed parameters 2020-04-08 11:50:00 +02:00
tobi-wan-kenobi
f22561cf90 [core/module] remove debug output 2020-04-08 11:48:16 +02:00
tobi-wan-kenobi
cde06bd33b [core/module] Re-enable aliases 2020-04-07 21:23:42 +02:00
tobi-wan-kenobi
be4e901e42 [doc] update notes 2020-04-07 21:04:22 +02:00
tobi-wan-kenobi
c4762d92f7 [core/theme] slight refactoring 2020-04-07 21:00:55 +02:00
tobi-wan-kenobi
32e5d633f4 [core/output] Add tests for pango formatting 2020-04-07 20:59:26 +02:00
tobi-wan-kenobi
2b8eda9d72 [modules/error] Add simple module to show bumblebee errors 2020-04-07 20:44:10 +02:00
tobi-wan-kenobi
c0cc1ccd75 [core] Remove "intelligent" theme accessors
To simplify code, remove the auto-generated theme accessors for
attributes, and instead use a generic "get" method.
2020-04-07 20:44:01 +02:00
tobi-wan-kenobi
f32affa563 [modules/shell] Fix missing trim of output string
Remove starting/trailing whitespaces, \n etc.

fixes #592
2020-04-06 08:23:17 +02:00
tobi-wan-kenobi
23215303ca [core] Add "merged" pango support
With this commit, it is possible to add pango directives inside every
piece that supports direct output (e.g. defaults/prefix or <module
name>/prefix) and those will be merged - i.e. it is possible to specify
defaults inside "defaults" and override/specify in the particular
modules.
2020-04-05 14:57:52 +02:00
tobi-wan-kenobi
e653624f5a [core/output] Re-enable basic pango support
Re-enable pango as simple "pango" dict wherever a normal value (e.g.
prefix, suffix) can go.
2020-04-05 14:27:09 +02:00
tobi-wan-kenobi
37cca1c3b9 [core/output] Fully switch to i3 block abstraction
According to the unit tests, at least, the old functionality is back
again - with the additional i3 block abstraction in output in place.

Also, pango support is temporarily removed again and will be
re-implemented based on the new architecture.
2020-04-05 13:58:59 +02:00
tobi-wan-kenobi
f5052473fb [core] Add preliminary block abstraction to output
The idea is to simplify the way the output module currently works by:
- introducing an abstraction that represents blocks; these abstractions
  contain all data - uninterpreted - required to draw a block
- separately from that, whenever the block is serialized into JSON,
  do the interpretation (pango vs. non-pango, etc.)

This - theoretically - should simplify code by creating two separate
concerns: collecting the data and actually interpreting it.
2020-04-05 10:53:17 +02:00
tobi-wan-kenobi
b5c2ca6ccf [core/output] Add generic pango support
Allow any piece of a theme that specifies a set of attributes (default,
cycles, states, widgets) to use pango *instead* of the usual attributes.

If pango is present, this will have precedence.

A practical example of this can be found in the powerline-pango theme,
which is added solely for demonstration purposes.

fixes #531
2020-04-04 14:38:37 +02:00
tobi-wan-kenobi
89247d834b [core/widget] Use __ for private variables 2020-04-04 13:58:22 +02:00
tobi-wan-kenobi
7547537c81 [core/event] Remove debug output 2020-04-04 13:57:54 +02:00
tobi-wan-kenobi
a6eb6c3a11 [core/output] Use __ for private variables 2020-04-04 13:57:42 +02:00
tobi-wan-kenobi
3aeec1c7e6 [core/module] Use __ for private variables 2020-04-04 13:55:54 +02:00
tobi-wan-kenobi
476b2000f9 [core/config] Use __ for private variables and methods 2020-04-04 13:54:08 +02:00
tobi-wan-kenobi
04fc9867ac [core/input] Move event handling to core.event
Until now, bumblebee-status did event handling in two places with almost
identical code: in core.event (makes sense) and core.input (still makes
sense, but a bit more dubious).

Changed core.input to use core.event
2020-04-04 13:52:10 +02:00
tobi-wan-kenobi
a43917594e [tests/event] Add some more tests for trigger-time args 2020-04-04 08:44:41 +02:00
tobi-wan-kenobi
52e5ad7b43 [core/input] Handle exceptions for non-existent commands 2020-04-04 08:17:35 +02:00
tobi-wan-kenobi
944b223f1e [doc] small updates 2020-04-04 08:04:48 +02:00
tobi-wan-kenobi
77443b7802 [tests/theme] Add more theme tests 2020-04-04 08:03:03 +02:00
tobi-wan-kenobi
a71828f0b4 [modules/weather] Small improvements 2020-04-04 07:47:53 +02:00
tobi-wan-kenobi
a7effbff78 [modules/weather] move to contrib 2020-04-02 22:10:37 +02:00
tobi-wan-kenobi
5ff0269662 [modules/weather] Update to new API 2020-04-02 22:10:18 +02:00
tobi-wan-kenobi
d0200b656d [core/config] Allow string formatted intervals
Allow users to specify intervals such as '5m', for convenience
2020-04-02 16:55:36 +02:00
tobi-wan-kenobi
18154dd74f [modules] Adjust update schedules
Make a few modules update more seldomly, to reduce CPU consumption.
2020-04-02 16:30:31 +02:00
tobi-wan-kenobi
0f6dfb3f1a [core/input] do not throw on wrong event type
until now, if a module had registered callbacks, events for nonexistent
buttons caused the trigger mechanism to raise an exception.
2020-04-02 16:21:07 +02:00
tobi-wan-kenobi
2756f8fbb3 [core/theme] Fix retrieval of rotating theme values (e.g. battery)
When rotating theme values (e.g. the "charge" icon of the battery
module(s)), until now, the code just showed the raw list (because it
wasn't aware of the need to rotate).
2020-04-02 12:51:08 +02:00
tobi-wan-kenobi
658fbd2c1c [modules/hddtemp] Update to new API 2020-03-31 21:06:44 +02:00
tobi-wan-kenobi
52ca6e5a43 [modules/hddtemp] double to single quotes 2020-03-31 21:03:30 +02:00
tobi-wan-kenobi
0873a58dc0 [modules] Re-added hddtemp 2020-03-31 21:03:07 +02:00
tobi-wan-kenobi
885cc09636 [core/output] Add some more tests 2020-03-31 20:57:09 +02:00
tobi-wan-kenobi
232dcb733e [doc] Improve HOWTO_MODULE 2020-03-30 21:51:46 +02:00
tobi-wan-kenobi
f262080e78 [modules/shell] Small refactoring
Use threads a bit differently do make do with fewer helper functions.
2020-03-30 21:18:28 +02:00
tobi-wan-kenobi
422a9986b4 [modules/shell] Update to new API 2020-03-30 21:09:09 +02:00
tobi-wan-kenobi
251a23d2f1 [modules/shell] double quotes -> single quotes 2020-03-30 20:56:35 +02:00
tobi-wan-kenobi
006a729be3 [modules] re-add shell module 2020-03-30 20:56:08 +02:00
tobi-wan-kenobi
95410e4adf [core/input] __ for private variables, use id as property 2020-03-29 14:43:04 +02:00
tobi-wan-kenobi
428b627daf [core/events] Use __ for private variables 2020-03-29 14:37:30 +02:00
tobi-wan-kenobi
c625baa47a [core] Move "every" to decorators module 2020-03-29 14:36:44 +02:00
tobi-wan-kenobi
11ebcee29a [tests] Fix kernel module test 2020-03-29 14:35:20 +02:00
tobi-wan-kenobi
bd7ff3c8f1 [core] Allow modules to specify default update interval
Expanding on the implementation in d582016, add a decorator
`core.module.every()` that allows a module to specify how often to
update the module's state.

This can still be overridden using the CLI parameter `interval`.
2020-03-29 14:32:47 +02:00
tobi-wan-kenobi
b66b13211e [modules/dunst] Fix toggling
At least Void Linux doesn't like kill -SIGUSR<N>

Also, added some debugging to inspect state changes for modules/widgets.

Also also, fix problem with min width, if no minwidth is set
2020-03-29 14:14:30 +02:00
tobi-wan-kenobi
da14ad3f36 [doc] Add small reminder about brightness 2020-03-29 14:13:12 +02:00
tobi-wan-kenobi
ce6fed181e [doc] Update notes 2020-03-29 13:56:43 +02:00
tobi-wan-kenobi
4f87f65f71 [doc] Add basic HOWTO write a module 2020-03-29 13:49:48 +02:00
tobi-wan-kenobi
95360fe522 [modules/brightness] Remove remaining reference to 'engine' 2020-03-28 21:17:38 +01:00
tobi-wan-kenobi
2a93a001b2 [theme] Add unit tests for WAL loading 2020-03-28 14:51:48 +01:00
tobi-wan-kenobi
18ea6d36d1 [doc] Add development priorities 2020-03-28 14:04:02 +01:00
tobi-wan-kenobi
5ade8e47f0 [core/theme] Add some unit tests 2020-03-28 14:03:50 +01:00
tobi-wan-kenobi
d5820160dc [modules/sensors2] Fix runtime errors 2020-03-28 13:44:45 +01:00
tobi-wan-kenobi
908ef67109 [core/widget] bump test coverage 2020-03-27 13:54:22 +01:00
Tobias Witek
9755a70578 [tests] Make tests pass again (configurable update interval) 2020-03-23 15:40:53 +01:00
Tobias Witek
e8288ef793 [core] Add per-module interval configuration
Allow individual modules to set different update intervals by using an
overriding 'interval' parameter.

see #574
2020-03-23 15:32:06 +01:00
Tobias Witek
0e538a6088 [modules/weather] double quotes to single quotes 2020-03-15 14:05:26 +01:00
Tobias Witek
633bbdd6ff [modules] Reimport weather module 2020-03-15 14:05:13 +01:00
Tobias Witek
9076b30325 [core/output] Make tests pass again
Using an empty configuration vs. no configuration at all to make tests
run again.
2020-03-15 14:02:48 +01:00
Tobias Witek
f2dc5f4b40 [core/output] Implement autohide
Allow output to automatically hide widgets that are *not* in any
critical mode
2020-03-15 14:01:09 +01:00
Tobias Witek
6975f179fc [modules/sensors2] Update to newest API 2020-03-15 13:53:12 +01:00
Tobias Witek
665fde5399 [modules/sensors2] Double to single quotes 2020-03-15 13:44:54 +01:00
Tobias Witek
d0406ffe83 [modules] Reimport sensors2 2020-03-15 13:44:38 +01:00
Tobias Witek
7fb626aa2c [modules/disk] Update to new API
Also, remove deprecated parameters
2020-03-15 13:40:13 +01:00
Tobias Witek
2007f8d0b9 [modules/disk] Quotes conversion 2020-03-15 13:34:09 +01:00
Tobias Witek
7aa1bc7b44 [modules] Reimport disk module 2020-03-14 14:04:45 +01:00
Tobias Witek
7ffb2d8626 [doc] Update documentation structure 2020-03-13 14:23:11 +01:00
Tobias Witek
5851b104c5 [modules/xrandr] Adapt to new API 2020-03-13 13:56:08 +01:00
Tobias Witek
932d362d30 [modules/xrandr] Change quotation 2020-03-11 21:35:45 +01:00
Tobias Witek
59dc5f8047 [modules] Reimport xrandr 2020-03-11 21:35:25 +01:00
Tobias Witek
41dc387d0c [core] Re-enable WAL support
Implement a generic "load keywords and replace during runtime"
mechanism, with the first concrete use-case of WAL colors (load them
during startup, and during runtime, whenever a matching name is found in
the keywords, replace with the actual color)
2020-03-08 14:19:21 +01:00
Tobias Witek
be2864b063 [modules/redshift] Update to latest framework 2020-03-07 14:13:25 +01:00
Tobias Witek
2592069fb9 [modules/redshift] Single quotes 2020-03-07 14:06:18 +01:00
Tobias Witek
967521593c [modules] Import redshift 2020-03-07 14:06:01 +01:00
Tobias Witek
867e87913d [modules/battery] Add warning log if no estimates 2020-03-07 13:56:05 +01:00
Tobias Witek
7cdcde0080 [modules/dunst] Update to make working again 2020-03-07 13:54:45 +01:00
Tobias Witek
4bc5f73ccc [modules/dunst] Single quotes 2020-03-07 13:49:09 +01:00
Tobias Witek
b373de4cad [modules] Import module dunst 2020-03-07 13:48:54 +01:00
Tobias Witek
f24cd6fa83 [modules/brightness] Update APIs 2020-03-07 13:37:56 +01:00
Tobias Witek
cfa4d0df48 [modules/brightness] Single quotes 2020-03-07 13:34:22 +01:00
Tobias Witek
e6181e2d94 [modules/brightness] Re-import brightness module 2020-03-07 13:34:02 +01:00
Tobias Witek
8efa101380 [util/format] Tests and minor renaming 2020-03-07 13:33:28 +01:00
Tobias Witek
0f96f2727b [doc] Some updates 2020-03-07 13:26:33 +01:00
Tobias Witek
47e8d5b82b [core] replace widget.minwidth with widget.set('theme.minwidth') 2020-03-07 13:21:19 +01:00
Tobias Witek
b841ba3c93 [modules/battery] Re-enable battery module 2020-03-06 20:57:32 +01:00
Tobias Witek
9b96c142d5 [modules/battery] Migrate constructor 2020-03-06 20:37:46 +01:00
Tobias Witek
c2b0c8e754 [modules/core] Add battery module 2020-03-06 20:34:30 +01:00
Tobias Witek
7c866f1476 [tests] Add tests for byte formats 2020-03-06 15:00:03 +01:00
Tobias Witek
c5f78ad60d [doc] Extend migration guide 2020-03-06 14:52:54 +01:00
Tobias Witek
56a6173282 [modules/memory] Simplify and use util methods 2020-03-06 14:52:16 +01:00
Tobias Witek
13e512d1f1 [modules/memory] Fix imports an module constructor 2020-03-06 14:48:11 +01:00
Tobias Witek
5ccf60a16b [modules] Re-added module memory 2020-03-06 14:46:33 +01:00
Tobias Witek
48c21a902e [tests/module] Skip failing tests in Python 3.{4,5} 2020-03-06 14:45:09 +01:00
Tobias Witek
d2e8057141 [modules] Add CPU module
Also, change minwidth from theme.minwidth to widget property
2020-03-06 14:33:40 +01:00
Tobias Witek
51faef9fd4 [modules/kernel] Make widget dynamic (update if kernel changes) 2020-03-06 14:31:08 +01:00
Tobias Witek
efc2e4f94e [modules] Separate modules into core & contrib
Also, improve errors when importing a module fails.

Also, add more tests.
2020-03-06 14:14:34 +01:00
Tobias Witek
47950240d0 [tests/core/module] Add tests for widget retrieval by name 2020-03-05 21:17:06 +01:00
Tobias Witek
28b891ef37 [doc] Extend module migration documentation 2020-03-05 21:13:48 +01:00
Tobias Witek
0a0e39b516 [modules] Re-add load module 2020-03-05 21:13:15 +01:00
Tobias Witek
d19eb72296 [tests/module] Test generation of error widget
When a module/widget throws, an error widget should be produced.
2020-03-04 21:06:09 +01:00
Tobias Witek
533b8ca0cc [modules] Re-add module nic 2020-03-01 14:36:12 +01:00
Tobias Witek
7420434358 [doc] Started module migration guide 2020-03-01 14:32:30 +01:00
Tobias Witek
b839a9eb44 [modules/pulseaudio] Raise if not able to handle error 2020-03-01 14:09:45 +01:00
Tobias Witek
a1aec8fff6 [core/module] Add error widgets if a module throws
Module should have to care less about thrown exceptions.
2020-03-01 14:08:16 +01:00
Tobias Witek
cb3482ae27 [core/decorators] Simplify and test scrolling 2020-02-29 14:05:02 +01:00
Tobias Witek
b99ac07ef0 [core/decorators] Add more tests 2020-02-27 21:39:04 +01:00
Tobias Witek
162398c6b6 [core/output] Make error widgets critical 2020-02-24 15:07:34 +01:00
Tobias Witek
0cc744ed15 [core/output] Cache widget output for selective redraw
To make redraw work correctly and elegantly with supplementary elements
(e.g. prefix, postfix, separators), always do a full redraw of the bar
(to make the theme update correctly, but cache the actual *data* to
display inside the output.
2020-02-24 15:05:58 +01:00
Tobias Witek
1a093a73b1 [core] Add prefix/postfix support 2020-02-24 14:54:11 +01:00
Tobias Witek
37e1deaa6f [tests] Add (partial) decorator tests 2020-02-24 14:20:54 +01:00
Tobias Witek
0a266b607e [core/output] Unify common i3 attributes 2020-02-24 14:06:57 +01:00
Tobias Witek
84833dc7db [core] Add (partial) support for states
Add states to the modules and widgets. Widgets are mostly just a
pass-through (backwards compatibility, and ease of use - making states
directly inside the widgets would require more code inside the modules
to ensure that each widget is correctly updated).

Still missing:
- Separators during partial update (right now, it takes one interval
until separators are drawn correctly)
2020-02-23 21:13:49 +01:00
Tobias Witek
fd57af9325 [tests] Make tests pass again 2020-02-23 14:55:13 +01:00
Tobias Witek
5e673482d3 [core] Nicify error widget 2020-02-23 14:52:58 +01:00
Tobias Witek
65186baea6 [core] Make error widget scrollable
Restrict error module to 15 characters and make it scrollable.
2020-02-23 14:47:47 +01:00
Tobias Witek
a69e058dd1 [core] Add decorators library
For now, simply add a decorator that allows for scrolling of text
2020-02-23 14:47:20 +01:00
Tobias Witek
a2446a36af [core/theme] Better separator handling
Add a generic method to the theme to get the "previous" value of an
attribute.
2020-02-23 14:31:30 +01:00
Tobias Witek
66bdfacf6f [modules/pulseaudio] Show bar charts
Re-enable optional display of barcharts
2020-02-23 13:59:47 +01:00
Tobias Witek
b2064142d0 [util] Add graph library
Add a library for drawing horizontal and vertical bar graphs (thanks to
@somospocos for contributing!)
2020-02-23 13:59:22 +01:00
Tobias Witek
3496fa622a [modules] Add preliminary version of pulseaudio module
Add pasink/pasource and __pulseaudio (internal module, not intended for
direct use).
2020-02-23 13:45:42 +01:00
Tobias Witek
30c539f1f2 [util/cli] Add option to ignore return codes
Add an option that ignores the return code of an execute, instead of
throwing an error (which sometimes causes ugly try/except blocks in
client code)
2020-02-23 13:44:49 +01:00
Tobias Witek
7a1cd4b613 [util] Add 'formatting' utility methods
Add helpers to unify formatting of bool/list/int values.
2020-02-23 13:43:44 +01:00
Tobias Witek
a9c1c3581a [core/theme] Iconset needs to be auto by default 2020-02-22 14:11:10 +01:00
Tobias Witek
30d34b12e0 [core/theme] Fix loading of custom iconset 2020-02-22 14:08:28 +01:00
Tobias Witek
3bb857f250 [core/theme] Add iconset support
Allow themes to specify iconsets. To do so, add a new util library
"util.algorithm", which currently contains support for deep merging of
dicts.
2020-02-22 14:07:24 +01:00
Tobias Witek
ef75e593f7 [core/config] Add stub for autohide
Add stub for autohide so that my status bar doesn't terminate with an
error (easier testing)
2020-02-22 13:44:44 +01:00
Tobias Witek
38410adcb8 [core/theme|output] Add separators
Add a way for themes to specify custom separators. Doing that, make
nicer interfaces for drawing "supplementary" components (separators)
for widgets and generalize the attribute retrieval within the theme.
2020-02-22 13:42:44 +01:00
Tobias Witek
d3fc648c08 [core/theme] Dynamically create theme accessors
Dynamically generate the accessors for a theme's attributes.

NB: This might be even nicer coming from a JSON rather than inside the
code.
2020-02-16 21:15:14 +01:00
Tobias Witek
6143c5e5a1 [doc] NOTES: Add more notes
Otherwise, I'll forget about them
2020-02-16 14:55:26 +01:00
Tobias Witek
808c46f551 [core/theme] Add separator-block-width 2020-02-16 14:54:20 +01:00
Tobias Witek
6e5e297d93 [core/theme] Add true cycling support
Using the freshly introduced eventing system, enable cycling of widget
attributes.
2020-02-16 14:39:10 +01:00
Tobias Witek
a4904d998f [core] Use core.event to decouple main and output
Make output act on events, not on concrete calls.
2020-02-16 14:30:45 +01:00
Tobias Witek
c1df1686c1 [core/event] Initial commit of simplistic event engine 2020-02-16 14:27:17 +01:00
Tobias Witek
26ae63b5ad [core/theme] Add preliminary cycling support
Add support for cycling values in a theme, for now as long as the cycle
size is 0 :P
2020-02-16 14:02:21 +01:00
Tobias Witek
f05098a120 [main] Add error widget for 'all' errors
One exception: Wrong arguments aren't handled, because argparse doesn't
throw in this circumstance.
2020-02-16 13:50:15 +01:00
Tobias Witek
0bae9e33bb [themes] restore all themes 2020-02-16 13:43:45 +01:00
Tobias Witek
dc8da374fb [themes] Re-add gruvbox-powerline-light 2020-02-15 14:06:45 +01:00
Tobias Witek
affeba5b8f [core/theme] Add simple theme stub
Add support for default fg/bg colors
2020-02-15 14:05:27 +01:00
Tobias Witek
99cca7002c [core/module] Fix error display 2020-02-15 14:04:53 +01:00
Tobias Witek
5d971267db [core/theme] Add preparatory work for themes
Add two new parameters: theme and iconset

Add a placeholder class core.theme.Theme, an instance of which is passed
in to the i3 output object (which is the only object that should ever
have need of the theme, hopefully).
2020-02-09 13:46:56 +01:00
Tobias Witek
b157aa9fb5 [core/output] Minor refactoring - widget assembly 2020-02-09 13:30:40 +01:00
Tobias Witek
4e2a645bd3 [core/input] Separate module/widget update and retrieval
To make it easier to update individual modules, separate the call to
update() and the call to actually drawing the status.

Additionally, this avoids the "side effect" of updating when drawing the
status line.
2020-02-09 13:25:34 +01:00
Tobias Witek
5810a12944 [core/output] Start implementation of a partial update
Add a "patch()" method that eventually will only update affected
modules.
2020-02-08 14:22:43 +01:00
Tobias Witek
cc0139e517 [tests/input] Add test for non-callable callback 2020-02-08 14:09:14 +01:00
Tobias Witek
468e30ce66 [main] Add input thread logic and logging
To the main application, add an input thread that "simply" reads
sys.stdin events and transmits them via core.input.

Additionally, set up some initial logging (yeah, for threading, this is
needed immediately)
2020-02-08 13:56:52 +01:00
Tobias Witek
fca364554e [core/output] Add id of widget and module to output
In the process of that, fix a bug in how the parent class constructors
were invoked.
2020-02-08 13:45:52 +01:00
Tobias Witek
4d34fa9261 [core/input] Use util.cli to execute commands
If the registered callback is not callable (i.e. no Python method),
assume it's a command that should be invoked via a shell.
2020-02-08 13:40:51 +01:00
Tobias Witek
9d4936b596 [util/cli] Add new CLI utilities
For now, this just allows executing commands
2020-02-08 13:39:35 +01:00
Tobias Witek
f9267f2131 [core/widget] fix small bug (wrong indentation) 2020-02-07 21:32:20 +01:00
Tobias Witek
e0df8b84e5 [core] Add input handling
Add a (half-finished) input library, that for now simply allows
registration and triggering of events.

As next steps, the trigger will happen as part of a separate thread that
reads input events.

Additionally, invoking commands via a execute() will be supported.

Thirdly, there is need of a way to selectively update the affected
modules (widgets), which should be possible given that the event
contains both the instance (widget ID) and name (module name).
2020-02-07 21:28:29 +01:00
Tobias Witek
a70c82dc4c [modules] Add date, time, datetime
Add modules to show date, time and date and time. Instead of introducing
a "fancy" aliasing mechanism, simply implement time and date in terms of
datetime, by inheriting and overwriting selectively.
2020-02-04 21:10:05 +01:00
Tobias Witek
6adc497f68 [core] Add support for module parameters
Parse '-p' parameters and make them accessible via 'parameter()' from
within a module (basically, restore the previous configuration
facility).

Still TODO: Add support for configuration files.
2020-02-04 21:09:11 +01:00
Tobias Witek
3a531c359f [core/module] Add config to module
Add access for all modules to the commandline parameters (called
"config" in bumblebee-status)
2020-02-03 21:30:06 +01:00
Tobias Witek
74e74bb78b [core/output] Make bar actually work (flush output)
Seems adding a \n isn't sufficient for flushing, instead explicitly
flush stdout to produce a result reliably in the i3bar.
2020-02-02 21:21:24 +01:00
Tobias Witek
eb38e1ce96 [travis] Exclude codeclimate
doesn't run on development branches anyhow, and for some reason produces
errors on python 3.8
2020-02-02 14:42:32 +01:00
Tobias Witek
1801e83322 [tests] Do not show stdout/stderr by default 2020-02-02 14:41:45 +01:00
Tobias Witek
854d7dc470 [modules] Add first "real" module: kernel
Since 'kernel' is small and shows static information, have this be the
very first module added.
2020-02-02 14:41:22 +01:00
Tobias Witek
445c5a65f1 [core/output] Move widget/module handling inside output
The core.output module now manages the list of modules and retrieves the
widgets inside draw() itself. That way, details of drawing/updating
widgets are not visible from the outside anymore.
2020-02-02 14:18:13 +01:00
Tobias Witek
96c7b762b2 [travis] switch to coverage instead of nosetest 2020-02-02 14:06:44 +01:00
Tobias Witek
69eb1ada0d [tests] Move from nosetests to unittest
Since coverage doc says nosetests hasn't been maintained for a while,
switch to unittest.
2020-02-02 13:27:03 +01:00
Tobias Witek
66537fbe05 [core/output] Rewrite to hide sys.stout
Add generic "draw()" method that redirects internally to the actual
calls. These can now produce JSON, which is nicer because:

1. Easier to use during testing
2. More flexible
3. Centralizes printing (somewhat)

Still, the "suffix" concept isn't really nice, but so far, I have no
better approach.
2020-02-01 21:37:38 +01:00
Tobias Witek
b7ca5eb3a5 [doc] Add pylint to not forget about it 2020-01-26 14:20:09 +01:00
Tobias Witek
fbd44618dc [test] Python 3.5 compatibility
Use assert_called_once_with() instead of assert_called_once() to
maintain compatibility with Python 3.5
2020-01-26 14:17:14 +01:00
Tobias Witek
2f78ee4beb [travis] drop python 2.7 support 2020-01-26 14:11:58 +01:00
Tobias Witek
7a9cb8b533 [core/module] Add very basic widget capability 2020-01-26 14:06:09 +01:00
Tobias Witek
da792f9b7b [doc] Update notes
Add more stuff that needs to be considered for backwards compatibility
2020-01-26 13:59:11 +01:00
Tobias Witek
fee7cf7882 [core/widget] Add widget class
To maintain backwards compatibility (and because I think it's an OK
design choice), keep the widget concept (a single module can produce
multiple widgets).
2020-01-26 13:58:29 +01:00
Tobias Witek
a17cd4759c [core/output] Implement simple wait
To pace the output lines, implement a simple wait() call for the i3
output.
2020-01-26 13:31:20 +01:00
Tobias Witek
fed80fb470 [doc] Add notes for redevelopment 2020-01-25 14:28:38 +01:00
Tobias Witek
acf9d6214e [tests] Restructure
Put tests into directory structure equivalent to that of the code
itself, for better separation (hopefully)
2020-01-25 14:27:41 +01:00
Tobias Witek
5a60a23ebd [core/config] Add interval (as parameter store)
Add a generic parameter store to the configuration and use it to set the
parameter "interval" (backwards compatibility)
2020-01-25 14:24:21 +01:00
Tobias Witek
8a2ef5ea5d [core] Add util.store for store-like get/set 2020-01-25 14:20:25 +01:00
Tobias Witek
bd12a51bfb [core] Add simple module loading
Add a way to load modules located in modules/*
2020-01-19 16:06:21 +01:00
Tobias Witek
8622673114 [core] Re-enable preliminary module loading (stubbed)
Add logic for parsing commandline options, and a preliminary stub for
loading modules.

Note: The idea is that core.module.load() will return a valid, but
empty, module that displays an error, if the module cannot be loaded
2020-01-19 15:36:52 +01:00
Tobias Witek
f234f81aa9 Add missing __init__.py 2020-01-19 14:22:56 +01:00
Tobias Witek
e931bb93c6 [core] Rework core implementation
Experimental re-implementation of core functionality with the aim:
- Depend only on the Python Standard Library for core
- If modules are missing elsewhere, *never* throw
- Unit test *everything*
- Cleaner and more minimal implementation
- Better integration points for existing implementations (charts,
  braille, etc.)
- Full backwards-compatibility with existing module system (except where
  modules can be vastly simplified)
2020-01-19 13:29:34 +01:00
474 changed files with 22345 additions and 11208 deletions

View file

@ -1,16 +1,19 @@
engines:
version: "2"
plugins:
duplication:
enabled: true
config:
languages:
- python
python:
fixme:
enabled: true
radon:
enabled: true
ratings:
paths:
- "**.py"
exclude_paths:
- tests/
- thirdparty/
config:
python_version: 3
threshold: "D"
exclude_patterns:
- "tests/"
- "versioneer.py"
- "bumblebee_status/_version.py"
- "setup.py"

View file

@ -1,6 +1,10 @@
[run]
omit =
versioneer.*
bumblebee_status/_version.*
setup.*
tests/*
pytests/*
*mock*
*funcsigs*
*pbr*

1
.gitattributes vendored
View file

@ -1 +0,0 @@
bumblebee/_version.py export-subst

View file

@ -1,6 +1,6 @@
---
name: Bug Report
about: Something doesn't work (as expected)
about: Something doesn't work as expected
title: ''
labels: ''
assignees: ''
@ -9,10 +9,10 @@ assignees: ''
### Bug Report
#### Summary
#### Description
Affected module: <module name>
Version used: <e.g. latest git, AUR, PIP>
#### Description:
<description>
#### How to reproduce

View file

@ -10,5 +10,3 @@ assignees: ''
### Feature Request
<!-- Fill in the relevant information below to help triage your issue. -->
#### Summary
<!-- Provide a summary of the feature you would like to see implemented. -->

View file

@ -6,5 +6,3 @@ about: You have some improvement to make bumblebee-status bar better?
### Improvement
<!-- Fill in the relevant information below to help triage your issue. -->
#### Summary
<!-- Provide a summary of the improvement you are submitting. -->

View file

@ -3,6 +3,8 @@ name: Other
about: You have some other ideas you want to introduce?
---
### Description
<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->
**What kind of change does this PR introduce?**

29
.github/workflows/aurpublish.yml vendored Normal file
View file

@ -0,0 +1,29 @@
---
name: Upload AUR Package
on:
release:
types: [created]
jobs:
aur-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Create PKGBUILD
run: |
python ./create-pkgbuild.py > ./PKGBUILD
- name: Publish AUR package
uses: KSXGitHub/github-actions-deploy-aur@v2.5.0
with:
pkgname: bumblebee-status
pkgbuild: ./PKGBUILD
commit_username: ${{ secrets.AUR_USERNAME }}
commit_email: ${{ secrets.AUR_EMAIL }}
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message: Update AUR package
ssh_keyscan_types: rsa,dsa,ecdsa,ed25519

45
.github/workflows/autotest.yml vendored Normal file
View file

@ -0,0 +1,45 @@
name: Tests
on:
pull_request:
types: [ opened, reopened, edited ]
push:
env:
CC_TEST_REPORTER_ID: 40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install Ubuntu dependencies
run: sudo apt-get install -y libdbus-1-dev libgit2-dev libvirt-dev taskwarrior
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -U coverage pytest pytest-mock freezegun
pip install 'pygit2<1' 'libvirt-python<6.3' 'feedparser<6' || true
pip install $(cat requirements/modules/*.txt | grep -v power | cut -d ' ' -f 1 | sort -u)
- name: Install Code Climate dependency
run: |
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
./cc-test-reporter before-build
- name: Run tests
run: |
coverage run --source=. -m pytest tests -v
- name: Report coverage
uses: paambaati/codeclimate-action@v3.2.0
with:
coverageCommand: coverage3 xml
debug: true

70
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '31 0 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

5
.gitignore vendored
View file

@ -1,3 +1,5 @@
*.o
# Vim swap files
*swp
*~
@ -94,3 +96,6 @@ ENV/
# Visual studio project files
.vscode/
# mypy cache
.mypy_cache

9
.readthedocs.yaml Normal file
View file

@ -0,0 +1,9 @@
version: 2
python:
install:
- requirements: docs/requirements.txt
build:
os: ubuntu-22.04
tools:
python: "3"

View file

@ -1,26 +0,0 @@
sudo: false
language: python
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y task libdbus-1-dev
install:
- pip install i3ipc
- pip install psutil
- pip install netifaces
- pip install -U coverage==4.3
- pip install codeclimate-test-reporter
- pip install taskw
- pip install pytz
- pip install tzlocal
- pip install dbus-python
script:
- nosetests -v --with-coverage --cover-erase tests/
- CODECLIMATE_REPO_TOKEN=40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf codeclimate-test-reporter
addons:
code_climate:
repo_token: 40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf

View file

@ -2,16 +2,15 @@ Most importantly: Many thanks for considering contributing to bumblebee-status!
One thing I need to mention: This is a project I am working on in my (limited) spare time. I try very hard to answer bug tickets and review Pull Requests as quickly as possible, but it might take days, in some cases even weeks, until I get around to doing so. I want to give every contribution the attention it deserves. Really: I am not ignoring you, I'm simply slow :-)
### Filing a Bug
### Filing a bug
If you want to file a bug, simply open an issue and describe your problem. Things that help narrow down the problem are:
* Steps to reproduce
* Relevant section of the i3 configuration
* Debug logs and console output of bumblebee-status
- Steps to reproduce
- Relevant section of the i3 configuration
- Debug logs and console output of bumblebee-status
But even if you can't provide those, any indicator that something is not working as it should is much appreciated!
### Adding a new Module or Theme
If you want to add a new module, please have a look at [How to write a new module](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-module) and [How to write a new theme](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-theme). Then simply create a Pull Request and I will review the changes as soon as possible.
If you want to do me a *big* favour, check the Travis status for any failing unit tests. Oh - and if you happen to add unit tests, that's also something I am very grateful for!
### Adding a new module or theme
If you want to add a new module, please have a look at [how to write a new module](docs/development/module.rst) and [how to write a new theme](docs/development/theme.rst). Then simply create a Pull Request and I will review the changes as soon as possible.
Thanks for reading until here! :)

View file

@ -1,7 +0,0 @@
#start with a Python 3.x container
FROM python:3
#grab repository from github
RUN git clone --recursive https://github.com/tobi-wan-kenobi/bumblebee-status.git /var/bumblebee-status
#run the statusline with no modules or themes specified
CMD python3 /var/bumblebee-status/bumblebee-status

View file

@ -1,6 +1,6 @@
include versioneer.py
include bumblebee/_version.py
include bumblebee_status/_version.py
include requirements/*
include requirements/modules/*
include bumblebee/themes/*
include bumblebee/themes/icons/*
include themes/*
include themes/icons/*

View file

@ -1,18 +0,0 @@
DEBIAN_ROOT = build/debian/bumblebee-status/
deb:
mkdir -p $(DEBIAN_ROOT)/DEBIAN
mkdir -p $(DEBIAN_ROOT)/usr/share/bumblebee-status/modules
mkdir -p $(DEBIAN_ROOT)/usr/share/bumblebee-status/themes
cp build/debian/control $(DEBIAN_ROOT)/DEBIAN/
cp bumblebee-status $(DEBIAN_ROOT)/usr/share/bumblebee-status/
cp bumblebee/modules/*.py $(DEBIAN_ROOT)/usr/share/bumblebee-status/modules/
cp -r themes/* $(DEBIAN_ROOT)/usr/share/bumblebee-status/themes
cp LICENSE $(DEBIAN_ROOT)/usr/share/bumblebee-status/
dpkg-deb --build $(DEBIAN_ROOT)
cp build/debian/bumblebee-status.deb .
clean:
rm -rf $(DEBIAN_ROOT)
rm -f build/debian/*.deb
rm -f *.deb

View file

@ -1,78 +0,0 @@
# Table of modules
|Name |Description |
|-----|------------|
|amixer |get volume level<br><br> |
|arch-update |Check updates to Arch Linux.<br><br>Requires the following executable:<br> * checkupdates (from pacman-contrib)<br> |
|battery-upower |Displays battery status, remaining percentage and charging information.<br><br>Parameters:<br> * battery-upower.warning : Warning threshold in % of remaining charge (defaults to 20)<br> * battery-upower.critical : Critical threshold in % of remaining charge (defaults to 10)<br> * battery-upower.showremaining : If set to true (default) shows the remaining time until the batteries are completely discharged<br> |
|battery |Displays battery status, remaining percentage and charging information.<br><br>Parameters:<br> * battery.device : Comma-separated list of battery devices to read information from (defaults to auto for auto-detection)<br> * battery.warning : Warning threshold in % of remaining charge (defaults to 20)<br> * battery.critical : Critical threshold in % of remaining charge (defaults to 10)<br> * battery.showdevice : If set to "true", add the device name to the widget (defaults to False)<br> * battery.decorate : If set to "false", hides additional icons (charging, etc.) (defaults to True)<br> * battery.showpowerconsumption: If set to "true", show current power consumption (defaults to False)<br> |
|battery_all |Displays battery status, remaining percentage and charging information.<br><br>Parameters:<br> * battery.device : Comma-separated list of battery devices to read information from (defaults to auto for auto-detection)<br> * battery.warning : Warning threshold in % of remaining charge (defaults to 20)<br> * battery.critical : Critical threshold in % of remaining charge (defaults to 10)<br> * batter.showremaining : If set to true (default) shows the remaining time until the batteries are completely discharged<br> |
|bluetooth |Displays bluetooth status (Bluez). Left mouse click launches manager app,<br>right click toggles bluetooth. Needs dbus-send to toggle bluetooth state.<br><br>Parameters:<br> * bluetooth.device : the device to read state from (default is hci0)<br> * bluetooth.manager : application to launch on click (blueman-manager)<br> * bluetooth.dbus_destination : dbus destination (defaults to org.blueman.Mechanism)<br> * bluetooth.dbus_destination_path : dbus destination path (defaults to /)<br> * bluetooth.right_click_popup : use popup menu when right-clicked (defaults to True)<br><br> |
|brightness |Displays the brightness of a display<br><br>Parameters:<br> * brightness.step: The amount of increase/decrease on scroll in % (defaults to 2)<br> * brightness.device_path: The device path (defaults to /sys/class/backlight/intel_backlight), can contain wildcards (in this case, the first matching path will be used)<br><br> |
|caffeine |Enable/disable automatic screen locking.<br><br>Requires the following executables:<br> * xdg-screensaver<br> * xdotool<br> * xprop (as dependency for xdotool)<br> * notify-send<br> |
|cmus |Displays information about the current song in cmus.<br><br>Requires the following executable:<br> * cmus-remote<br><br>Parameters:<br> * cmus.format: Format string for the song information. Tag values can be put in curly brackets (i.e. {artist})<br> Additional tags:<br> * {file} - full song file name<br> * {file1} - song file name without path prefix<br> if {file} = '/foo/bar.baz', then {file1} = 'bar.baz'<br> * {file2} - song file name without path prefix and extension suffix<br> if {file} = '/foo/bar.baz', then {file2} = 'bar'<br> * cmus.layout: Space-separated list of widgets to add. Possible widgets are the buttons/toggles cmus.prev, cmus.next, cmus.shuffle and cmus.repeat, and the main display with play/pause function cmus.main.<br> |
|cpu |Displays CPU utilization across all CPUs.<br><br>Parameters:<br> * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%)<br> * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%)<br> * cpu.format : Format string (defaults to "{:.01f}%")<br> |
|cpu2 |Multiwidget CPU module<br><br>Can display any combination of:<br><br> * max CPU frequency<br> * total CPU load in percents (integer value)<br> * per-core CPU load as graph - either mono or colored<br> * CPU temperature (in Celsius degrees)<br> * CPU fan speed<br><br>Requirements:<br><br> * the psutil Python module for the first three items from the list above<br> * sensors executable for the rest<br><br>Parameters:<br> * cpu2.layout: Space-separated list of widgets to add.<br> Possible widgets are:<br> * cpu2.maxfreq<br> * cpu2.cpuload<br> * cpu2.coresload<br> * cpu2.temp<br> * cpu2.fanspeed<br> * cpu2.colored: 1 for colored per core load graph, 0 for mono (default)<br> if this is set to 1, use --markup=pango<br> * cpu2.temp_pattern: pattern to look for in the output of 'sensors -u';<br> required if cpu2.temp widged is used<br> * cpu2.fan_pattern: pattern to look for in the output of 'sensors -u';<br> required if cpu2.fanspeed widged is used<br><br>Note: if you are getting "n/a" for CPU temperature / fan speed, then you're<br>lacking the aforementioned pattern settings or they have wrong values.<br><br> |
|currency |Displays currency exchange rates. Currently, displays currency between GBP and USD/EUR only.<br><br>Requires the following python packages:<br> * requests<br><br>Parameters:<br> * currency.interval: Interval in minutes between updates, default is 1.<br> * currency.source: Source currency (ex. "GBP", "EUR"). Defaults to "auto", which infers the local one from IP address.<br> * currency.destination: Comma-separated list of destination currencies (defaults to "USD,EUR")<br> * currency.sourceformat: String format for source formatting; Defaults to "{}: {}" and has two variables,<br> the base symbol and the rate list<br> * currency.destinationdelimiter: Delimiter used for separating individual rates (defaults to "|")<br><br>Note: source and destination names right now must correspond to the names used by the API of https://markets.ft.com<br> |
|datetime |Displays the current date and time.<br><br>Parameters:<br> * datetime.format: strftime()-compatible formatting string<br> * date.format : alias for datetime.format<br> * time.format : alias for datetime.format<br> * datetime.locale: locale to use rather than the system default<br> * date.locale : alias for datetime.locale<br> * time.locale : alias for datetime.locale<br> |
|datetimetz |Displays the current date and time with timezone options.<br><br>Parameters:<br> * datetimetz.format : strftime()-compatible formatting string<br> * datetimetz.timezone : IANA timezone name<br> * datetz.format : alias for datetimetz.format<br> * timetz.format : alias for datetimetz.format<br> * timetz.timezone : alias for datetimetz.timezone<br> * datetimetz.locale : locale to use rather than the system default<br> * datetz.locale : alias for datetimetz.locale<br> * timetz.locale : alias for datetimetz.locale<br> * timetz.timezone : alias for datetimetz.timezone<br> |
|deadbeef |Displays the current song being played in DeaDBeeF and provides<br>some media control bindings.<br>Left click toggles pause, scroll up skips the current song, scroll<br>down returns to the previous song.<br><br>Requires the following library:<br> * subprocess<br>Parameters:<br> * deadbeef.format: Format string (defaults to "{artist} - {title}")<br> Available values are: {artist}, {title}, {album}, {length},<br> {trackno}, {year}, {comment},<br> {copyright}, {time}<br> This is deprecated, but much simpler.<br> * deadbeef.tf_format: A foobar2000 title formatting-style format string.<br> These can be much more sophisticated than the standard<br> format strings. This is off by default, but specifying<br> any tf_format will enable it. If both deadbeef.format<br> and deadbeef.tf_format are specified, deadbeef.tf_format<br> takes priority.<br> * deadbeef.tf_format_if_stopped: Controls whether or not the tf_format format<br> string should be displayed even if no song is paused or<br> playing. This could be useful if you want to implement<br> your own stop strings with the built in logic. Any non-<br> null value will enable this (by default the module will<br> hide itself when the player is stopped).<br> * deadbeef.previous: Change binding for previous song (default is left click)<br> * deadbeef.next: Change binding for next song (default is right click)<br> * deadbeef.pause: Change binding for toggling pause (default is middle click)<br> Available options for deadbeef.previous, deadbeef.next and deadbeef.pause are:<br> LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, SCROLL_UP, SCROLL_DOWN<br><br> |
|deezer |Displays the current song being played<br>Requires the following library:<br> * python-dbus<br>Parameters:<br> * deezer.format: Format string (defaults to "{artist} - {title}")<br> Available values are: {album}, {title}, {artist}, {trackNumber}, {playbackStatus}<br> * deezer.previous: Change binding for previous song (default is left click)<br> * deezer.next: Change binding for next song (default is right click)<br> * deezer.pause: Change binding for toggling pause (default is middle click)<br> Available options for deezer.previous, deezer.next and deezer.pause are:<br> LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, SCROLL_UP, SCROLL_DOWN<br> |
|disk |Shows free diskspace, total diskspace and the percentage of free disk space.<br><br>Parameters:<br> * disk.warning: Warning threshold in % of disk space (defaults to 80%)<br> * disk.critical: Critical threshold in % of disk space (defaults ot 90%)<br> * disk.path: Path to calculate disk usage from (defaults to /)<br> * disk.open: Which application / file manager to launch (default xdg-open)<br> * disk.format: Format string, tags {path}, {used}, {left}, {size} and {percent} (defaults to "{path} {used}/{size} ({percent:05.02f}%)")<br> * (deprecated) disk.showUsed: Show used space (defaults to yes)<br> * (deprecated) disk.showSize: Show total size (defaults to yes)<br> * (deprecated) disk.showPercent: Show usage percentage (defaults to yes)<br> |
|dnf |Displays DNF package update information (\<security\>/\<bugfixes\>/\<enhancements\>/\<other\>)<br><br>Requires the following executable:<br> * dnf<br><br>Parameters:<br> * dnf.interval: Time in minutes between two consecutive update checks (defaults to 30 minutes)<br><br> |
|docker_ps |Displays the number of docker containers running<br><br>Requires the following python packages:<br> * docker<br><br> |
|dunst |Toggle dunst notifications. |
|error |Draws an error widget.<br> |
|getcrypto |Displays the price of a cryptocurrency.<br><br>Requires the following python packages:<br> * requests<br><br>Parameters:<br> * getcrypto.interval: Interval in seconds for updating the price, default is 120, less than that will probably get your IP banned.<br> * getcrypto.getbtc: 0 for not getting price of BTC, 1 for getting it (default).<br> * getcrypto.geteth: 0 for not getting price of ETH, 1 for getting it (default).<br> * getcrypto.getltc: 0 for not getting price of LTC, 1 for getting it (default).<br> * getcrypto.getcur: Set the currency to display the price in, usd is the default.<br> |
|git |Print the branch and git status for the<br>currently focused window.<br><br>Requires:<br> * xcwd<br> * Python module 'pygit2'<br> |
|github |Displays the unread GitHub notifications for a GitHub user<br><br>Requires the following library:<br> * requests<br><br>Parameters:<br> * github.token: GitHub user access token, the token needs to have the 'notifications' scope.<br> * github.interval: Interval in minutes between updates, default is 5.<br> |
|gpmdp |Displays information about the current song in Google Play music player.<br><br>Requires the following executable:<br> * gpmdp-remote<br> |
|hddtemp |Fetch hard drive temeperature data from a hddtemp daemon<br>that runs on localhost and default port (7634)<br> |
|hostname |Displays the system hostname. |
|http_status |Display HTTP status code<br><br>Parameters:<br> * http_status.label: Prefix label (optional)<br> * http_status.target: Target to retrieve the HTTP status from<br> * http_status.expect: Expected HTTP status<br> |
|indicator |Displays the indicator status, for numlock, scrolllock and capslock <br><br>Parameters:<br> * indicator.include: Comma-separated list of interface prefixes to include (defaults to "numlock,capslock")<br> * indicator.signalstype: If you want the signali type color to be "critical" or "warning" (defaults to "warning")<br> |
|kernel |Shows Linux kernel version information |
|layout-xkb |Displays the current keyboard layout using libX11<br><br>Requires the following library:<br> * libX11.so.6<br>and python module:<br> * xkbgroup<br><br>Parameters:<br> * layout-xkb.showname: Boolean that indicate whether the full name should be displayed. Defaults to false (only the symbol will be displayed)<br> * layout-xkb.show_variant: Boolean that indecates whether the variant name should be displayed. Defaults to true.<br> |
|layout-xkbswitch |Displays and changes the current keyboard layout<br><br>Requires the following executable:<br> * xkb-switch<br> |
|layout |Displays and changes the current keyboard layout<br><br>Requires the following executable:<br> * setxkbmap<br> |
|load |Displays system load.<br><br>Parameters:<br> * load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs)<br> * load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs)<br> |
|memory |Displays available RAM, total amount of RAM and percentage available.<br><br>Parameters:<br> * memory.warning : Warning threshold in % of memory used (defaults to 80%)<br> * memory.critical: Critical threshold in % of memory used (defaults to 90%)<br> * memory.format: Format string (defaults to "{used}/{total} ({percent:05.02f}%)")<br> * memory.usedonly: Only show the amount of RAM in use (defaults to False). Same as memory.format="{used}"<br> |
|mocp |Displays information about the current song in mocp. Left click toggles play/pause. Right click toggles shuffle.<br><br>Requires the following executable:<br> * mocp<br><br>Parameters:<br> * mocp.format: Format string for the song information. Replace string sequences with the actual information:<br> %state State<br> %file File<br> %title Title, includes track, artist, song title and album<br> %artist Artist<br> %song SongTitle<br> %album Album<br> %tt TotalTime<br> %tl TimeLeft<br> %ts TotalSec<br> %ct CurrentTime<br> %cs CurrentSec<br> %b Bitrate<br> %r Sample rate<br> |
|mpd |Displays information about the current song in mpd.<br><br>Requires the following executable:<br> * mpc<br><br>Parameters:<br> * mpd.format: Format string for the song information.<br> Supported tags (see `man mpc` for additional information)<br> * {name}<br> * {artist}<br> * {album}<br> * {albumartist}<br> * {comment}<br> * {composer}<br> * {date}<br> * {originaldate}<br> * {disc}<br> * {genre}<br> * {performer}<br> * {title}<br> * {track}<br> * {time}<br> * {file}<br> * {id}<br> * {prio}<br> * {mtime}<br> * {mdate}<br> Additional tags:<br> * {position} - position of currently playing song<br> not to be confused with %position% mpc tag<br> * {duration} - duration of currently playing song<br> * {file1} - song file name without path prefix<br> if {file} = '/foo/bar.baz', then {file1} = 'bar.baz'<br> * {file2} - song file name without path prefix and extension suffix<br> if {file} = '/foo/bar.baz', then {file2} = 'bar'<br> * mpd.host: MPD host to connect to. (mpc behaviour by default)<br> * mpd.layout: Space-separated list of widgets to add. Possible widgets are the buttons/toggles mpd.prev, mpd.next, mpd.shuffle and mpd.repeat, and the main display with play/pause function mpd.main.<br> |
|network_traffic |Displays network traffic<br> * No extra configuration needed<br> |
|nic |Displays the name, IP address(es) and status of each available network interface.<br><br>Requires the following python module:<br> * netifaces<br><br>Parameters:<br> * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth,br")<br> * nic.include: Comma-separated list of interfaces to include<br> * nic.states: Comma-separated list of states to show (prefix with "^" to invert - i.e. ^down -\> show all devices that are not in state down)<br> * nic.format: Format string (defaults to "{intf} {state} {ip} {ssid}")<br> |
|notmuch_count |Displays the result of a notmuch count query<br> default : unread emails which path do not contained "Trash" (notmuch count "tag:unread AND NOT path:/.*Trash.*/")<br><br>Parameters:<br> * notmuch_count.query: notmuch count query to show result <br><br>Errors:<br> if the notmuch query failed, the shown value is -1<br><br>Dependencies:<br> notmuch (https://notmuchmail.org/)<br> |
|nvidiagpu |Displays GPU name, temperature and memory usage.<br><br>Parameters:<br> * nvidiagpu.format: Format string (defaults to "{name}: {temp}°C %{usedmem}/{totalmem} MiB")<br> Available values are: {name} {temp} {mem_used} {mem_total} {fanspeed} {clock_gpu} {clock_mem}<br><br>Requires nvidia-smi<br> |
|pacman |Displays update information per repository for pacman.<br><br>Parameters:<br> * pacman.sum: If you prefere displaying updates with a single digit (defaults to "False")<br><br>Requires the following executables:<br> * fakeroot<br> * pacman<br> |
|pihole |Displays the pi-hole status (up/down) together with the number of ads that were blocked today<br>Parameters:<br> * pihole.address : pi-hole address (e.q: http://192.168.1.3)<br> * pihole.pwhash : pi-hole webinterface password hash (can be obtained from the /etc/pihole/SetupVars.conf file)<br> |
|ping |Periodically checks the RTT of a configurable host using ICMP echos<br><br>Requires the following executable:<br> * ping<br><br>Parameters:<br> * ping.interval: Time in seconds between two RTT checks (defaults to 60)<br> * ping.address : IP address to check<br> * ping.timeout : Timeout for waiting for a reply (defaults to 5.0)<br> * ping.probes : Number of probes to send (defaults to 5)<br> * ping.warning : Threshold for warning state, in seconds (defaults to 1.0)<br> * ping.critical: Threshold for critical state, in seconds (defaults to 2.0)<br> |
|pomodoro |Display and run a Pomodoro timer.<br>Left click to start timer, left click again to pause.<br>Right click will cancel the timer.<br><br>Parameters:<br> * pomodoro.work: The work duration of timer in minutes (defaults to 25)<br> * pomodoro.break: The break duration of timer in minutes (defaults to 5)<br> * pomodoro.format: Timer display format with "%m" and "%s" for minutes and seconds (defaults to "%m:%s")<br> Examples: "%m min %s sec", "%mm", "", "timer"<br> * pomodoro.notify: Notification command to run when timer ends/starts (defaults to nothing)<br> Example: 'notify-send "Time up!"'. If you want to chain multiple commands,<br> please use an external wrapper script and invoke that. The module itself does<br> not support command chaining (see https://github.com/tobi-wan-kenobi/bumblebee-status/issues/532<br> for a detailled explanation)<br> |
|prime |Displays and changes the current selected prime video card<br><br>Left click will call 'sudo prime-select nvidia'<br>Right click will call 'sudo prime-select nvidia'<br><br>Running these commands without a password requires editing your sudoers file<br>(always use visudo, it's very easy to make a mistake and get locked out of your computer!)<br><br>sudo visudo -f /etc/sudoers.d/prime<br><br>Then put a line like this in there:<br><br> user ALL=(ALL) NOPASSWD: /usr/bin/prime-select<br><br>If you can't figure out the sudoers thing, then don't worry, it's still really useful.<br><br>Parameters:<br> * prime.nvidiastring: String to use when nvidia is selected (defaults to "intel")<br> * prime.intelstring: String to use when intel is selected (defaults to "intel")<br><br>Requires the following executable:<br> * prime-select<br><br> |
|progress |<br>Show progress for cp, mv, dd, ...<br><br>Parameters:<br> * progress.placeholder: Text to display while no process is running (defaults to "n/a")<br> * progress.barwidth: Width of the progressbar if it is used (defaults to 8)<br> * progress.format: Format string (defaults to "{bar} {cmd} {arg}")<br> Available values are: {bar} {pid} {cmd} {arg} {percentage} {quantity} {speed} {time}<br> * progress.barfilledchar: Character used to draw the filled part of the bar (defaults to "#"), notice that it can be a string<br> * progress.baremptychar: Character used to draw the empty part of the bar (defaults to "-"), notice that it can be a string<br><br>Requires the following executable:<br> * progress<br> |
|publicip |Displays public IP address<br><br>Requires the following python packages:<br> * requests<br><br>Parameters:<br> * publicip.region: us-central (default), us-east, us-west, uk, de, pl, nl<br> * publicip.service: web address that returns plaintext ip address (ex. "http://l2.io/ip")<br> |
|pulseaudio |Displays volume and mute status and controls for PulseAudio devices. Use wheel up and down to change volume, left click mutes, right click opens pavucontrol.<br><br>Aliases: pasink (use this to control output instead of input), pasource<br><br>Parameters:<br> * pulseaudio.autostart: If set to "true" (default is "false"), automatically starts the pulseaudio daemon if it is not running<br> * pulseaudio.percent_change: How much to change volume by when scrolling on the module (default is 2%)<br> * pulseaudio.limit: Upper limit for setting the volume (default is 0%, which means "no limit")<br> Note: If the left and right channels have different volumes, the limit might not be reached exactly.<br> * pulseaudio.showbars: 1 for showing volume bars, requires --markup=pango;<br> 0 for not showing volume bars (default)<br><br>Requires the following executable:<br> * pulseaudio<br> * pactl<br> * pavucontrol<br> |
|redshift |Displays the current color temperature of redshift<br><br>Requires the following executable:<br> * redshift<br><br>Parameters:<br> * redshift.location : location provider, either of "geoclue2" (default), "ipinfo" (requires the requests package), or "manual"<br> * redshift.lat : latitude if location is set to "manual"<br> * redshift.lon : longitude if location is set to "manual"<br> |
|rotation |Shows a widget for each connected screen and allows the user to loop through different orientations.<br><br>Requires the following executable:<br> * xrandr<br> |
|rss |RSS news ticker<br><br>Fetches rss news items and shows these as a news ticker.<br>Left-clicking will open the full story in a browser.<br>New stories are highlighted.<br><br>Parameters:<br> * rss.feeds : Space-separated list of RSS URLs<br> * rss.length : Maximum length of the module, default is 60<br> |
|sensors |Displays sensor temperature<br><br>Parameters:<br> * sensors.path: path to temperature file (default /sys/class/thermal/thermal_zone0/temp).<br> * sensors.json: if set to "true", interpret sensors.path as JSON "path" in the output<br> of "sensors -j" (i.e. \<key1\>/\<key2\>/.../\<value\>), for example, path could<br> be: "coretemp-isa-00000/Core 0/temp1_input" (defaults to "false")<br> * sensors.match: (fallback) Line to match against output of 'sensors -u' (default: temp1_input)<br> * sensors.match_pattern: (fallback) Line to match against before temperature is read (no default)<br> * sensors.match_number: (fallback) which of the matches you want (default -1: last match).<br> * sensors.show_freq: whether to show CPU frequency. (default: true)<br> |
|sensors2 |Displays sensor temperature and CPU frequency<br><br>Parameters:<br><br> * sensors2.chip: "sensors -u" compatible filter for chip to display (default to empty - show all chips)<br> * sensors2.showcpu: Enable or disable CPU frequency display (default: true)<br> * sensors2.showtemp: Enable or disable temperature display (default: true)<br> * sensors2.showfan: Enable or disable fan display (default: true)<br> * sensors2.showother: Enable or display "other" sensor readings (default: false)<br> * sensors2.showname: Enable or disable show of sensor name (default: false)<br> * sensors2.chip_include: Comma-separated list of chip to include (defaults to "" will include all by default, example: "coretemp,bat") <br> * sensors2.chip_exclude:Comma separated list of chip to exclude (defaults to "" will exlude none by default) <br> * sensors2.field_include: Comma separated list of chip to include (defaults to "" will include all by default, example: "temp,fan") <br> * sensors2.field_exclude: Comma separated list of chip to exclude (defaults to "" will exclude none by default) <br> * sensors2.chip_field_exclude: Comma separated list of chip field to exclude (defaults to "" will exclude none by default, example: "coretemp-isa-0000.temp1,coretemp-isa-0000.fan1") <br> * sensors2.chip_field_include: Comma-separated list of adaper field to include (defaults to "" will include all by default) <br>|
|shell | Execute command in shell and print result<br><br>Few command examples:<br> 'ping 1.1.1.1 -c 1 | grep -Po "(?\<=time=)\d+(\.\d+)? ms"'<br> 'echo "BTC=$(curl -s rate.sx/1BTC | grep -Po "^\d+")USD"'<br> 'curl -s https://wttr.in/London?format=%l+%t+%h+%w'<br> 'pip3 freeze | wc -l'<br> 'any_custom_script.sh | grep arguments'<br><br>Parameters:<br> * shell.command: Command to execute<br> Use single parentheses if evaluating anything inside (sh-style)<br> For example shell.command='echo $(date +"%H:%M:%S")'<br> But NOT shell.command="echo $(date +'%H:%M:%S')"<br> Second one will be evaluated only once at startup<br> * shell.interval: Update interval in seconds<br> (defaults to 1s == every bumblebee-status update)<br> * shell.async: Run update in async mode. Won't run next thread if<br> previous one didn't finished yet. Useful for long<br> running scripts to avoid bumblebee-status freezes<br> (defaults to False)<br> |
|shortcut |Shows a widget per user-defined shortcut and allows to define the behaviour<br>when clicking on it.<br><br>For more than one shortcut, the commands and labels are strings separated by<br>a demiliter (; semicolon by default).<br><br>For example in order to create two shortcuts labeled A and B with commands<br>cmdA and cmdB you could do:<br><br> ./bumblebee-status -m shortcut -p shortcut.cmd="ls;ps" shortcut.label="A;B"<br><br>Parameters:<br> * shortcut.cmds : List of commands to execute<br> * shortcut.labels: List of widgets' labels (text)<br> * shortcut.delim : Commands and labels delimiter (; semicolon by default)<br> |
|spaceapi |Displays the state of a Space API endpoint<br>Space API is an API for hackspaces based on JSON. See spaceapi.io for<br>an example.<br><br>Requires the following libraries:<br> * requests<br> * regex<br><br>Parameters:<br> * spaceapi.url: String representation of the api endpoint<br> * spaceapi.format: Format string for the output<br><br>Format Strings:<br> * Format strings are indicated by double %%<br> * They represent a leaf in the JSON tree, layers seperated by "."<br> * Boolean values can be overwritten by appending "%true%false"<br> in the format string<br> * Example: to reference "open" in "{"state":{"open": true}}"<br> you would write "%%state.open%%", if you also want<br> to say "Open/Closed" depending on the boolean you<br> would write "%%state.open%Open%Closed%%"<br> |
|spacer |Draws a widget with configurable text content.<br><br>Parameters:<br> * spacer.text: Widget contents (defaults to empty string)<br> |
|spotify |Displays the current song being played<br>Requires the following library:<br> * python-dbus<br>Parameters:<br> * spotify.format: Format string (defaults to "{artist} - {title}")<br> Available values are: {album}, {title}, {artist}, {trackNumber}, {playbackStatus}<br> * spotify.previous: Change binding for previous song (default is left click)<br> * spotify.next: Change binding for next song (default is right click)<br> * spotify.pause: Change binding for toggling pause (default is middle click)<br> Available options for spotify.previous, spotify.next and spotify.pause are:<br> LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, SCROLL_UP, SCROLL_DOWN<br> |
|stock |Display a stock quote from worldtradingdata.com<br><br>Requires the following python packages:<br> * requests<br><br>Parameters:<br> * stock.symbols : Comma-separated list of symbols to fetch<br> * stock.change : Should we fetch change in stock value (defaults to True)<br> |
|sun |Displays sunrise and sunset times<br><br>Requires the following python packages:<br> * requests<br> * suntime<br><br>Parameters:<br> * cpu.lat : Latitude of your location<br> * cpu.lon : Longitude of your location<br> |
|system | system module<br><br>adds the possibility to<br> * shutdown<br> * reboot<br>the system.<br> <br>Per default a confirmation dialog is shown before the actual action is performed.<br> <br>Parameters:<br> * system.confirm: show confirmation dialog before performing any action (default: true) <br> * system.reboot: specify a reboot command (defaults to 'reboot')<br> * system.shutdown: specify a shutdown command (defaults to 'shutdown -h now')<br> * system.logout: specify a logout command (defaults to 'i3exit logout')<br> * system.switch_user: specify a command for switching the user (defaults to 'i3exit switch_user')<br> * system.lock: specify a command for locking the screen (defaults to 'i3exit lock')<br> * system.suspend: specify a command for suspending (defaults to 'i3exit suspend')<br> * system.hibernate: specify a command for hibernating (defaults to 'i3exit hibernate')<br> |
|taskwarrior |Displays the number of pending tasks in TaskWarrior.<br><br>Requires the following library:<br> * taskw<br><br>Parameters:<br> * taskwarrior.taskrc : path to the taskrc file (defaults to ~/.taskrc)<br> |
|test |Test module<br> |
|title |Displays focused i3 window title.<br><br>Requirements:<br> * i3ipc<br><br>Parameters:<br> * title.max : Maximum character length for title before truncating. Defaults to 64.<br> * title.placeholder : Placeholder text to be placed if title was truncated. Defaults to "...".<br> * title.scroll : Boolean flag for scrolling title. Defaults to False<br> |
|todo |Displays the number of todo items from a text file<br><br>Requirements:<br> * xdg-open<br><br>Parameters:<br> * todo.file: File to read TODOs from (defaults to ~/Documents/todo.txt)<br> |
|traffic |Displays network IO for interfaces.<br><br>Parameters:<br> * traffic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")<br> * traffic.states: Comma-separated list of states to show (prefix with "^" to invert - i.e. ^down -\> show all devices that are not in state down)<br> * traffic.showname: If set to False, hide network interface name (defaults to True)<br> * traffic.format: Format string for download/upload speeds.<br> Defaults to "{:.2f}"<br> * traffic.graphlen: Graph lenth in seconds. Positive even integer. Each<br> char shows 2 seconds. If set, enables up/down traffic<br> graphs<br> |
|twmn |Toggle twmn notifications. |
|uptime |Displays the system uptime. |
|vault |Copy passwords from a password store into the clipboard (currently supports only "pass")<br><br>Many thanks to [@bbernhard](https://github.com/bbernhard) for the idea!<br><br>Parameters:<br> * vault.duration: Duration until password is cleared from clipboard (defaults to 30)<br> * vault.location: Location of the password store (defaults to ~/.password-store)<br> * vault.offx: x-axis offset of popup menu (defaults to 0)<br> * vault.offy: y-axis offset of popup menu (defaults to 0)<br> |
|vpn | Displays the VPN profile that is currently in use.<br><br> Left click opens a popup menu that lists all available VPN profiles and allows to establish<br> a VPN connection using that profile.<br><br> Prerequisites:<br> * nmcli needs to be installed and configured properly.<br> To quickly test, whether nmcli is working correctly, type "nmcli -g NAME,TYPE,DEVICE con" which<br> lists all the connection profiles that are configured. Make sure that your VPN profile is in that list!<br><br> e.g: to import a openvpn profile via nmcli:<br> sudo nmcli connection import type openvpn file \</path/to/your/openvpn/profile.ovpn\><br> |
|weather |Displays the temperature on the current location based on the ip<br><br>Requires the following python packages:<br> * requests<br><br>Parameters:<br> * weather.location: Set location, defaults to 'auto' for getting location from http://ipinfo.io<br> If set to a comma-separated list, left-click and right-click can be used to rotate the locations.<br> Locations should be city names or city ids.<br> * weather.unit: metric (default), kelvin, imperial<br> * weather.showcity: If set to true, show location information, otherwise hide it (defaults to true)<br> * weather.showminmax: If set to true, show the minimum and maximum temperature, otherwise hide it (defaults to false)<br> * weather.apikey: API key from http://api.openweathermap.org<br> |
|xkcd |Opens a random xkcd comic in the browser. |
|xrandr |Shows a widget for each connected screen and allows the user to enable/disable screens.<br><br>Parameters:<br> * xrandr.overwrite_i3config: If set to 'true', this module assembles a new i3 config<br> every time a screen is enabled or disabled by taking the file "~/.config/i3/config.template"<br> and appending a file "~/.config/i3/config.\<screen name\>" for every screen.<br> * xrandr.autoupdate: If set to 'false', does *not* invoke xrandr automatically. Instead, the<br> module will only refresh when displays are enabled or disabled (defaults to true)<br><br>Requires the following python module:<br> * (optional) i3 - if present, the need for updating the widget list is auto-detected<br><br>Requires the following executable:<br> * xrandr<br> |
|zpool |Displays info about zpools present on the system<br><br>Parameters:<br> * zpool.list: Comma-separated list of zpools to display info for. If empty, info for all zpools<br> is displayed. (Default: "")<br> * zpool.format: Format string, tags {name}, {used}, {left}, {size}, {percentfree}, {percentuse},<br> {status}, {shortstatus}, {fragpercent}, {deduppercent} are supported.<br> (Default: "{name} {used}/{size} ({percentfree}%)")<br> * zpool.showio: Show also widgets detailing current read and write I/O (Default: true)<br> * zpool.ioformat: Format string for I/O widget, tags {ops} (operations per seconds) and {band}<br> (bandwidth) are supported. (Default: "{band}")<br> * zpool.warnfree: Warn if free space is below this percentage (Default: 10)<br> * zpool.sudo: Use sudo when calling the `zpool` binary. (Default: false)<br><br>Option `zpool.sudo` is intended for Linux users using zfsonlinux older than 0.7.0: In pre-0.7.0<br>releases of zfsonlinux regular users couldn't invoke even informative commands such as<br>`zpool list`. If this option is true, command `zpool list` is invoked with sudo. If this option<br>is used, the following (or ekvivalent) must be added to the `sudoers(5)`:<br><br>```<br>\<username/ALL\> ALL = (root) NOPASSWD: /usr/bin/zpool list<br>```<br><br>Be aware of security implications of doing this!<br> |

45
PKGBUILD.template Normal file
View file

@ -0,0 +1,45 @@
# Maintainer: Tobias Witek <tobi@tobi-wan-kenobi.at>
# Contributor: Daniel M. Capella <polycitizen@gmail.com>
# Contributor: spookykidmm <https://github.com/spookykidmm>
pkgname=bumblebee-status
pkgver=<PKGVERSION>
pkgrel=1
pkgdesc='Modular, theme-able status line generator for the i3 window manager'
arch=('any')
url=https://github.com/tobi-wan-kenobi/bumblebee-status
license=('MIT')
depends=('python' 'python-netifaces' 'python-psutil' 'python-requests')
optdepends=('xorg-xbacklight: to display a displays brightness'
'xorg-xset: enable/disable automatic screen locking'
'libnotify: enable/disable automatic screen locking'
'dnf: display DNF package update information'
'xorg-setxkbmap: display/change the current keyboard layout'
'redshift: display the redshifts current color'
'pulseaudio: control pulseaudio sink/sources'
'xorg-xrandr: enable/disable screen outputs'
'pacman: display current status of pacman'
'iputils: display a ping'
'python-i3ipc: display titlebar'
'fakeroot: dependency of the pacman module'
'python-pytz: timezone conversion for datetimetz module'
'python-tzlocal: retrieve system timezone for datetimetz module'
)
source=("$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz")
sha512sums=('<SHA512SUM>')
package() {
install -d "$pkgdir"/usr/bin \
"$pkgdir"/usr/share/$pkgname/bumblebee_status/{core,util} \
"$pkgdir"/usr/share/$pkgname/bumblebee_status/modules/{core,contrib} \
"$pkgdir"/usr/share/$pkgname/themes/icons
ln -s /usr/share/$pkgname/$pkgname "$pkgdir"/usr/bin/$pkgname
ln -s /usr/share/$pkgname/bumblebee-ctl "$pkgdir"/usr/bin/bumblebee-ctl
cd $pkgname-$pkgver
cp -a --parents $pkgname bumblebee_status/{,core/,util/,modules/core/,modules/contrib/}*.py \
themes/{,icons/}*.json $pkgdir/usr/share/$pkgname
cp -r bin $pkgdir/usr/share/$pkgname/
install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
}

364
README.md
View file

@ -1,20 +1,77 @@
# bumblebee-status
<img src="https://github.com/kellya/bumblebee-status-icon/blob/main/img/bumblebee_status_rtl.svg" width="50" style="display:inline-block">bumblebee-status
=====================================================
logo courtesy of [kellya](https://github.com/kellya) - thank you!
[![Documentation Status](https://readthedocs.org/projects/bumblebee-status/badge/?version=main)](https://bumblebee-status.readthedocs.io/en/main/?badge=main)
![Commits since release](https://img.shields.io/github/commits-since/tobi-wan-kenobi/bumblebee-status/latest)
![AUR version (release)](https://img.shields.io/aur/version/bumblebee-status)
![AUR version (git)](https://img.shields.io/aur/version/bumblebee-status-git)
![PyPI version](https://img.shields.io/pypi/v/bumblebee-status)
![Contributors](https://img.shields.io/github/contributors-anon/tobi-wan-kenobi/bumblebee-status)
[![Tests](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/autotest.yml/badge.svg?branch=main)](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/autotest.yml)
[![Build Status](https://travis-ci.org/tobi-wan-kenobi/bumblebee-status.svg?branch=master)](https://travis-ci.org/tobi-wan-kenobi/bumblebee-status)
[![Code Climate](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/gpa.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
[![Test Coverage](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/coverage.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/coverage)
[![Issue Count](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/issue_count.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
[![CodeQL](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/codeql-analysis.yml)
![License](https://img.shields.io/github/license/tobi-wan-kenobi/bumblebee-status)
**Many, many thanks to all contributors! As of now, 57 of the modules are from various contributors (!), and only 19 from myself.**
**Many, many thanks to all contributors! I am still amazed by and deeply grateful for how many PRs this project gets.**
![List of modules](./Modules.md)
[Click here for a list of available modules](https://bumblebee-status.readthedocs.io/en/main/modules.html)
![Solarized Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-solarized.png)
![Solarized Powerline](screenshots/themes/powerline-solarized.png)
bumblebee-status is a modular, theme-able status line generator for the [i3 window manager](https://i3wm.org/).
**How to install**
Focus is on:
* ease of use, sane defaults (no mandatory configuration file)
* [easy creation of custom themes](https://bumblebee-status.readthedocs.io/en/main/development/theme.html)
* [easy creation of custom modules](https://bumblebee-status.readthedocs.io/en/main/development/module.html)
I hope you like it and I appreciate any kind of feedback: bug reports, feature requests, etc. :)
Thanks a lot!
Required i3wm version: 4.12+ (in earlier versions, blocks won't have background colors)
Supported Python versions: 3.4, 3.5, 3.6, 3.7, 3.8, 3.9
Supported FontAwesome version: 4 (free version of 5 doesn't include some of the icons)
---
***NOTE***
The default branch for this project is `main`. If you are curious why: [ZDNet:github-master-alternative](https://www.zdnet.com/article/github-to-replace-master-with-alternative-term-to-avoid-slavery-references/)
---
Example usage:
```
bar {
status_command <path>/bumblebee-status -m cpu memory battery time \
pasink pasource -p time.format="%H:%M" -t solarized
}
```
# Documentation
See [the docs](https://bumblebee-status.readthedocs.io) for detailed documentation.
See [FAQ](https://bumblebee-status.readthedocs.io/en/main/FAQ.html) for. well, FAQs.
Other resources:
* A list of [available modules](https://bumblebee-status.readthedocs.io/en/main/modules.html)
* [How to write a module](https://bumblebee-status.readthedocs.io/en/main/development/module.html)
* [How to write a theme](https://bumblebee-status.readthedocs.io/en/main/development/theme.html)
# Installation
```
# from git (development snapshot)
$ git clone git://github.com/tobi-wan-kenobi/bumblebee-status
# from AUR:
git clone https://aur.archlinux.org/bumblebee-status.git
cd bumblebee-status
@ -25,83 +82,21 @@ makepkg -sicr
pip install --user bumblebee-status
```
Focus is on:
* Ease of use (no configuration files!)
* Theme support
* Extensibility (of course...)
There is also a SlackBuild available here: [slackbuilds:bumblebee-status](http://slackbuilds.org/repository/14.2/desktop/bumblebee-status/) - many thanks to [@Tonus1](https://github.com/Tonus1)!
One thing I like in particular: You can use the mouse wheel up/down to switch workspaces forward and back everywhere throughout the bar (unless you have mapped the mouse wheel buttons to another action for a widget, in which case this doesn't work while hovering that particular widget).
I hope you like it and appreciate any kind of feedback: Bug reports, Feature requests, etc. :)
Thanks a lot!
Required i3wm version: 4.12+ (in earlier versions, blocks won't have background colors)
Supported Python versions: 2.7, 3.3, 3.4, 3.5, 3.6
Supported FontAwesome version: 4 (free version of 5 doesn't include some of the icons)
Explicitly unsupported Python versions: 3.2 (missing unicode literals)
:information_source: The ![Font Awesome](https://fontawesome.com/) is required for all themes that contain icons (because that is the font that includes these icons). Please refer to your distribution's package management on how to install them, or get them from their website directly. Also, please note that Font Awesome removed some icons used by `bumblebee-status` from the free set in version 5, so if possible, stick with 4.
```
# Font Awesome installation instructions
# Arch Linux
$ sudo pacman -S awesome-terminal-fonts
# FreeBSD
$ sudo pkg install font-awesome
$ sudo pkg install py36-tzlocal py36-pytz py36-netifaces py36-psutil py36-requests #for dependencies
# Other
# see https://github.com/gabrielelana/awesome-terminal-fonts
```
Example usage:
```
bar {
status_command <path>/bumblebee-status -m cpu memory battery time pasink pasource -p time.format="%H:%M" -t solarized
}
```
# Documentation
See [the wiki](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki) for documentation.
See [FAQ](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/FAQ) for, well, FAQs.
Other resources:
* A list of [available modules](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/Available-Modules)
* [How to write a theme](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-theme)
* [How to write a module](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-module)
User contributions:
[@somospocos:bumblebee-status-contrib](https://github.com/somospocos/bumblebee-status-contrib): Collected resources and useful tricks by @somospocos
[@somospocos:bumblebee-bridge-dwm](https://github.com/somospocos/bumblebee-bridge-dwm): Bridge bumblebee-status output into dwm status bar
[@somospocos:bumblebee-bridge-dzen2](https://github.com/somospocos/bumblebee-bridge-dzen2): Bridge bumblebee-status output into dzen2
# Installation
```
$ git clone git://github.com/tobi-wan-kenobi/bumblebee-status
```
An ebuild, for Gentoo Linux, is available on [gallifrey overlay](https://github.com/fedeliallalinea/gallifrey/tree/master/x11-misc/bumblebee-status). Instructions for adding the overlay can be found [here](https://github.com/fedeliallalinea/gallifrey/blob/master/README.md).
# Dependencies
[Available modules](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/Available-Modules) lists the dependencies (Python modules and external executables)
[Available modules](https://bumblebee-status.readthedocs.io/en/main/modules.html) lists the dependencies (Python modules and external executables)
for each module. If you are not using a module, you don't need the dependencies.
Some themes (e.g. all powerline themes) require Font Awesome http://fontawesome.io/ and a powerline-compatible font (powerline-fonts) https://github.com/powerline/fonts
# Usage
## Normal usage
In your i3wm configuration, modify the *status_command* for your i3bar like this:
```
```bash
bar {
status_command <path to bumblebee-status/bumblebee-status> \
-m <list of modules> \
@ -111,228 +106,45 @@ bar {
```
You can retrieve a list of modules (and their parameters) and themes by entering:
```
```bash
$ cd bumblebee-status
$ ./bumblebee-status -l themes
$ ./bumblebee-status -l modules
```
Any parameter you can specify with `-p <name>=<value>`, you can alternatively specify in `~/.bumblebee-status.conf` or `~/.config/bumblebee-status.conf`. This parameters act as a **fallback**, so values specified with `-p` have priority.
Parameters can also be used to override theme settings, such as:
```
$ ./bumblebee-status -p <module>.theme.<theme field>=<value>
# for example, to get a spacer with a red background:
$ ./bumblebee-status -m spacer -p spacer.theme.bg=#ff0000
```
Configuration files have a format like this:
```
$ cat ~/.bumblebee-status.conf
[module-parameters]
<key> = <value>
```
For example:
```
$ cat ~/.bumblebee-status.conf
[module-parameters]
github.token=abcdefabcdef12345
```
To change the update interval, use:
```
```bash
$ ./bumblebee-status -m <list of modules> -p interval=<interval in seconds>
```
The update interval can also be changed on a per-module basis, like this:
```bash
$ ./bumblebee-status -m cpu memory -p cpu.interval=5s memory.interval=1m
```
All modules can be given "aliases" using `<module name>:<alias>`, by which they can be parametrized, for example:
```bash
$ ./bumblebee-status -m disk:root disk:home -p root.path=/ home.path=/home
```
As a simple example, this is what my i3 configuration looks like:
```
```bash
bar {
font pango:Inconsolata 10
position top
tray_output none
status_command ~/.i3/bumblebee-status/bumblebee-status -m nic disk:root cpu memory battery date time pasink pasource dnf -p root.path=/ time.format="%H:%M CW %V" date.format="%a, %b %d %Y" -t solarized-powerline
status_command ~/.i3/bumblebee-status/bumblebee-status -m nic disk:root cpu \
memory battery date time pasink pasource dnf \
-p root.path=/ time.format="%H:%M CW %V" date.format="%a, %b %d %Y" \
-t solarized-powerline
}
```
Restart i3wm and - that's it!
## Events
By default, the following events are handled:
- Mouse-Wheel on any module moves to the next/previous i3 workspace
- Left-click on the "disk" module opens the specified path in nautilus
- Left-click on either "memory" or "cpu" opens gnome-system-monitor
- Left-click on a "pulseaudio" (or pasource/pasink) module toggles the mute state
- Right-click on a "pulseaudio" module opens pavucontrol
- Mouse-Wheel up/down on a "pulseaudio" module raises/lowers the volume
By default, the Mouse-Wheel wraps for the current output. You can disable this behavior by providing the parameter `engine.workspacewrap=false` (starting with version 1.4.5). Also, you can completely disable output switching by using `engine.workspacewheel=false`.
You can provide your own handlers to any module by using the following "special" configuration parameters:
- left-click
- right-click
- middle-click
- wheel-up
- wheel-down
For example, to execute "pavucontrol" whenever you left-click on the nic module, you could write:
`$ bumblebee-status -p nic.left-click="pavucontrol"`
In the string, you can use the following format identifiers:
- name
- instance
- button
For example:
`$ bumblebee-status -p disk.left-click="nautilus {instance}"`
Advanced usage:
You can also "programmatically" trigger click events like this:
```
$ bumblebee-ctl --button <button> --id <ID of the widget> --module <name of the module>
```
For that to work, you need to know the ID of the widget - this can be configured by adding the `<name>.id` parameter - it's a comma-separated list with one entry per widget (for multi-widget modules).
Example:
```
$ bumblebee-status -m cmus -p cmus.id="prev,main,next,shuffle,repeat"
# to "simulate" a left-click on "next":
$ bumblebee-ctl --button left-mouse --module cmus --id next
```
For a full synopsis, please see `bumblebee-ctl --help`.
## Errors
If errors occur, you should see them in the i3bar itself. If that does not work, or you need more information for troubleshooting, you can activate a debug log using the `-d` or `--debug` switch:
```
$ ./bumblebee-status -d -m <list of modules>
```
This will create a file called `~/bumblebee-status-debug.log` by default. The file name can be changed by using the `-f` or `--logfile` option.
### Advanced Usage
If you want to have a minimal bar that stays out of the way, you can use the `-a` or `--autohide` switch to specify a list of module names. All those modules will only be displayed when (and as long as) their state is either warning or critical (high CPU usage, low disk space, etc.). As long as the module is in a "normal" state and does not require attention, it will remain hidden.
Note that this parameter is specified *in addition* to `-m` (i.e. to autohide the CPU module, you would use `bumblebee-status -m cpu memory traffic -a cpu`).
# Required Modules
Modules and commandline utilities are only required for modules, the core itself has no external dependencies at all.
* psutil (for the modules 'cpu', 'memory', 'traffic')
* netifaces (for the modules 'nic', 'traffic')
* requests (for the modules 'weather', 'github', 'getcrypto', 'stock', 'currency', 'sun')
* power (for the module 'battery')
* dbus (for the module 'spotify', 'deezer')
* i3ipc (for the module 'title')
* pacman-contrib (for module 'arch-update')
* docker (for the module 'docker_ps')
* pytz (for the module 'datetimetz')
* localtz (for the module 'datetimetz')
* suntime (for the module 'sun')
* feedparser (for the module 'rss')
# Required commandline utilities
* xset (for the module 'caffeine')
* notify-send (for the module 'caffeine')
* cmus-remote (for the module 'cmus')
* dnf (for the module 'dnf')
* gpmdp-remote (for the module 'gpmdp')
* setxkbmap (for the module 'layout')
* fakeroot (for the module 'pacman')
* pacman (for the module 'pacman')
* pactl (for the module 'pulseaudio')
* ping (for the module 'ping')
* redshift (for the module 'redshift')
* xrandr (for the module 'xrandr')
* mpc (for the module 'mpd')
* bluez / blueman (for module 'bluetooth')
* dbus-send (for module 'bluetooth')
* nvidia-smi (for module 'nvidiagpu')
* sensors (for module 'sensors', as fallback)
* zpool (for module 'zpool')
* progress (for module 'progress')
* i3exit (for module 'system')
* dunst (for module 'dunst')
* hddtemp (for module 'hddtemp')
# Examples
Here are some screenshots for all themes that currently exist:
:exclamation: Some themes (all 'Powerline' themes) require [Font Awesome](http://fontawesome.io/) and a powerline-compatible font ([powerline-fonts](https://github.com/powerline/fonts), for example) to display all icons correctly.
:exclamation: If you want to add your own theme, just drop it into `~/.config/bumblebee-status/themes/`
Gruvbox Powerline (`-t gruvbox-powerline`) (contributed by [@TheEdgeOfRage](https://github.com/TheEdgeOfRage)):
![Gruvbox Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-gruvbox.png)
Gruvbox Powerline Light (`-t gruvbox-powerline-light`) (contributed by [freed00m](https://github.com/freed00m)):
![Gruvbox Powerline Light](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/gruvbox-powerline-light.png)
Solarized Powerline (`-t solarized-powerline`):
![Solarized Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-solarized.png)
Gruvbox (`-t gruvbox`):
![Gruvbox](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/gruvbox.png)
Gruvbox Light (`-t gruvbox-light`) (contributed by [freed00m](https://github.com/freed00m)):
![Gruvbox Light](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/gruvbox-light.png)
Solarized (`-t solarized`):
![Solarized](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/solarized.png)
Powerline (`-t powerline`):
![Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline.png)
Greyish Powerline (`-t greyish-powerline`) (contributed by Joshua Bark):
![Greyish Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-greyish.png)
Iceberg (`-t iceberg`) (contributed by [whzup](https://github.com/whzup)):
![Iceberg](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/iceberg.png)
Iceberg Powerline (`-t iceberg-powerline`) (contributed by [whzup](https://github.com/whzup)):
![Iceberg Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/iceberg-powerline.png)
Iceberg Dark Powerline (`-t iceberg-dark-powerline`) (contributed by [gkeep](https://github.com/gkeep)):
![Iceberg Dark Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/iceberg-dark-powerline.png)
Iceberg Rainbow (`-t iceberg-rainbow`) (contributed by [whzup](https://github.com/whzup)):
![Iceberg Rainbow](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/iceberg-rainbow.png)
One Dark Powerline (`-t onedark-powerline`) (contributed by [dillasyx](https://github.com/dillasyx)):
![One Dark Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/onedark-powerline.png)
Dracula Powerline (-t dracula-powerline) (contributed by [xsteadfastx](https://github.com/xsteadfastx)):
![Dracula Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/dracula-powerline.png)
Nord Powerline (-t nord-powerline) (contributed by [uselessthird](https://github.com/uselessthird)):
![Nord Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/nord-powerline.png)
Default (nothing or `-t default`):
![Default](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/default.png)
[List of themes](https://bumblebee-status.readthedocs.io/en/main/themes.html)

View file

@ -1,5 +0,0 @@
Package: bumblebee-status
Version: 0
Maintainer: tobi-wan-kenobi
Architecture: all
Description: bumblebee-status is a modular, theme-able status line generator for the i3 window manager.

View file

@ -1,5 +0,0 @@
#!/bin/sh
ln -s /usr/share/bumblebee-status/bumblebee-status /usr/local/bin/bumblebee-status

View file

@ -1,35 +1,53 @@
#!/usr/bin/env python
import os
import argparse
import json
import glob
import socket
button = {
'left-mouse': 1,
'middle-mouse': 2,
'right-mouse': 3,
'wheel-up': 4,
'wheel-down': 5,
"left-mouse": 1,
"middle-mouse": 2,
"right-mouse": 3,
"wheel-up": 4,
"wheel-down": 5,
"update": -1,
}
def main():
parser = argparse.ArgumentParser(description='send commands to bumblebee-status')
parser.add_argument('-b', '--button', choices=['left-mouse', 'right-mouse', 'middle-mouse', 'wheel-up', 'wheel-down'], help='button to emulate', default='left-mouse')
parser.add_argument('-i', '--id', help='ID of widget to trigger', required=True)
parser.add_argument('-m', '--module', help='name of the module to trigger', required=True)
parser = argparse.ArgumentParser(description="send commands to bumblebee-status")
parser.add_argument(
"-b",
"--button",
choices=["left-mouse", "right-mouse", "middle-mouse", "wheel-up", "wheel-down", "update"],
help="button to emulate",
default="left-mouse",
)
parser.add_argument("-i", "--id", help="ID of widget to trigger")
parser.add_argument(
"-m", "--module", help="name of the module to trigger", required=True
)
args = parser.parse_args()
for f in glob.glob('/tmp/.bumblebee-status.*'):
print('accessing {}'.format(f))
for f in glob.glob("/tmp/.bumblebee-status.*"):
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(f)
s.sendall(json.dumps({
'name': args.module,
'instance': args.id,
'button': button[args.button],
}).encode('ascii'))
try:
s.connect(f)
s.sendall(
json.dumps(
{
"name": args.module,
"instance": args.id,
"button": button[args.button],
}
).encode("ascii")
)
except Exception as e:
os.remove(f)
if __name__ == "__main__":
main()

View file

@ -1,84 +1,177 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import os
import sys
import logging
import json
import time
import signal
import bumblebee.theme
import bumblebee.engine
import bumblebee.config
import bumblebee.output
import bumblebee.input
import bumblebee.modules.error
import socket
import logging
import threading
try:
reload(sys)
sys.setdefaultencoding('UTF8')
except Exception:
pass
import bumblebee_status.discover
bumblebee_status.discover.discover()
import core.config
import core.output
import core.module
import core.input
import core.event
import util.format
started = False
class CommandSocket(object):
def __init__(self):
self.__name = "/tmp/.bumblebee-status.{}".format(os.getpid())
self.__socket = None
def __enter__(self):
self.__socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.__socket.bind(self.__name)
self.__socket.listen(5)
return self.__socket
def __exit__(self, type, value, traceback):
self.__socket.close()
os.unlink(self.__name)
def process_event(event_line, config, update_lock):
modules = {}
try:
event = json.loads(event_line)
core.input.trigger(event)
if "name" in event:
modules[event["name"]] = True
except ValueError:
pass
delay = float(config.get("engine.input_delay", 0.0))
if delay > 0:
time.sleep(delay)
if update_lock.acquire(blocking=False) == True:
core.event.trigger("update", modules.keys(), force=True)
core.event.trigger("draw")
update_lock.release()
def handle_commands(config, update_lock):
with CommandSocket() as cmdsocket:
while True:
tmp, _ = cmdsocket.accept()
line = tmp.recv(4096).decode()
tmp.close()
logging.debug("socket event {}".format(line))
process_event(line, config, update_lock)
def handle_events(config, update_lock):
while True:
try:
line = sys.stdin.readline().strip(",").strip()
if line == "[": continue
logging.info("input event: {}".format(line))
process_event(line, config, update_lock)
except Exception as e:
logging.error(e)
def main():
global started
config = core.config.Config(sys.argv[1:])
level = logging.DEBUG if config.debug() else logging.ERROR
if config.logfile():
logging.basicConfig(
level=level,
format="[%(asctime)s] %(module)-16s %(levelname)-8s %(message)s",
filename=os.path.abspath(os.path.expanduser(config.logfile())),
)
else:
logging.basicConfig(
level=level,
format="[%(asctime)s] %(module)-16s %(levelname)-8s %(message)s",
stream=sys.stderr,
)
theme = core.theme.Theme(config.theme(), config.iconset())
output = core.output.i3(theme, config)
modules = []
core.input.register(None, core.input.WHEEL_UP, "i3-msg workspace prev_on_output")
core.input.register(None, core.input.WHEEL_DOWN, "i3-msg workspace next_on_output")
core.event.trigger("start")
started = True
update_lock = threading.Lock()
event_thread = threading.Thread(target=handle_events, args=(config, update_lock, ))
event_thread.daemon = True
event_thread.start()
cmd_thread = threading.Thread(target=handle_commands, args=(config, update_lock, ))
cmd_thread.daemon = True
cmd_thread.start()
def sig_USR1_handler(signum,stack):
engine.write_output()
config = bumblebee.config.Config(sys.argv[1:])
if update_lock.acquire(blocking=False) == True:
core.event.trigger("update", force=True)
core.event.trigger("draw")
update_lock.release()
if config.debug():
if config.logfile() in ["stdout", "stderr"]:
logging.basicConfig(
level=logging.DEBUG,
format="[%(asctime)s] %(levelname)-8s %(message)s",
stream=sys.stdout if config.logfile() == "stdout" else sys.stderr
)
else:
logging.basicConfig(
level=logging.DEBUG,
format="[%(asctime)s] %(levelname)-8s %(message)s",
filename=config.logfile()
)
modules.append(core.module.load("debug", config, theme))
theme = bumblebee.theme.Theme(config.theme(), config.iconset())
output = bumblebee.output.I3BarOutput(theme=theme, config=config)
inp = bumblebee.input.I3BarInput()
engine = None
for module in config.modules():
modules.append(core.module.load(module, config, theme))
modules[-1].register_callbacks()
if config.reverse():
modules.reverse()
output.modules(modules)
if util.format.asbool(config.get("engine.collapsible", True)) == True:
core.input.register(None, core.input.MIDDLE_MOUSE, output.toggle_minimize)
signal.signal(10, sig_USR1_handler)
while True:
if update_lock.acquire(blocking=False) == True:
core.event.trigger("update")
core.event.trigger("draw")
update_lock.release()
output.wait(config.interval())
core.event.trigger("stop")
try:
engine = bumblebee.engine.Engine(
config=config,
output=output,
inp=inp,
theme=theme,
)
signal.signal(10,sig_USR1_handler)
engine.run()
except KeyboardInterrupt as error:
inp.stop()
sys.exit(0)
except BaseException as e:
if not engine: raise
module = engine.current_module()
logging.exception(e)
if output.started():
output.flush()
output.end()
else:
output.start()
import time
while True:
output.begin()
error = bumblebee.modules.error.Module(engine, {
"config": config, "name": "error"
})
error.set("exception occurred: {} in {}".format(e, module))
widget = error.widgets()[0]
widget.link_module(error)
output.draw(widget, error)
output.flush()
output.end()
time.sleep(1)
if __name__ == "__main__":
main()
try:
if sys.version_info.major < 3:
raise Exception("at least Python 3.4 required (Python 2.x not supported)")
main()
except Exception as e:
# really basic errors -> make sure these are shown in the status bar by minimal config
logging.exception(e)
if not started:
print("{\"version\":1}")
print("[")
while True:
sys.stdout.write(
json.dumps(
[
{
"full_text": " {} ".format(e),
"background": "#ff0000",
"color": "#ffffff",
"name": "error",
"instance": "the-only-one",
}
]
)
)
sys.stdout.write(",\n")
sys.stdout.flush()
time.sleep(5)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,4 +0,0 @@
from ._version import get_versions
__version__ = get_versions()['version']
del get_versions

View file

@ -1,140 +0,0 @@
"""Configuration handling
This module provides configuration information (loaded modules,
module parameters, etc.) to all other components
"""
import os
import sys
import logging
import argparse
import textwrap
import importlib
import bumblebee.store
MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <module>:<alias> to provide an alias in case you want to load the same module multiple times, but specify different parameters."
THEME_HELP = "Specify the theme to use for drawing modules"
PARAMETER_HELP = "Provide configuration parameters in the form of <module>.<key>=<value>"
LIST_HELP = "Display a list of either available themes or available modules along with their parameters."
DEBUG_HELP = "Enable debug log, This will create '~/bumblebee-status-debug.log' by default, can be changed with the '-f' option"
ICONMARKUP_HELP = "A Python format string that is valid Pango markup used for low level customization of icons on top of themes. There is no validation performed, this is delegated to the user. Used together with --markup=pango. Example: \"<span foreground='#ffffff' background='#000000'>{}</span>\". WARNING: highly experimental feature"
class print_usage(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs)
self._indent = " "*2
def __call__(self, parser, namespace, value, option_string=None):
if value == "modules":
self._args = namespace
self.print_modules()
elif value == "themes":
self.print_themes()
sys.exit(0)
def print_themes(self):
print(", ".join(bumblebee.theme.themes()))
def print_modules(self):
if self._args.list_format == "markdown":
print("# Table of modules")
print("|Name |Description |")
print("|-----|------------|")
for m in bumblebee.engine.all_modules():
try:
mod = importlib.import_module("bumblebee.modules.{}".format(m["name"]))
if self._args.list_format == "markdown":
doc = mod.__doc__.replace("<", "\<")
doc = doc.replace(">", "\>")
doc = doc.replace("\n", "<br>")
print("|{} |{} |".format(m["name"], doc))
else:
print(textwrap.fill("{}:".format(m["name"]), 80,
initial_indent=self._indent*2, subsequent_indent=self._indent*2))
for line in mod.__doc__.split("\n"):
print(textwrap.fill(line, 80,
initial_indent=self._indent*3, subsequent_indent=self._indent*6))
except:
pass
def create_parser():
"""Create the argument parser"""
parser = argparse.ArgumentParser(description="display system data in the i3bar")
parser.add_argument("-m", "--modules", nargs="+", action='append', default=[],
help=MODULE_HELP)
parser.add_argument("-t", "--theme", default="default", help=THEME_HELP)
parser.add_argument("--markup", default="none", help="Specify the markup type of the output (e.g. 'pango')")
parser.add_argument("--iconmarkup", default="none", help=ICONMARKUP_HELP)
parser.add_argument("-p", "--parameters", nargs="+", action='append', default=[],
help=PARAMETER_HELP)
parser.add_argument("-l", "--list", choices=["modules", "themes"], action=print_usage,
help=LIST_HELP)
parser.add_argument("--list-format", choices=["plain", "markdown"], help="output format of -l, *must* be specified before -l")
parser.add_argument("-d", "--debug", action="store_true",
help=DEBUG_HELP)
parser.add_argument("-r", "--right-to-left", action="store_true",
help="Draw widgets from right to left, rather than left to right (which is the default)")
parser.add_argument("-f", "--logfile", default="~/bumblebee-status-debug.log",
help="Location of the debug log file")
parser.add_argument("-i", "--iconset", default="auto",
help="Specify the name of an iconset to use (overrides theme default)")
parser.add_argument("-a", "--autohide", nargs="+", default=[],
help="Specify a list of modules to hide when not in warning/error state")
return parser
class Config(bumblebee.store.Store):
"""Top-level configuration class
Parses commandline arguments and provides non-module
specific configuration information.
"""
def __init__(self, args=None):
super(Config, self).__init__()
parser = create_parser()
self._args = parser.parse_args(args if args else [])
if not self._args.debug:
logging.getLogger().disabled = True
parameters = [item for sub in self._args.parameters for item in sub]
for param in parameters:
key, value = param.split("=", 1)
self.set(key, value)
def modules(self):
modules = [item for sub in self._args.modules for item in sub]
"""Return a list of all activated modules"""
return [{
"module": x.split(":")[0],
"name": x if not ":" in x else x.split(":")[1],
} for x in modules]
def theme(self):
"""Return the name of the selected theme"""
return self._args.theme
def iconset(self):
"""Return the name of a user-specified icon-set"""
return self._args.iconset
def debug(self):
return self._args.debug
def reverse(self):
return self._args.right_to_left
def logfile(self):
return os.path.expanduser(self._args.logfile)
def autohide(self):
return self._args.autohide
def markup(self):
return self._args.markup
def iconmarkup(self):
return self._args.iconmarkup
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,313 +0,0 @@
"""Core application engine"""
import os
import json
import time
import pkgutil
import logging
import importlib
import bumblebee.error
import bumblebee.modules
log = logging.getLogger(__name__)
try:
from ConfigParser import RawConfigParser
except ImportError:
from configparser import RawConfigParser
def all_modules():
"""Return a list of available modules"""
result = []
path = os.path.dirname(bumblebee.modules.__file__)
for mod in [name for _, name, _ in pkgutil.iter_modules([path])]:
result.append({
"name": mod
})
return result
class Module(object):
"""Module instance base class
Objects of this type represent the modules that
the user configures. Concrete module implementations
(e.g. CPU utilization, disk usage, etc.) derive from
this base class.
"""
def __init__(self, engine, config={}, widgets=[]):
self.name = config.get("name", self.__module__.split(".")[-1])
self._config = config
self.id = self.name
self.error = None
self._next = int(time.time())
self._default_interval = 0
self._interval_factor = 1
self._engine = engine
self._configFile = None
for cfg in [os.path.expanduser("~/.bumblebee-status.conf"), os.path.expanduser("~/.config/bumblebee-status.conf")]:
if os.path.exists(cfg):
self._configFile = RawConfigParser()
self._configFile.read(cfg)
log.debug("reading configuration file {}".format(cfg))
break
if self._configFile is not None and self._configFile.has_section("module-parameters"):
log.debug(self._configFile.items("module-parameters"))
self._widgets = []
if widgets:
self._widgets = widgets if isinstance(widgets, list) else [widgets]
def theme(self):
return self._engine.theme()
def widgets(self, widgets=None):
"""Return the widgets to draw for this module"""
if widgets:
self._widgets = widgets if isinstance(widgets, list) else [widgets]
return self._widgets
def hidden(self):
return False
def widget(self, name):
for widget in self._widgets:
if widget.name == name:
return widget
def errorWidget(self):
msg = self.error
if len(msg) > 10:
msg = "{}...".format(msg[0:7])
return bumblebee.output.Widget(full_text="error: {}".format(msg))
def widget_by_id(self, uid):
for widget in self._widgets:
if widget.id == uid:
return widget
return None
def update(self, widgets):
"""By default, update() is a NOP"""
pass
def update_wrapper(self, widgets):
if self._next > int(time.time()):
return
try:
self.error = None
self.update(self._widgets)
except Exception as e:
log.error("error updating '{}': {}".format(self.name, str(e)))
self.error = str(e)
self._next += int(self.parameter("interval", self._default_interval))*self._interval_factor
def interval_factor(self, factor):
self._interval_factor = factor
def interval(self, intvl):
self._default_interval = intvl
self._next = int(time.time())
def update_all(self):
self.update_wrapper(self._widgets)
def has_parameter(self, name):
v = self.parameter(name)
return v is not None
def parameter(self, name, default=None):
"""Return the config parameter 'name' for this module"""
module_name = "{}.{}".format(self.__module__.split(".")[-1], name)
name = "{}.{}".format(self.name, name)
value = self._config["config"].get(module_name, default)
value = self._config["config"].get(name, value)
if value == default:
try:
value = self._configFile.get("module-parameters", name)
except:
pass
return value
def threshold_state(self, value, warn, crit):
if value > float(self.parameter("critical", crit)):
return "critical"
if value > float(self.parameter("warning", warn)):
return "warning"
return None
class Engine(object):
"""Engine for driving the application
This class connects input/output, instantiates all
required modules and drives the "event loop"
"""
def __init__(self, config, output=None, inp=None, theme=None):
self._output = output
self._config = config
self._running = True
self._modules = []
self.input = inp
self._aliases = self._aliases()
self.load_modules(config.modules())
self._current_module = None
self._theme = theme
if bumblebee.util.asbool(config.get("engine.workspacewheel", "true")):
if bumblebee.util.asbool(config.get("engine.workspacewrap", "true")):
self.input.register_callback(None, bumblebee.input.WHEEL_UP,
"i3-msg workspace prev_on_output")
self.input.register_callback(None, bumblebee.input.WHEEL_DOWN,
"i3-msg workspace next_on_output")
else:
self.input.register_callback(None, bumblebee.input.WHEEL_UP,
cmd=self._prev_workspace)
self.input.register_callback(None, bumblebee.input.WHEEL_DOWN,
cmd=self._next_workspace)
if bumblebee.util.asbool(config.get("engine.collapsible", "true")):
self.input.register_callback(None, bumblebee.input.MIDDLE_MOUSE,
cmd=self._toggle_minimize)
self.input.start()
def theme(self):
return self._theme
def _toggle_minimize(self, event):
for module in self._modules:
widget = module.widget_by_id(event["instance"])
if widget:
log.debug("module {} found - toggle minimize".format(module.id))
widget.toggle_minimize()
def _prev_workspace(self, event):
self._change_workspace(-1)
def _next_workspace(self, event):
self._change_workspace(1)
def _change_workspace(self, amount):
try:
active_output = None
active_index = -1
output_workspaces = {}
data = json.loads(bumblebee.util.execute("i3-msg -t get_workspaces"))
for workspace in data:
output_workspaces.setdefault(workspace["output"], []).append(workspace)
if workspace["focused"]:
active_output = workspace["output"]
active_index = len(output_workspaces[workspace["output"]]) - 1
if (active_index + amount) < 0:
return
if (active_index + amount) >= len(output_workspaces[active_output]):
return
while amount != 0:
if amount > 0:
bumblebee.util.execute("i3-msg workspace next_on_output")
amount = amount - 1
if amount < 0:
bumblebee.util.execute("i3-msg workspace prev_on_output")
amount = amount + 1
except Exception as e:
log.error("failed to change workspace: {}".format(e))
def modules(self):
return self._modules
def load_modules(self, modules):
"""Load specified modules and return them as list"""
for module in modules:
mod = self._load_module(module["module"], module["name"])
self._modules.append(mod)
self._register_module_callbacks(mod)
return self._modules
def _register_module_callbacks(self, module):
buttons = [
{"name": "left-click", "id": bumblebee.input.LEFT_MOUSE},
{"name": "middle-click", "id": bumblebee.input.MIDDLE_MOUSE},
{"name": "right-click", "id": bumblebee.input.RIGHT_MOUSE},
{"name": "wheel-up", "id": bumblebee.input.WHEEL_UP},
{"name": "wheel-down", "id": bumblebee.input.WHEEL_DOWN},
]
for button in buttons:
if module.parameter(button["name"], None):
self.input.register_callback(obj=module,
button=button["id"], cmd=module.parameter(button["name"]))
def _aliases(self):
return {
'date': 'datetime',
'time': 'datetime',
'datetz': 'datetimetz',
'timetz': 'datetimetz',
'pasink': 'pulseaudio',
'pasource': 'pulseaudio',
'test-alias': 'test',
}
def _load_module(self, module_name, config_name=None):
"""Load specified module and return it as object"""
if module_name in self._aliases:
config_name is config_name if config_name else module_name
module_name = self._aliases[module_name]
if config_name is None:
config_name = module_name
err = None
try:
module = importlib.import_module("bumblebee.modules.{}".format(module_name))
except ImportError as error:
err = error
log.fatal("failed to import {}: {}".format(module_name, str(error)))
if err:
raise bumblebee.error.ModuleLoadError("unable to load module {}: {}".format(module_name, str(err)))
return getattr(module, "Module")(self, {
"name": config_name,
"config": self._config
})
def running(self):
"""Check whether the event loop is running"""
return self._running
def stop(self):
"""Stop the event loop"""
self._running = False
def current_module(self):
return self._current_module.__module__
def run(self):
"""Start the event loop"""
self._output.start()
while self.running():
self.write_output()
if self.running():
self.input.wait(float(self._config.get("interval", 1)))
self._output.stop()
self.input.stop()
def write_output(self):
self._output.begin()
for module in self._modules:
self._current_module = module
module.update_wrapper(module.widgets())
if module.error is None:
widget_ids = []
if module.parameter('id'):
widget_ids = module.parameter('id').split(',')
idx = 0
for widget in module.widgets():
widget.link_module(module)
if idx < len(widget_ids):
widget.id = widget_ids[idx]
idx = idx + 1
self._output.draw(widget=widget, module=module, engine=self)
else:
self._output.draw(widget=module.errorWidget(), module=module, engine=self)
self._output.flush()
self._output.end()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,15 +0,0 @@
"""Collection of all exceptions raised by this tool"""
class BaseError(Exception):
"""Base class for all exceptions generated by this tool"""
pass
class ModuleLoadError(BaseError):
"""Raised whenever loading a module fails"""
pass
class ThemeLoadError(BaseError):
"""Raised whenever loading a theme fails"""
pass
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,175 +0,0 @@
"""Input classes"""
import os
import sys
import json
import uuid
import time
import socket
import select
import logging
import threading
import bumblebee.util
log = logging.getLogger(__name__)
LEFT_MOUSE = 1
MIDDLE_MOUSE = 2
RIGHT_MOUSE = 3
WHEEL_UP = 4
WHEEL_DOWN = 5
def is_terminated():
for thread in threading.enumerate():
if thread.name == "MainThread" and not thread.is_alive():
return True
return False
class CommandSocket(object):
def __init__(self):
self._name = "/tmp/.bumblebee-status.{}".format(os.getpid())
self._socket = None
def __enter__(self):
self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self._socket.bind(self._name)
self._socket.listen(5)
return self._socket
def __exit__(self, type, value, traceback):
self._socket.close()
os.unlink(self._name)
def read_input(inp):
"""Read i3bar input and execute callbacks"""
with CommandSocket() as cmdsocket:
poll = select.poll()
poll.register(sys.stdin, select.POLLIN)
poll.register(cmdsocket, select.POLLIN)
log.debug("starting click event processing")
while inp.running:
if is_terminated():
return
try:
events = poll.poll(1000)
except Exception:
continue
for fileno, event in events:
if fileno == cmdsocket.fileno():
tmp, _ = cmdsocket.accept()
line = tmp.recv(4096).decode()
tmp.close()
else:
line = "["
while line.startswith("["):
line = sys.stdin.readline().strip(",").strip()
log.debug("new event: {}".format(line))
inp.has_event = True
try:
event = json.loads(line)
if "instance" in event:
inp.callback(event)
inp.redraw()
else:
log.debug("field 'instance' missing in input, not processing the event")
except ValueError as e:
log.debug("failed to parse event: {}".format(e))
log.debug("exiting click event processing")
poll.unregister(sys.stdin.fileno())
inp.has_event = True
inp.clean_exit = True
class I3BarInput(object):
"""Process incoming events from the i3bar"""
def __init__(self):
self.running = True
self._callbacks = {}
self.clean_exit = False
self.global_id = str(uuid.uuid4())
self.need_event = False
self.has_event = False
self._condition = threading.Condition()
def start(self):
"""Start asynchronous input processing"""
self.has_event = False
self.running = True
self._condition.acquire()
self._thread = threading.Thread(target=read_input, args=(self,))
self._thread.start()
def redraw(self):
self._condition.acquire()
self._condition.notify()
self._condition.release()
def alive(self):
"""Check whether the input processing is still active"""
return self._thread.is_alive()
def wait(self, timeout):
self._condition.wait(timeout)
def _wait(self):
while not self.has_event:
time.sleep(0.1)
self.has_event = False
def stop(self):
"""Stop asynchronous input processing"""
self._condition.release()
if self.need_event:
self._wait()
self.running = False
self._thread.join()
return self.clean_exit
def _uuidstr(self, name, button):
return "{}::{}".format(name, button)
def _uid(self, obj, button):
uid = self.global_id
if obj:
uid = obj.id
return self._uuidstr(uid, button)
def deregister_callbacks(self, obj):
to_delete = []
uid = obj.id if obj else self.global_id
for key in self._callbacks:
if uid in key:
to_delete.append(key)
for key in to_delete:
del self._callbacks[key]
def register_callback(self, obj, button, cmd):
"""Register a callback function or system call"""
uid = self._uid(obj, button)
if uid not in self._callbacks:
self._callbacks[uid] = {}
self._callbacks[uid] = cmd
def callback(self, event):
"""Execute callback action for an incoming event"""
button = event["button"]
cmd = self._callbacks.get(self._uuidstr(self.global_id, button), None)
cmd = self._callbacks.get(self._uuidstr(event["name"], button), cmd)
cmd = self._callbacks.get(self._uuidstr(event["instance"], button), cmd)
if cmd is None:
return
try:
if callable(cmd):
cmd(event)
else:
bumblebee.util.execute(cmd, False)
except Exception:
# fall back to global default
if not "__fallback" in event:
return self.callback({"name": None, "instance": None, "__fallback": True, "button": event["button"]})
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,42 +0,0 @@
"""get volume level
"""
import re
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.volume)
)
self._level = "0"
self._muted = True
device = self.parameter("device", "Master,0")
self._cmdString = "amixer get {}".format(device)
def volume(self, widget):
m = re.search(r'([\d]+)\%', self._level)
self._muted = True
if m:
if m.group(1) != "0" and "[on]" in self._level:
self._muted = False
return "{}%".format(m.group(1))
else:
return "0%"
def update(self, widgets):
level = ""
try:
level = bumblebee.util.execute(self._cmdString)
except Exception as e:
level = ""
self._level = level
def state(self, widget):
if self._muted:
return ["warning", "muted"]
return ["unmuted"]

View file

@ -1,81 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays APT package update information (<to upgrade>/<to remove >)
Requires the following debian packages:
* python-parse
* aptitude
"""
import threading
from parse import *
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
APT_CHECK_PATH = ("aptitude full-upgrade --simulate --assume-yes")
PATTERN = "{} packages upgraded, {} newly installed, {} to remove and {} not upgraded."
def parse_result(to_parse):
# We want to line with the iforamtion about package upgrade
line_to_parse = to_parse.split("\n")[-4]
result = parse(PATTERN, line_to_parse)
return int(result[0]), int(result[2])
def get_apt_check_info(widget):
try:
res = bumblebee.util.execute(APT_CHECK_PATH)
widget.set("error", None)
except (RuntimeError, FileNotFoundError) as e:
widget.set("error", "unable to query APT: {}".format(e))
return
to_upgrade = 0
to_remove = 0
try:
to_upgrade, to_remove = parse_result(res)
except e:
widget.set("error", "parse error: {}".format(e))
return
widget.set("to_upgrade", to_upgrade)
widget.set("to_remove", to_remove)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.updates)
super(Module, self).__init__(engine, config, widget)
self.interval_factor(60)
self.interval(30)
def updates(self, widget):
result = []
if widget.get("error"):
return widget.get("error")
for t in ["to_upgrade", "to_remove"]:
result.append(str(widget.get(t, 0)))
return "/".join(result)
def update(self, widgets):
thread = threading.Thread(target=get_apt_check_info, args=(widgets[0],))
thread.start()
def state(self, widget):
cnt = 0
ret = "good"
for t in ["to_upgrade", "to_remove"]:
cnt += widget.get(t, 0)
if cnt > 50:
ret = "critical"
elif cnt > 0:
ret = "warning"
if widget.get("error"):
ret = "critical"
return ret
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,51 +0,0 @@
"""Check updates to Arch Linux.
Requires the following executable:
* checkupdates (from pacman-contrib)
"""
import subprocess
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.utilization)
super(Module, self).__init__(engine, config, widget)
self.packages = self.check_updates()
def check_updates(self):
p = subprocess.Popen(
"checkupdates", stdout=subprocess.PIPE, shell=True)
p_status = p.wait()
if p_status == 0:
(output, err) = p.communicate()
output = output.decode('utf-8')
packages = output.split('\n')
packages.pop()
return len(packages)
return 0
@property
def _format(self):
return self.parameter("format", "Update Arch: {}")
def utilization(self, widget):
return self._format.format(self.packages)
def hidden(self):
return self.check_updates() == 0
def update(self, widgets):
self.packages = self.check_updates()
def state(self, widget):
return self.threshold_state(self.packages, 1, 100)

View file

@ -1,140 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays battery status, remaining percentage and charging information.
Parameters:
* battery.device : Comma-separated list of battery devices to read information from (defaults to auto for auto-detection)
* battery.warning : Warning threshold in % of remaining charge (defaults to 20)
* battery.critical : Critical threshold in % of remaining charge (defaults to 10)
* battery.showdevice : If set to "true", add the device name to the widget (defaults to False)
* battery.decorate : If set to "false", hides additional icons (charging, etc.) (defaults to True)
* battery.showpowerconsumption: If set to "true", show current power consumption (defaults to False)
"""
import os
import glob
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
try:
import power
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = []
super(Module, self).__init__(engine, config, widgets)
self._batteries = self.parameter("device", "auto").split(",")
if self._batteries[0] == "auto":
self._batteries = glob.glob("/sys/class/power_supply/BAT*")
else:
self._batteries = ["/sys/class/power_supply/{}".format(b) for b in self._batteries]
if len(self._batteries) == 0:
self._batteries = ["/sys/class/power_supply/BAT0"]
self.update(widgets)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="gnome-power-statistics")
def update(self, widgets):
new_widgets = []
for path in self._batteries:
widget = self.widget(path)
if not widget:
widget = bumblebee.output.Widget(full_text=self.capacity, name=path)
new_widgets.append(widget)
self.capacity(widget)
while len(widgets) > 0: del widgets[0]
for widget in new_widgets:
if bumblebee.util.asbool(self.parameter("decorate", True)) == False:
widget.set("theme.exclude", "suffix")
widgets.append(widget)
self._widgets = widgets
def remaining(self):
estimate = 0.0
try:
estimate = power.PowerManagement().get_time_remaining_estimate()
# do not show remaining if on AC
if estimate == power.common.TIME_REMAINING_UNLIMITED:
return None
if estimate == power.common.TIME_REMAINING_UNKNOWN:
return ""
except Exception:
return ""
return bumblebee.util.durationfmt(estimate*60, shorten=True, suffix=True) # estimate is in minutes
def capacity(self, widget):
widget.set("capacity", -1)
widget.set("ac", False)
if not os.path.exists(widget.name):
widget.set("capacity", 100)
widget.set("ac", True)
return "ac"
capacity = 100
try:
with open("{}/capacity".format(widget.name)) as f:
capacity = int(f.read())
except IOError:
return "n/a"
capacity = capacity if capacity < 100 else 100
widget.set("capacity", capacity)
# Read power conumption
if bumblebee.util.asbool(self.parameter("showpowerconsumption", False)):
r=open(widget.name + '/power_now', "r")
output = "{}% ({})".format(capacity,str(int(r.read())/1000000) + "W")
else:
output = "{}%".format(capacity)
widget.set("theme.minwidth", "100%")
if bumblebee.util.asbool(self.parameter("showremaining", True))\
and self.getCharge(widget) == "Discharging":
output = "{} {}".format(output, self.remaining())
if bumblebee.util.asbool(self.parameter("showdevice", False)):
output = "{} ({})".format(output, os.path.basename(widget.name))
return output
def state(self, widget):
state = []
capacity = widget.get("capacity")
if capacity < 0:
return ["critical", "unknown"]
if capacity < int(self.parameter("critical", 10)):
state.append("critical")
elif capacity < int(self.parameter("warning", 20)):
state.append("warning")
if widget.get("ac"):
state.append("AC")
else:
charge = self.getCharge(widget)
if charge == "Discharging":
state.append("discharging-{}".format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity))))
elif charge == "Unknown":
state.append("unknown-{}".format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity))))
else:
if capacity > 95:
state.append("charged")
else:
state.append("charging")
return state
def getCharge(self, widget):
charge = ""
try:
with open("{}/status".format(widget.name)) as f:
charge = f.read().strip()
except IOError:
pass
return charge
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,130 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays battery status, remaining percentage and charging information.
Parameters:
* battery.device : Comma-separated list of battery devices to read information from (defaults to auto for auto-detection)
* battery.warning : Warning threshold in % of remaining charge (defaults to 20)
* battery.critical : Critical threshold in % of remaining charge (defaults to 10)
* batter.showremaining : If set to true (default) shows the remaining time until the batteries are completely discharged
"""
import os
import glob
import logging
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
try:
import power
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
self._batteries = []
try:
for battery in os.listdir('/sys/class/power_supply/'):
if not any(i in battery for i in ['AC', 'hidpp']):
self._batteries.append("/sys/class/power_supply/" + battery)
except Exception as e:
logging.exception("unable to detect batteries: {}".format(str(e)))
super(Module, self).__init__(engine, config, bumblebee.output.Widget(full_text=self.capacity))
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="gnome-power-statistics")
def remaining(self):
estimate = 0.0
power_now = 0.0
try:
estimate = power.PowerManagement().get_time_remaining_estimate()
# do not show remaining if on AC
if estimate == power.common.TIME_REMAINING_UNLIMITED:
return None
elif estimate == power.common.TIME_REMAINING_UNKNOWN:
return ""
except Exception:
return ""
return bumblebee.util.durationfmt(estimate*60, shorten=True, suffix=True) # estimate is in minutes
def capacity(self, widget):
widget.set("capacity", -1)
widget.set("ac", False)
capacity = 100
self.energy_now = 0
self.energy_full = 0
errors = 0
for path in self._batteries:
try:
with open("{}/energy_full".format(path)) as f:
self.energy_full += int(f.read())
with open("{}/energy_now".format(path)) as o:
self.energy_now += int(o.read())
except IOError:
return "n/a"
except Exception:
errors += 1
if errors == len(self._batteries):
return "n/a"
capacity = int( float(self.energy_now) / float(self.energy_full) * 100.0)
capacity = capacity if capacity < 100 else 100
widget.set("capacity", capacity)
output = "{}%".format(capacity)
widget.set("theme.minwidth", "100%")
if bumblebee.util.asbool(self.parameter("showremaining", True))\
and self.getCharge(widget) == "Discharging":
output = "{} {}".format(output, self.remaining())
return output
def state(self, widget):
state = []
capacity = widget.get("capacity", -1)
if capacity < 0:
return ["critical", "unknown"]
if capacity < int(self.parameter("critical", 10)):
state.append("critical")
elif capacity < int(self.parameter("warning", 20)):
state.append("warning")
if widget.get("ac"):
state.append("AC")
else:
charge = self.getCharge(widget)
if charge == "Discharging":
state.append("discharging-{}".format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity))))
elif charge == "Unknown":
state.append("unknown-{}".format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity))))
else:
if capacity > 95:
state.append("charged")
else:
state.append("charging")
return state
def getCharge(self, widget):
charge = ""
charge_list = []
for x in range(len(self._batteries)):
try:
with open("{}/status".format(self._batteries[x])) as f:
charge_list.append(f.read().strip())
except IOError:
pass
for x in range(len(charge_list)):
if charge_list[x] == "Discharging":
charge = charge_list[x]
break
return charge

View file

@ -1,104 +0,0 @@
"""Displays bluetooth status. Left mouse click launches manager app,
right click toggles bluetooth. Needs dbus-send to toggle bluetooth state and
python-dbus to count the number of connections
Parameters:
* bluetooth.manager : application to launch on click (blueman-manager)
"""
import os
import re
import subprocess
import dbus
import dbus.mainloop.glib
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
import bumblebee.popup
import logging
class Module(bumblebee.engine.Module):
"""Bluetooth module."""
def __init__(self, engine, config):
"""Initialize."""
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(
full_text=self.status))
self.manager = self.parameter("manager", "blueman-manager")
self._status = "Off"
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
self._bus = dbus.SystemBus()
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.manager)
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE,
cmd=self._toggle)
def status(self, widget):
"""Get status."""
return self._status
def update(self, widgets):
"""Update current state."""
state = len(subprocess.run(['bluetoothctl', 'list'], stdout=subprocess.PIPE).stdout)
if state > 0:
connected_devices = self.get_connected_devices()
self._status = "On - {}".format(connected_devices)
else:
self._status = "Off"
adapters_cmd = 'rfkill list | grep Bluetooth'
if not len(subprocess.run(adapters_cmd, shell=True, stdout=subprocess.PIPE).stdout):
self._status = "No Adapter Found"
return
def manager(self, widget):
"""Launch manager."""
bumblebee.util.execute(self.manager)
def _toggle(self, widget=None):
"""Toggle bluetooth state."""
if "On" in self._status:
state = "false"
else:
state = "true"
cmd = "dbus-send --system --print-reply --dest=org.blueman.Mechanism /org/blueman/mechanism org.blueman.Mechanism.SetRfkillState boolean:%s" % state
logging.debug('bt: toggling bluetooth')
bumblebee.util.execute(cmd)
def state(self, widget):
"""Get current state."""
state = []
if self._status == "No Adapter Found":
state.append("critical")
elif self._status == "On - 0":
state.append("warning")
elif "On" in self._status and not(self._status == "On - 0"):
state.append("ON")
else:
state.append("critical")
return state
def get_connected_devices(self):
devices = 0
objects = dbus.Interface(
self._bus.get_object("org.bluez", "/"),
"org.freedesktop.DBus.ObjectManager"
).GetManagedObjects()
for path, interfaces in objects.items():
if "org.bluez.Device1" in interfaces:
if dbus.Interface(
self._bus.get_object("org.bluez", path),
"org.freedesktop.DBus.Properties"
).Get(
"org.bluez.Device1", "Connected"
):
devices += 1
return devices

View file

@ -1,63 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the brightness of a display
Parameters:
* brightness.step: The amount of increase/decrease on scroll in % (defaults to 2)
* brightness.device_path: The device path (defaults to /sys/class/backlight/intel_backlight), can contain wildcards (in this case, the first matching path will be used)
"""
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import glob
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.brightness))
self._brightness = 0
self._device_path = self.find_device(self.parameter("device_path", "/sys/class/backlight/intel_backlight"))
step = self.parameter("step", 2)
if bumblebee.util.which("light"):
self.register_cmd(engine, "light -A {}%".format(step),
"light -U {}%".format(step))
elif bumblebee.util.which("brightnessctl"):
self.register_cmd(engine, "brightnessctl s {}%+".format(step),
"brightnessctl s {}%-".format(step))
else:
self.register_cmd(engine, "xbacklight +{}%".format(step),
"xbacklight -{}%".format(step))
def find_device(self, device_path):
res = glob.glob(device_path)
if len(res) == 0:
return device_path
return res[0]
def register_cmd(self, engine, upCmd, downCmd):
engine.input.register_callback(self, button=bumblebee.input.WHEEL_UP, cmd=upCmd)
engine.input.register_callback(self, button=bumblebee.input.WHEEL_DOWN, cmd=downCmd)
def brightness(self, widget):
if isinstance(self._brightness, float):
return "{:3.0f}%".format(self._brightness).strip()
else:
return "n/a"
def update(self, widgets):
try:
with open("{}/brightness".format(self._device_path)) as f:
backlight = int(f.readline())
with open("{}/max_brightness".format(self._device_path)) as f:
max_brightness = int(f.readline())
self._brightness = float(backlight * 100 / max_brightness)
except:
return "Error"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,102 +0,0 @@
#pylint: disable=C0111,R0903,W0212
"""Enable/disable automatic screen locking.
Requires the following executables:
* xdg-screensaver
* xdotool
* xprop (as dependency for xdotool)
* notify-send
"""
import logging
import os
import psutil
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text="")
)
self._active = False
self._xid = None
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self._toggle
)
def _check_requirements(self):
requirements = ['xdotool', 'xprop', 'xdg-screensaver']
missing = []
for tool in requirements:
if not bumblebee.util.which(tool):
missing.append(tool)
return missing
def _get_i3bar_xid(self):
xid = bumblebee.util.execute("xdotool search --class \"i3bar\"").partition('\n')[0].strip()
if xid.isdigit():
return xid
logging.warning("Module caffeine: xdotool couldn't get X window ID of \"i3bar\".")
return None
def _notify(self):
if not bumblebee.util.which('notify-send'):
return
if self._active:
bumblebee.util.execute("notify-send \"Consuming caffeine\"")
else:
bumblebee.util.execute("notify-send \"Out of coffee\"")
def _suspend_screensaver(self):
self._xid = self._get_i3bar_xid()
if self._xid is None:
return False
pid = os.fork()
if pid == 0:
os.setsid()
bumblebee.util.execute("xdg-screensaver suspend {}".format(self._xid))
os._exit(0)
else:
os.waitpid(pid, 0)
return True
def _resume_screensaver(self):
success = True
xprop_path = bumblebee.util.which('xprop')
pids = [ p.pid for p in psutil.process_iter() if p.cmdline() == [xprop_path, '-id', str(self._xid), '-spy'] ]
for pid in pids:
try:
os.kill(pid, 9)
except OSError:
success = False
return success
def state(self, _):
if self._active:
return "activated"
return "deactivated"
def _toggle(self, _):
missing = self._check_requirements()
if missing:
logging.warning('Could not run caffeine - missing %s!', ", ".join(missing))
return
self._active = not self._active
if self._active:
success = self._suspend_screensaver()
else:
success = self._resume_screensaver()
if success:
self._notify()
else:
self._active = not self._active
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,42 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays CPU utilization across all CPUs.
Parameters:
* cpu.warning : Warning threshold in % of CPU usage (defaults to 70%)
* cpu.critical: Critical threshold in % of CPU usage (defaults to 80%)
* cpu.format : Format string (defaults to "{:.01f}%")
"""
try:
import psutil
except ImportError:
pass
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.utilization)
super(Module, self).__init__(engine, config, widget)
widget.set("theme.minwidth", self._format.format(10.0-10e-20))
self._utilization = psutil.cpu_percent(percpu=False)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="gnome-system-monitor")
@property
def _format(self):
return self.parameter("format", "{:.01f}%")
def utilization(self, _):
return self._format.format(self._utilization)
def update(self, widgets):
self._utilization = psutil.cpu_percent(percpu=False)
def state(self, _):
return self.threshold_state(self._utilization, 70, 80)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,156 +0,0 @@
"""Multiwidget CPU module
Can display any combination of:
* max CPU frequency
* total CPU load in percents (integer value)
* per-core CPU load as graph - either mono or colored
* CPU temperature (in Celsius degrees)
* CPU fan speed
Requirements:
* the psutil Python module for the first three items from the list above
* sensors executable for the rest
Parameters:
* cpu2.layout: Space-separated list of widgets to add.
Possible widgets are:
* cpu2.maxfreq
* cpu2.cpuload
* cpu2.coresload
* cpu2.temp
* cpu2.fanspeed
* cpu2.colored: 1 for colored per core load graph, 0 for mono (default)
if this is set to 1, use --markup=pango
* cpu2.temp_pattern: pattern to look for in the output of 'sensors -u';
required if cpu2.temp widged is used
* cpu2.fan_pattern: pattern to look for in the output of 'sensors -u';
required if cpu2.fanspeed widged is used
Note: if you are getting "n/a" for CPU temperature / fan speed, then you're
lacking the aforementioned pattern settings or they have wrong values.
"""
try:
import psutil
except ImportError:
pass
import bumblebee.engine
import bumblebee.util
import bumblebee.output
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config, [])
self._layout = self.parameter("layout", "cpu2.maxfreq cpu2.cpuload cpu2.coresload cpu2.temp cpu2.fanspeed")
self._widget_names = self._layout.split()
widget_list = []
for widget_name in self._widget_names:
if widget_name == "cpu2.maxfreq":
widget = bumblebee.output.Widget(
name=widget_name, full_text=self.maxfreq)
widget.set("type", "freq")
elif widget_name == "cpu2.cpuload":
widget = bumblebee.output.Widget(
name=widget_name, full_text=self.cpuload)
widget.set("type", "load")
elif widget_name == "cpu2.coresload":
widget = bumblebee.output.Widget(
name=widget_name, full_text=self.coresload)
widget.set("type", "loads")
elif widget_name == "cpu2.temp":
widget = bumblebee.output.Widget(
name=widget_name, full_text=self.temp)
widget.set("type", "temp")
elif widget_name == "cpu2.fanspeed":
widget = bumblebee.output.Widget(
name=widget_name, full_text=self.fanspeed)
widget.set("type", "fan")
widget_list.append(widget)
self.widgets(widget_list)
self._colored = bumblebee.util.asbool(self.parameter("colored", 0))
self._temp_pattern = self.parameter("temp_pattern")
if self._temp_pattern is None:
self._temp = "n/a"
self._fan_pattern = self.parameter("fan_pattern")
if self._fan_pattern is None:
self._fan = "n/a"
# maxfreq is loaded only once at startup
if "cpu2.maxfreq" in self._widget_names:
self._maxfreq = psutil.cpu_freq().max / 1000
self.update(widget_list)
def maxfreq(self, _):
return "{:.2f}GHz".format(self._maxfreq)
def cpuload(self, _):
return "{:>3}%".format(self._cpuload)
def add_color(self, bar):
"""add color as pango markup to a bar"""
if bar in ["", ""]:
color = self.theme().color("green", "green")
elif bar in ["", ""]:
color = self.theme().color("yellow", "yellow")
elif bar in ["", ""]:
color = self.theme().color("orange", "orange")
elif bar in ["", ""]:
color = self.theme().color("red", "red")
colored_bar = "<span foreground='{}'>{}</span>".format(color, bar)
return colored_bar
def coresload(self, _):
mono_bars = [bumblebee.output.hbar(x) for x in self._coresload]
if not self._colored:
return "".join(mono_bars)
colored_bars = [self.add_color(x) for x in mono_bars]
return "".join(colored_bars)
def temp(self, _):
if self._temp == "n/a" or self._temp == 0:
return "n/a"
return "{}°C".format(self._temp)
def fanspeed(self, _):
if self._fanspeed == "n/a":
return "n/a"
return "{}RPM".format(self._fanspeed)
def _parse_sensors_output(self):
output = bumblebee.util.execute("sensors -u")
lines = output.split("\n")
temp = "n/a"
fan = "n/a"
temp_line = None
fan_line = None
for line in lines:
if self._temp_pattern is not None and self._temp_pattern in line:
temp_line = line
if self._fan_pattern is not None and self._fan_pattern in line:
fan_line = line
if temp_line is not None and fan_line is not None:
break
if temp_line is not None:
temp = round(float(temp_line.split(":")[1].strip()))
if fan_line is not None:
fan = int(fan_line.split(":")[1].strip()[:-4])
return temp, fan
def update(self, _):
if "cpu2.maxfreq" in self._widget_names:
self._maxfreq = psutil.cpu_freq().max / 1000
if "cpu2.cpuload" in self._widget_names:
self._cpuload = round(psutil.cpu_percent(percpu=False))
if "cpu2.coresload" in self._widget_names:
self._coresload = psutil.cpu_percent(percpu=True)
if "cpu2.temp" in self._widget_names or "cpu2.fanspeed" in self._widget_names:
self._temp, self._fanspeed = self._parse_sensors_output()
def state(self, widget):
"""for having per-widget icons"""
return [widget.get("type", "")]

View file

@ -1,139 +0,0 @@
# -*- coding: UTF-8 -*-
# pylint: disable=C0111,R0903
"""Displays currency exchange rates. Currently, displays currency between GBP and USD/EUR only.
Requires the following python packages:
* requests
Parameters:
* currency.interval: Interval in minutes between updates, default is 1.
* currency.source: Source currency (ex. "GBP", "EUR"). Defaults to "auto", which infers the local one from IP address.
* currency.destination: Comma-separated list of destination currencies (defaults to "USD,EUR")
* currency.sourceformat: String format for source formatting; Defaults to "{}: {}" and has two variables,
the base symbol and the rate list
* currency.destinationdelimiter: Delimiter used for separating individual rates (defaults to "|")
Note: source and destination names right now must correspond to the names used by the API of https://markets.ft.com
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
try:
import requests
except ImportError:
pass
try:
from babel.numbers import format_currency
except ImportError:
format_currency = None
import json
import os
SYMBOL = {
"GBP": u"£", "EUR": u"", "USD": u"$", "JPY": u"¥", "KRW": u""
}
DEFAULT_DEST = "USD,EUR,auto"
DEFAULT_SRC = "GBP"
API_URL = "https://markets.ft.com/data/currencies/ajax/conversion?baseCurrency={}&comparison={}"
LOCATION_URL = "https://ipvigilante.com/"
def get_local_country():
r = requests.get(LOCATION_URL)
location = r.json()
return location['data']['country_name']
def load_country_to_currency():
fname = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'data', 'country-by-currency-code.json')
with open(fname, 'r') as f:
data = json.load(f)
country2curr = {}
for dt in data:
country2curr[dt['country']] = dt['currency_code']
return country2curr
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.price)
)
self._data = []
self.interval_factor(60)
self.interval(1)
self._nextcheck = 0
src = self.parameter("source", DEFAULT_SRC)
if src == "auto":
self._base = self.find_local_currency()
else:
self._base = src
self._symbols = []
for d in self.parameter("destination", DEFAULT_DEST).split(","):
if d == 'auto':
new = self.find_local_currency()
else:
new = d
if new != self._base:
self._symbols.append(new)
def price(self, widget):
if len(self._data) == 0:
return "?"
rates = []
for sym, rate in self._data:
rate_float = float(rate.replace(',',''))
if format_currency:
rates.append(format_currency(rate_float, sym))
else:
rate = self.fmt_rate(rate)
rates.append(u"{}{}".format(rate, SYMBOL[sym] if sym in SYMBOL else sym))
basefmt = u"{}".format(self.parameter("sourceformat", "{}={}"))
ratefmt = u"{}".format(self.parameter("destinationdelimiter", "="))
if format_currency:
base_val = format_currency(1, self._base)
else:
base_val = '1{}'.format(SYMBOL[self._base] if self._base in SYMBOL else self._base)
return basefmt.format(base_val, ratefmt.join(rates))
def update(self, widgets):
self._data = []
for symbol in self._symbols:
url = API_URL.format(self._base, symbol)
try:
response = requests.get(url).json()
self._data.append((symbol, response['data']['exchangeRate']))
except Exception:
pass
def find_local_currency(self):
'''Use geolocation lookup to find local currency'''
try:
country = get_local_country()
currency_map = load_country_to_currency()
return currency_map.get(country, DEFAULT_SRC)
except:
return DEFAULT_SRC
def fmt_rate(self, rate):
float_rate = float(rate.replace(',', ''))
if not 0.01 < float_rate < 100:
ret = rate
else:
ret = "%.3g" % float_rate
return ret
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,974 +0,0 @@
[
{
"country": "Afghanistan",
"currency_code": "AFN"
},
{
"country": "Albania",
"currency_code": "ALL"
},
{
"country": "Algeria",
"currency_code": "DZD"
},
{
"country": "American Samoa",
"currency_code": "USD"
},
{
"country": "Andorra",
"currency_code": "EUR"
},
{
"country": "Angola",
"currency_code": "AOA"
},
{
"country": "Anguilla",
"currency_code": "XCD"
},
{
"country": "Antarctica",
"currency_code": "XCD"
},
{
"country": "Antigua and Barbuda",
"currency_code": "XCD"
},
{
"country": "Argentina",
"currency_code": "ARS"
},
{
"country": "Armenia",
"currency_code": "AMD"
},
{
"country": "Aruba",
"currency_code": "AWG"
},
{
"country": "Australia",
"currency_code": "AUD"
},
{
"country": "Austria",
"currency_code": "EUR"
},
{
"country": "Azerbaijan",
"currency_code": "AZN"
},
{
"country": "Bahamas",
"currency_code": "BSD"
},
{
"country": "Bahrain",
"currency_code": "BHD"
},
{
"country": "Bangladesh",
"currency_code": "BDT"
},
{
"country": "Barbados",
"currency_code": "BBD"
},
{
"country": "Belarus",
"currency_code": "BYR"
},
{
"country": "Belgium",
"currency_code": "EUR"
},
{
"country": "Belize",
"currency_code": "BZD"
},
{
"country": "Benin",
"currency_code": "XOF"
},
{
"country": "Bermuda",
"currency_code": "BMD"
},
{
"country": "Bhutan",
"currency_code": "BTN"
},
{
"country": "Bolivia",
"currency_code": "BOB"
},
{
"country": "Bosnia and Herzegovina",
"currency_code": "BAM"
},
{
"country": "Botswana",
"currency_code": "BWP"
},
{
"country": "Bouvet Island",
"currency_code": "NOK"
},
{
"country": "Brazil",
"currency_code": "BRL"
},
{
"country": "British Indian Ocean Territory",
"currency_code": "USD"
},
{
"country": "Brunei",
"currency_code": "BND"
},
{
"country": "Bulgaria",
"currency_code": "BGN"
},
{
"country": "Burkina Faso",
"currency_code": "XOF"
},
{
"country": "Burundi",
"currency_code": "BIF"
},
{
"country": "Cambodia",
"currency_code": "KHR"
},
{
"country": "Cameroon",
"currency_code": "XAF"
},
{
"country": "Canada",
"currency_code": "CAD"
},
{
"country": "Cape Verde",
"currency_code": "CVE"
},
{
"country": "Cayman Islands",
"currency_code": "KYD"
},
{
"country": "Central African Republic",
"currency_code": "XAF"
},
{
"country": "Chad",
"currency_code": "XAF"
},
{
"country": "Chile",
"currency_code": "CLP"
},
{
"country": "China",
"currency_code": "CNY"
},
{
"country": "Christmas Island",
"currency_code": "AUD"
},
{
"country": "Cocos (Keeling) Islands",
"currency_code": "AUD"
},
{
"country": "Colombia",
"currency_code": "COP"
},
{
"country": "Comoros",
"currency_code": "KMF"
},
{
"country": "Congo",
"currency_code": "XAF"
},
{
"country": "Cook Islands",
"currency_code": "NZD"
},
{
"country": "Costa Rica",
"currency_code": "CRC"
},
{
"country": "Croatia",
"currency_code": "HRK"
},
{
"country": "Cuba",
"currency_code": "CUP"
},
{
"country": "Cyprus",
"currency_code": "EUR"
},
{
"country": "Czech Republic",
"currency_code": "CZK"
},
{
"country": "Denmark",
"currency_code": "DKK"
},
{
"country": "Djibouti",
"currency_code": "DJF"
},
{
"country": "Dominica",
"currency_code": "XCD"
},
{
"country": "Dominican Republic",
"currency_code": "DOP"
},
{
"country": "East Timor",
"currency_code": "USD"
},
{
"country": "Ecuador",
"currency_code": "ECS"
},
{
"country": "Egypt",
"currency_code": "EGP"
},
{
"country": "El Salvador",
"currency_code": "SVC"
},
{
"country": "England",
"currency_code": "GBP"
},
{
"country": "Equatorial Guinea",
"currency_code": "XAF"
},
{
"country": "Eritrea",
"currency_code": "ERN"
},
{
"country": "Estonia",
"currency_code": "EUR"
},
{
"country": "Ethiopia",
"currency_code": "ETB"
},
{
"country": "Falkland Islands",
"currency_code": "FKP"
},
{
"country": "Faroe Islands",
"currency_code": "DKK"
},
{
"country": "Fiji Islands",
"currency_code": "FJD"
},
{
"country": "Finland",
"currency_code": "EUR"
},
{
"country": "France",
"currency_code": "EUR"
},
{
"country": "French Guiana",
"currency_code": "EUR"
},
{
"country": "French Polynesia",
"currency_code": "XPF"
},
{
"country": "French Southern territories",
"currency_code": "EUR"
},
{
"country": "Gabon",
"currency_code": "XAF"
},
{
"country": "Gambia",
"currency_code": "GMD"
},
{
"country": "Georgia",
"currency_code": "GEL"
},
{
"country": "Germany",
"currency_code": "EUR"
},
{
"country": "Ghana",
"currency_code": "GHS"
},
{
"country": "Gibraltar",
"currency_code": "GIP"
},
{
"country": "Greece",
"currency_code": "EUR"
},
{
"country": "Greenland",
"currency_code": "DKK"
},
{
"country": "Grenada",
"currency_code": "XCD"
},
{
"country": "Guadeloupe",
"currency_code": "EUR"
},
{
"country": "Guam",
"currency_code": "USD"
},
{
"country": "Guatemala",
"currency_code": "QTQ"
},
{
"country": "Guinea",
"currency_code": "GNF"
},
{
"country": "Guinea-Bissau",
"currency_code": "CFA"
},
{
"country": "Guyana",
"currency_code": "GYD"
},
{
"country": "Haiti",
"currency_code": "HTG"
},
{
"country": "Heard Island and McDonald Islands",
"currency_code": "AUD"
},
{
"country": "Holy See (Vatican City State)",
"currency_code": "EUR"
},
{
"country": "Honduras",
"currency_code": "HNL"
},
{
"country": "Hong Kong",
"currency_code": "HKD"
},
{
"country": "Hungary",
"currency_code": "HUF"
},
{
"country": "Iceland",
"currency_code": "ISK"
},
{
"country": "India",
"currency_code": "INR"
},
{
"country": "Indonesia",
"currency_code": "IDR"
},
{
"country": "Iran",
"currency_code": "IRR"
},
{
"country": "Iraq",
"currency_code": "IQD"
},
{
"country": "Ireland",
"currency_code": "EUR"
},
{
"country": "Israel",
"currency_code": "ILS"
},
{
"country": "Italy",
"currency_code": "EUR"
},
{
"country": "Ivory Coast",
"currency_code": "XOF"
},
{
"country": "Jamaica",
"currency_code": "JMD"
},
{
"country": "Japan",
"currency_code": "JPY"
},
{
"country": "Jordan",
"currency_code": "JOD"
},
{
"country": "Kazakhstan",
"currency_code": "KZT"
},
{
"country": "Kenya",
"currency_code": "KES"
},
{
"country": "Kiribati",
"currency_code": "AUD"
},
{
"country": "Kuwait",
"currency_code": "KWD"
},
{
"country": "Kyrgyzstan",
"currency_code": "KGS"
},
{
"country": "Laos",
"currency_code": "LAK"
},
{
"country": "Latvia",
"currency_code": "LVL"
},
{
"country": "Lebanon",
"currency_code": "LBP"
},
{
"country": "Lesotho",
"currency_code": "LSL"
},
{
"country": "Liberia",
"currency_code": "LRD"
},
{
"country": "Libyan Arab Jamahiriya",
"currency_code": "LYD"
},
{
"country": "Liechtenstein",
"currency_code": "CHF"
},
{
"country": "Lithuania",
"currency_code": "LTL"
},
{
"country": "Luxembourg",
"currency_code": "EUR"
},
{
"country": "Macao",
"currency_code": "MOP"
},
{
"country": "North Macedonia",
"currency_code": "MKD"
},
{
"country": "Madagascar",
"currency_code": "MGF"
},
{
"country": "Malawi",
"currency_code": "MWK"
},
{
"country": "Malaysia",
"currency_code": "MYR"
},
{
"country": "Maldives",
"currency_code": "MVR"
},
{
"country": "Mali",
"currency_code": "XOF"
},
{
"country": "Malta",
"currency_code": "EUR"
},
{
"country": "Marshall Islands",
"currency_code": "USD"
},
{
"country": "Martinique",
"currency_code": "EUR"
},
{
"country": "Mauritania",
"currency_code": "MRO"
},
{
"country": "Mauritius",
"currency_code": "MUR"
},
{
"country": "Mayotte",
"currency_code": "EUR"
},
{
"country": "Mexico",
"currency_code": "MXN"
},
{
"country": "Micronesia, Federated States of",
"currency_code": "USD"
},
{
"country": "Moldova",
"currency_code": "MDL"
},
{
"country": "Monaco",
"currency_code": "EUR"
},
{
"country": "Mongolia",
"currency_code": "MNT"
},
{
"country": "Montserrat",
"currency_code": "XCD"
},
{
"country": "Morocco",
"currency_code": "MAD"
},
{
"country": "Mozambique",
"currency_code": "MZN"
},
{
"country": "Myanmar",
"currency_code": "MMR"
},
{
"country": "Namibia",
"currency_code": "NAD"
},
{
"country": "Nauru",
"currency_code": "AUD"
},
{
"country": "Nepal",
"currency_code": "NPR"
},
{
"country": "Netherlands",
"currency_code": "EUR"
},
{
"country": "Netherlands Antilles",
"currency_code": "ANG"
},
{
"country": "New Caledonia",
"currency_code": "XPF"
},
{
"country": "New Zealand",
"currency_code": "NZD"
},
{
"country": "Nicaragua",
"currency_code": "NIO"
},
{
"country": "Niger",
"currency_code": "XOF"
},
{
"country": "Nigeria",
"currency_code": "NGN"
},
{
"country": "Niue",
"currency_code": "NZD"
},
{
"country": "Norfolk Island",
"currency_code": "AUD"
},
{
"country": "North Korea",
"currency_code": "KPW"
},
{
"country": "Northern Ireland",
"currency_code": "GBP"
},
{
"country": "Northern Mariana Islands",
"currency_code": "USD"
},
{
"country": "Norway",
"currency_code": "NOK"
},
{
"country": "Oman",
"currency_code": "OMR"
},
{
"country": "Pakistan",
"currency_code": "PKR"
},
{
"country": "Palau",
"currency_code": "USD"
},
{
"country": "Palestine",
"currency_code": null
},
{
"country": "Panama",
"currency_code": "PAB"
},
{
"country": "Papua New Guinea",
"currency_code": "PGK"
},
{
"country": "Paraguay",
"currency_code": "PYG"
},
{
"country": "Peru",
"currency_code": "PEN"
},
{
"country": "Philippines",
"currency_code": "PHP"
},
{
"country": "Pitcairn",
"currency_code": "NZD"
},
{
"country": "Poland",
"currency_code": "PLN"
},
{
"country": "Portugal",
"currency_code": "EUR"
},
{
"country": "Puerto Rico",
"currency_code": "USD"
},
{
"country": "Qatar",
"currency_code": "QAR"
},
{
"country": "Reunion",
"currency_code": "EUR"
},
{
"country": "Romania",
"currency_code": "RON"
},
{
"country": "Russian Federation",
"currency_code": "RUB"
},
{
"country": "Rwanda",
"currency_code": "RWF"
},
{
"country": "Saint Helena",
"currency_code": "SHP"
},
{
"country": "Saint Kitts and Nevis",
"currency_code": "XCD"
},
{
"country": "Saint Lucia",
"currency_code": "XCD"
},
{
"country": "Saint Pierre and Miquelon",
"currency_code": "EUR"
},
{
"country": "Saint Vincent and the Grenadines",
"currency_code": "XCD"
},
{
"country": "Samoa",
"currency_code": "WST"
},
{
"country": "San Marino",
"currency_code": "EUR"
},
{
"country": "Sao Tome and Principe",
"currency_code": "STD"
},
{
"country": "Saudi Arabia",
"currency_code": "SAR"
},
{
"country": "Scotland",
"currency_code": "GBP"
},
{
"country": "Senegal",
"currency_code": "XOF"
},
{
"country": "Seychelles",
"currency_code": "SCR"
},
{
"country": "Sierra Leone",
"currency_code": "SLL"
},
{
"country": "Singapore",
"currency_code": "SGD"
},
{
"country": "Slovakia",
"currency_code": "EUR"
},
{
"country": "Slovenia",
"currency_code": "EUR"
},
{
"country": "Solomon Islands",
"currency_code": "SBD"
},
{
"country": "Somalia",
"currency_code": "SOS"
},
{
"country": "South Africa",
"currency_code": "ZAR"
},
{
"country": "South Georgia and the South Sandwich Islands",
"currency_code": "GBP"
},
{
"country": "South Korea",
"currency_code": "KRW"
},
{
"country": "South Sudan",
"currency_code": "SSP"
},
{
"country": "Spain",
"currency_code": "EUR"
},
{
"country": "SriLanka",
"currency_code": "LKR"
},
{
"country": "Sudan",
"currency_code": "SDG"
},
{
"country": "Suriname",
"currency_code": "SRD"
},
{
"country": "Svalbard and Jan Mayen",
"currency_code": "NOK"
},
{
"country": "Swaziland",
"currency_code": "SZL"
},
{
"country": "Sweden",
"currency_code": "SEK"
},
{
"country": "Switzerland",
"currency_code": "CHF"
},
{
"country": "Syria",
"currency_code": "SYP"
},
{
"country": "Tajikistan",
"currency_code": "TJS"
},
{
"country": "Tanzania",
"currency_code": "TZS"
},
{
"country": "Thailand",
"currency_code": "THB"
},
{
"country": "The Democratic Republic of Congo",
"currency_code": "CDF"
},
{
"country": "Togo",
"currency_code": "XOF"
},
{
"country": "Tokelau",
"currency_code": "NZD"
},
{
"country": "Tonga",
"currency_code": "TOP"
},
{
"country": "Trinidad and Tobago",
"currency_code": "TTD"
},
{
"country": "Tunisia",
"currency_code": "TND"
},
{
"country": "Turkey",
"currency_code": "TRY"
},
{
"country": "Turkmenistan",
"currency_code": "TMT"
},
{
"country": "Turks and Caicos Islands",
"currency_code": "USD"
},
{
"country": "Tuvalu",
"currency_code": "AUD"
},
{
"country": "Uganda",
"currency_code": "UGX"
},
{
"country": "Ukraine",
"currency_code": "UAH"
},
{
"country": "United Arab Emirates",
"currency_code": "AED"
},
{
"country": "United Kingdom",
"currency_code": "GBP"
},
{
"country": "United States",
"currency_code": "USD"
},
{
"country": "United States Minor Outlying Islands",
"currency_code": "USD"
},
{
"country": "Uruguay",
"currency_code": "UYU"
},
{
"country": "Uzbekistan",
"currency_code": "UZS"
},
{
"country": "Vanuatu",
"currency_code": "VUV"
},
{
"country": "Venezuela",
"currency_code": "VEF"
},
{
"country": "Vietnam",
"currency_code": "VND"
},
{
"country": "Virgin Islands, British",
"currency_code": "USD"
},
{
"country": "Virgin Islands, U.S.",
"currency_code": "USD"
},
{
"country": "Wales",
"currency_code": "GBP"
},
{
"country": "Wallis and Futuna",
"currency_code": "XPF"
},
{
"country": "Western Sahara",
"currency_code": "MAD"
},
{
"country": "Yemen",
"currency_code": "YER"
},
{
"country": "Yugoslavia",
"currency_code": null
},
{
"country": "Zambia",
"currency_code": "ZMW"
},
{
"country": "Zimbabwe",
"currency_code": "ZWD"
}
]

View file

@ -1,50 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the current date and time.
Parameters:
* datetime.format: strftime()-compatible formatting string
* date.format : alias for datetime.format
* time.format : alias for datetime.format
* datetime.locale: locale to use rather than the system default
* date.locale : alias for datetime.locale
* time.locale : alias for datetime.locale
"""
from __future__ import absolute_import
import datetime
import locale
import bumblebee.engine
def default_format(module):
default = "%x %X"
if module == "date":
default = "%x"
if module == "time":
default = "%X"
return default
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.get_time))
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="calendar")
self._fmt = self.parameter("format", default_format(self.name))
l = locale.getdefaultlocale()
if not l or l == (None, None):
l = ('en_US', 'UTF-8')
lcl = self.parameter("locale", ".".join(l))
try:
locale.setlocale(locale.LC_TIME, lcl.split("."))
except Exception as e:
locale.setlocale(locale.LC_TIME, ('en_US', 'UTF-8'))
def get_time(self, widget):
enc = locale.getpreferredencoding()
retval = datetime.datetime.now().strftime(self._fmt)
if hasattr(retval, "decode"):
return retval.decode(enc)
return retval
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,143 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the current song being played in DeaDBeeF and provides
some media control bindings.
Left click toggles pause, scroll up skips the current song, scroll
down returns to the previous song.
Requires the following library:
* subprocess
Parameters:
* deadbeef.format: Format string (defaults to "{artist} - {title}")
Available values are: {artist}, {title}, {album}, {length},
{trackno}, {year}, {comment},
{copyright}, {time}
This is deprecated, but much simpler.
* deadbeef.tf_format: A foobar2000 title formatting-style format string.
These can be much more sophisticated than the standard
format strings. This is off by default, but specifying
any tf_format will enable it. If both deadbeef.format
and deadbeef.tf_format are specified, deadbeef.tf_format
takes priority.
* deadbeef.tf_format_if_stopped: Controls whether or not the tf_format format
string should be displayed even if no song is paused or
playing. This could be useful if you want to implement
your own stop strings with the built in logic. Any non-
null value will enable this (by default the module will
hide itself when the player is stopped).
* deadbeef.previous: Change binding for previous song (default is left click)
* deadbeef.next: Change binding for next song (default is right click)
* deadbeef.pause: Change binding for toggling pause (default is middle click)
Available options for deadbeef.previous, deadbeef.next and deadbeef.pause are:
LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, SCROLL_UP, SCROLL_DOWN
"""
import sys
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from bumblebee.output import scrollable
try:
import subprocess
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.deadbeef)
)
buttons = {"LEFT_CLICK": bumblebee.input.LEFT_MOUSE,
"RIGHT_CLICK": bumblebee.input.RIGHT_MOUSE,
"MIDDLE_CLICK": bumblebee.input.MIDDLE_MOUSE,
"SCROLL_UP": bumblebee.input.WHEEL_UP,
"SCROLL_DOWN": bumblebee.input.WHEEL_DOWN,
}
self._song = ""
self._format = self.parameter("format", "{artist} - {title}")
self._tf_format = self.parameter("tf_format", "")
self._show_tf_when_stopped = bool(self.parameter("tf_format_if_stopped", ""))
prev_button = self.parameter("previous", "LEFT_CLICK")
next_button = self.parameter("next", "RIGHT_CLICK")
pause_button = self.parameter("pause", "MIDDLE_CLICK")
self.now_playing = ["deadbeef", "--nowplaying", "%a;%t;%b;%l;%n;%y;%c;%r;%e"]
self.now_playing_tf = ["deadbeef", "--nowplaying-tf", ""]
cmd = "deadbeef "
engine.input.register_callback(self, button=buttons[prev_button],
cmd=cmd + "--prev")
engine.input.register_callback(self, button=buttons[next_button],
cmd=cmd + "--next")
engine.input.register_callback(self, button=buttons[pause_button],
cmd=cmd + "--play-pause")
# modify the tf_format if we don't want it to show on stop
# this adds conditions to the query itself, rather than
# polling to see if deadbeef is running
# doing this reduces the number of calls we have to make
if self._tf_format and not self._show_tf_when_stopped:
self._tf_format = "$if($or(%isplaying%,%ispaused%),{query})".format(query=self._tf_format)
@scrollable
def deadbeef(self, widget):
return self.string_song
def hidden(self):
return self.string_song == ""
def update(self, widgets):
try:
if self._tf_format == "": # no tf format set, use the old style
return self.update_standard(widgets)
return self.update_tf(widgets)
except Exception:
self._song = "error"
def update_tf(self, widgets):
## ensure that deadbeef is actually running
## easiest way to do this is to check --nowplaying for
## the string "nothing"
if read_process(self.now_playing) == "nothing":
self._song = ""
return
## perform the actual query -- these can be much more sophisticated
self.now_playing_tf[-1] = self._tf_format
data = read_process(self.now_playing_tf)
self._song = data
def update_standard(self, widgets):
data = read_process(self.now_playing)
if data == "nothing":
self._song = ""
else:
data = data.split(";")
self._song = self._format.format(artist=data[0],
title=data[1],
album=data[2],
length=data[3],
trackno=data[4],
year=data[5],
comment=data[6],
copyright=data[7],
time=data[8])
@property
def string_song(self):
"""\
Returns the current song as a string, either as a unicode() (Python <
3) or a regular str() (Python >= 3)
"""
if sys.version_info.major < 3:
return unicode(self._song)
return str(self._song)
def read_process(command):
proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
return proc.stdout.read()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,77 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the current song being played
Requires the following library:
* python-dbus
Parameters:
* deezer.format: Format string (defaults to "{artist} - {title}")
Available values are: {album}, {title}, {artist}, {trackNumber}, {playbackStatus}
* deezer.previous: Change binding for previous song (default is left click)
* deezer.next: Change binding for next song (default is right click)
* deezer.pause: Change binding for toggling pause (default is middle click)
Available options for deezer.previous, deezer.next and deezer.pause are:
LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, SCROLL_UP, SCROLL_DOWN
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from bumblebee.output import scrollable
try:
import dbus
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.deezer)
)
buttons = {"LEFT_CLICK":bumblebee.input.LEFT_MOUSE,
"RIGHT_CLICK":bumblebee.input.RIGHT_MOUSE,
"MIDDLE_CLICK":bumblebee.input.MIDDLE_MOUSE,
"SCROLL_UP":bumblebee.input.WHEEL_UP,
"SCROLL_DOWN":bumblebee.input.WHEEL_DOWN,
}
self._song = ""
self._format = self.parameter("format", "{artist} - {title}")
prev_button = self.parameter("previous", "LEFT_CLICK")
next_button = self.parameter("next", "RIGHT_CLICK")
pause_button = self.parameter("pause", "MIDDLE_CLICK")
cmd = "dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.deezer \
/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player."
engine.input.register_callback(self, button=buttons[prev_button],
cmd=cmd + "Previous")
engine.input.register_callback(self, button=buttons[next_button],
cmd=cmd + "Next")
engine.input.register_callback(self, button=buttons[pause_button],
cmd=cmd + "PlayPause")
## @scrollable
def deezer(self, widget):
return str(self._song)
def hidden(self):
return str(self._song) == ""
def update(self, widgets):
try:
bus = dbus.SessionBus()
deezer = bus.get_object("org.mpris.MediaPlayer2.deezer", "/org/mpris/MediaPlayer2")
deezer_iface = dbus.Interface(deezer, 'org.freedesktop.DBus.Properties')
props = deezer_iface.Get('org.mpris.MediaPlayer2.Player', 'Metadata')
playback_status = str(deezer_iface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus'))
self._song = self._format.format(album=str(props.get('xesam:album')),
title=str(props.get('xesam:title')),
artist=','.join(props.get('xesam:artist')),
trackNumber=str(props.get('xesam:trackNumber')),
playbackStatus=u"\u25B6" if playback_status=="Playing" else u"\u258D\u258D" if playback_status=="Paused" else "",)
except Exception:
self._song = ""
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,88 +0,0 @@
# pylint: disable=C0111,R0903
"""Shows free diskspace, total diskspace and the percentage of free disk space.
Parameters:
* disk.warning: Warning threshold in % of disk space (defaults to 80%)
* disk.critical: Critical threshold in % of disk space (defaults ot 90%)
* disk.path: Path to calculate disk usage from (defaults to /)
* disk.open: Which application / file manager to launch (default xdg-open)
* disk.format: Format string, tags {path}, {used}, {left}, {size} and {percent} (defaults to "{path} {used}/{size} ({percent:05.02f}%)")
* (deprecated) disk.showUsed: Show used space (defaults to yes)
* (deprecated) disk.showSize: Show total size (defaults to yes)
* (deprecated) disk.showPercent: Show usage percentage (defaults to yes)
"""
import os
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.diskspace)
)
self._path = self.parameter("path", "/")
self._format = self.parameter("format", "{used}/{size} ({percent:05.02f}%)")
self._app = self.parameter("open", "xdg-open")
self._used = 0
self._left = 0
self._size = 0
self._percent = 0
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="{} {}".format(self._app,
self._path))
def diskspace(self, widget):
used_str = bumblebee.util.bytefmt(self._used)
size_str = bumblebee.util.bytefmt(self._size)
left_str = bumblebee.util.bytefmt(self._left)
percent_str = self._percent
sused = self.has_parameter("showUsed")
ssize = self.has_parameter("showSize")
spercent = self.has_parameter("showPercent")
if all(not param for param in (sused, ssize, spercent)):
return self._format.format(path=self._path,
used=used_str,
left=left_str,
size=size_str,
percent=percent_str)
else:
rv = ""
sused = bumblebee.util.asbool(self.parameter("showUsed", True))
ssize = bumblebee.util.asbool(self.parameter("showSize", True))
spercent = bumblebee.util.asbool(self.parameter("showPercent", True))
if sused:
rv = "{}{}".format(rv, used_str)
if sused and ssize:
rv = "{}/".format(rv)
if ssize:
rv = "{}{}".format(rv, size_str)
if spercent:
if not sused and not ssize:
rv = "{:05.02f}%".format(percent_str)
else:
rv = "{} ({:05.02f}%)".format(rv, percent_str)
return rv
def update(self, widgets):
st = os.statvfs(self._path)
self._size = st.f_blocks * st.f_frsize
self._used = (st.f_blocks - st.f_bfree) * st.f_frsize
self._left = self._size - self._used;
self._percent = 100.0 * self._used/self._size
def state(self, widget):
return self.threshold_state(self._percent, 80, 90)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,77 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays DNF package update information (<security>/<bugfixes>/<enhancements>/<other>)
Requires the following executable:
* dnf
Parameters:
* dnf.interval: Time in minutes between two consecutive update checks (defaults to 30 minutes)
"""
import threading
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
def get_dnf_info(widget):
try:
res = bumblebee.util.execute("dnf updateinfo")
except RuntimeError:
pass
security = 0
bugfixes = 0
enhancements = 0
other = 0
for line in res.split("\n"):
if not line.startswith(" "): continue
elif "ecurity" in line:
for s in line.split():
if s.isdigit(): security += int(s)
elif "ugfix" in line:
for s in line.split():
if s.isdigit(): bugfixes += int(s)
elif "hancement" in line:
for s in line.split():
if s.isdigit(): enhancements += int(s)
else:
for s in line.split():
if s.isdigit(): other += int(s)
widget.set("security", security)
widget.set("bugfixes", bugfixes)
widget.set("enhancements", enhancements)
widget.set("other", other)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.updates)
super(Module, self).__init__(engine, config, widget)
self.interval_factor(60)
self.interval(30)
def updates(self, widget):
result = []
for t in ["security", "bugfixes", "enhancements", "other"]:
result.append(str(widget.get(t, 0)))
return "/".join(result)
def update(self, widgets):
thread = threading.Thread(target=get_dnf_info, args=(widgets[0],))
thread.start()
def state(self, widget):
cnt = 0
for t in ["security", "bugfixes", "enhancements", "other"]:
cnt += widget.get(t, 0)
if cnt == 0:
return "good"
if cnt > 50 or widget.get("security", 0) > 0:
return "critical"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,50 +0,0 @@
# -*- coding: utf-8 -*-
"""Displays the number of docker containers running
Requires the following python packages:
* docker
"""
try:
import docker
except ImportError:
pass
from requests.exceptions import ConnectionError
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = bumblebee.output.Widget(full_text=self.status)
super(Module, self).__init__(engine, config, widgets)
self._status = self.status
self._state = self.state
def update(self, widgets):
self._status = self.status
self._state = self.state
def state(self, widget):
state = []
status = self.status(widget)
if status == "OK - 0":
state.append("warning")
elif status in ["n/a", "Daemon off"]:
state.append("critical")
return state
def status(self, widget):
try:
cli = docker.DockerClient(base_url='unix://var/run/docker.sock')
cli.ping()
except ConnectionError:
return "Daemon off"
except Exception:
return "n/a"
return "OK - {}".format(len(cli.containers.list(filters={'status': "running"})))

View file

@ -1,39 +0,0 @@
#pylint: disable=C0111,R0903
"""Toggle dunst notifications."""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text="")
)
self._paused = False
# Make sure that dunst is currently not paused
try:
bumblebee.util.execute("killall -SIGUSR2 dunst")
except:
pass
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.toggle_status
)
def toggle_status(self, event):
self._paused = not self._paused
try:
if self._paused:
bumblebee.util.execute("killall -SIGUSR1 dunst")
else:
bumblebee.util.execute("killall -SIGUSR2 dunst")
except:
self._paused = not self._paused # toggling failed
def state(self, widget):
if self._paused:
return ["muted", "warning"]
return ["unmuted"]

View file

@ -1,26 +0,0 @@
# pylint: disable=C0111,R0903
"""Draws an error widget.
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.text)
)
self._text = ""
def set(self, text):
self._text = text
def text(self, widget):
return self._text
def state(self, widget):
return ["critical"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,75 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the price of a cryptocurrency.
Requires the following python packages:
* requests
Parameters:
* getcrypto.interval: Interval in seconds for updating the price, default is 120, less than that will probably get your IP banned.
* getcrypto.getbtc: 0 for not getting price of BTC, 1 for getting it (default).
* getcrypto.geteth: 0 for not getting price of ETH, 1 for getting it (default).
* getcrypto.getltc: 0 for not getting price of LTC, 1 for getting it (default).
* getcrypto.getcur: Set the currency to display the price in, usd is the default.
"""
import requests
import time
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from requests.exceptions import RequestException
def getfromkrak(coin, currency):
abbrev = {
"Btc": ["xbt", "XXBTZ"],
"Eth": ["eth", "XETHZ"],
"Ltc": ["ltc", "XLTCZ"],
}
data = abbrev.get(coin, None)
if not data: return
epair = "{}{}".format(data[0], currency)
tickname = "{}{}".format(data[1], currency.upper())
try:
krakenget = requests.get('https://api.kraken.com/0/public/Ticker?pair='+epair).json()
except (RequestException, Exception):
return "No connection"
if not 'result' in krakenget:
return "No data"
kethusdask = float(krakenget['result'][tickname]['a'][0])
kethusdbid = float(krakenget['result'][tickname]['b'][0])
return coin+": "+str((kethusdask+kethusdbid)/2)[0:6]
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.curprice)
)
self._curprice = ""
self._nextcheck = 0
self._interval = int(self.parameter("interval", "120"))
self._getbtc = bumblebee.util.asbool(self.parameter("getbtc", True))
self._geteth = bumblebee.util.asbool(self.parameter("geteth", True))
self._getltc = bumblebee.util.asbool(self.parameter("getltc", True))
self._getcur = self.parameter("getcur", "usd")
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="xdg-open https://cryptowat.ch/")
def curprice(self, widget):
return self._curprice
def update(self, widgets):
if self._nextcheck < int(time.time()):
self._nextcheck = int(time.time()) + self._interval
currency = self._getcur
btcprice, ethprice, ltcprice = "", "", ""
if self._getbtc:
btcprice = getfromkrak('Btc', currency)
if self._geteth:
ethprice = getfromkrak('Eth', currency)
if self._getltc:
ltcprice = getfromkrak('Ltc', currency)
self._curprice = btcprice+" "*(self._getbtc*self._geteth)+ethprice+" "*(self._getltc*max(self._getbtc, self._geteth))+ltcprice
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,81 +0,0 @@
# pylint: disable=C0111,R0903
"""Print the branch and git status for the
currently focused window.
Requires:
* xcwd
* Python module 'pygit2'
"""
import os
import string
import logging
try:
import pygit2
except ImportError:
pass
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = []
super(Module, self).__init__(engine, config, widgets)
self._engine = engine
self._error = False
self.update(self.widgets())
def hidden(self):
return self._error
def update(self, widgets):
state = {}
new_widgets = []
try:
directory = bumblebee.util.execute("xcwd").strip()
directory = self._get_git_root(directory)
repo = pygit2.Repository(directory)
new_widgets.append(bumblebee.output.Widget(name='git.main', full_text=repo.head.shorthand))
for filepath, flags in repo.status().items():
if flags == pygit2.GIT_STATUS_WT_NEW or \
flags == pygit2.GIT_STATUS_INDEX_NEW:
state['new'] = True
if flags == pygit2.GIT_STATUS_WT_DELETED or \
flags == pygit2.GIT_STATUS_INDEX_DELETED:
state['deleted'] = True
if flags == pygit2.GIT_STATUS_WT_MODIFIED or \
flags == pygit2.GIT_STATUS_INDEX_MODIFIED:
state['modified'] = True
self._error = False
if 'new' in state:
new_widgets.append(bumblebee.output.Widget(name='git.new'))
if 'modified' in state:
new_widgets.append(bumblebee.output.Widget(name='git.modified'))
if 'deleted' in state:
new_widgets.append(bumblebee.output.Widget(name='git.deleted'))
while len(widgets) > 0:
del widgets[0]
for widget in new_widgets:
widgets.append(widget)
except Exception as e:
self._error = True
def state(self, widget):
return widget.name.split('.')[1]
def _get_git_root(self, directory):
while len(directory) > 1:
if os.path.exists(os.path.join(directory, ".git")):
return directory
directory = "/".join(directory.split("/")[0:-1])
return "/"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,62 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the unread GitHub notifications for a GitHub user
Requires the following library:
* requests
Parameters:
* github.token: GitHub user access token, the token needs to have the 'notifications' scope.
* github.interval: Interval in minutes between updates, default is 5.
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
try:
import requests
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.github)
)
self._count = 0
self.interval_factor(60)
self.interval(5)
self._requests = requests.Session()
cmd = "xdg-open"
if not bumblebee.util.which(cmd):
cmd = "x-www-browser"
self._requests.headers.update({"Authorization":"token {}".format(self.parameter("token", ""))})
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="{} https://github.com/notifications".format(cmd))
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd=self.update)
def github(self, _):
return str(self._count)
def update(self, _):
try:
self._count = 0
url = "https://api.github.com/notifications"
while True:
notifications = self._requests.get(url)
self._count += len(list(filter(lambda notification: notification['unread'], notifications.json())))
next_link = notifications.links.get('next')
if next_link is not None:
url = next_link.get('url')
else:
break
except Exception:
self._count = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,56 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays information about the current song in Google Play music player.
Requires the following executable:
* gpmdp-remote
"""
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = [
bumblebee.output.Widget(name="gpmdp.prev"),
bumblebee.output.Widget(name="gpmdp.main", full_text=self.description),
bumblebee.output.Widget(name="gpmdp.next"),
]
super(Module, self).__init__(engine, config, widgets)
engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE,
cmd="playerctl previous")
engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE,
cmd="playerctl play-pause")
engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE,
cmd="playerctl next")
self._status = None
self._tags = None
def description(self, widget):
return self._tags if self._tags else "n/a"
def update(self, widgets):
self._load_song()
def state(self, widget):
if widget.name == "gpmdp.prev":
return "prev"
if widget.name == "gpmdp.next":
return "next"
return self._status
def _load_song(self):
info = ""
try:
info = bumblebee.util.execute("gpmdp-remote current")
status = bumblebee.util.execute("gpmdp-remote status")
except RuntimeError:
pass
self._status = status.split("\n")[0].lower()
self._tags = info.split("\n")[0]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,24 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the system hostname."""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.output)
)
self._hname = ""
def output(self, _):
return self._hname+" "+u"\uf233"
def update(self, widgets):
with open('/proc/sys/kernel/hostname', 'r') as f:
self._hname = f.readline().split()[0]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,68 +0,0 @@
# pylint: disable=C0111,R0903
"""Display HTTP status code
Parameters:
* http_status.label: Prefix label (optional)
* http_status.target: Target to retrieve the HTTP status from
* http_status.expect: Expected HTTP status
"""
from requests import head
import psutil
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
UNK = "UNK"
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.output)
super(Module, self).__init__(engine, config, widget)
self._label = self.parameter("label")
self._target = self.parameter("target")
self._expect = self.parameter("expect", "200")
self._status = self.getStatus()
self._output = self.getOutput()
def labelize(self, s):
if self._label is None:
return s
return "{}: {}".format(self._label, s)
def getStatus(self):
try:
res = head(self._target)
except Exception:
return self.UNK
else:
status = str(res.status_code)
self._status = status
return status
def getOutput(self):
if self._status == self._expect:
return self.labelize(self._status)
else:
reason = " != {}".format(self._expect)
return self.labelize("{}{}".format(self._status, reason))
def output(self, widget):
return self._output
def update(self, widgets):
self.getStatus()
self._output = self.getOutput()
def state(self, widget):
if self._status == self.UNK:
return "warning"
if self._status != self._expect:
return "critical"
return self._output
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,51 +0,0 @@
#pylint: disable=C0111,R0903
"""Displays the indicator status, for numlock, scrolllock and capslock
Parameters:
* indicator.include: Comma-separated list of interface prefixes to include (defaults to "numlock,capslock")
* indicator.signalstype: If you want the signali type color to be "critical" or "warning" (defaults to "warning")
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = []
self.status = False
super(Module,self).__init__(engine, config, widgets)
self._include = tuple(filter(len, self.parameter("include", "NumLock,CapsLock").split(",")))
self._signalType = self.parameter("signaltype") if not self.parameter("signaltype") is None else "warning"
def update(self, widgets):
self._update_widgets(widgets)
def state(self, widget):
states = []
if widget.status:
states.append(self._signalType)
elif not widget.status:
states.append("normal")
return states
def _update_widgets(self, widgets):
status_line = ""
for line in bumblebee.util.execute("xset q").replace(" ", "").split("\n"):
if "capslock" in line.lower():
status_line = line
break
for indicator in self._include:
widget = self.widget(indicator)
if not widget:
widget = bumblebee.output.Widget(name=indicator)
widgets.append(widget)
widget.status = True if indicator.lower()+":on" in status_line.lower() else False
widget.full_text(indicator)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,21 +0,0 @@
# pylint: disable=C0111,R0903
"""Shows Linux kernel version information"""
import platform
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.output)
)
self._release_name = platform.release()
def output(self, widget):
return self._release_name
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,65 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the current keyboard layout using libX11
Requires the following library:
* libX11.so.6
and python module:
* xkbgroup
Parameters:
* layout-xkb.showname: Boolean that indicate whether the full name should be displayed. Defaults to false (only the symbol will be displayed)
* layout-xkb.show_variant: Boolean that indecates whether the variant name should be displayed. Defaults to true.
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
has_xkb = True
try:
from xkbgroup import *
except ImportError:
has_xkb = False
import logging
log = logging.getLogger(__name__)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.current_layout)
)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self._next_keymap)
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE,
cmd=self._prev_keymap)
self._show_variant = bumblebee.util.asbool(self.parameter("show_variant", "true"))
def _next_keymap(self, event):
self._set_keymap(1)
def _prev_keymap(self, event):
self._set_keymap(-1)
def _set_keymap(self, rotation):
if not has_xkb: return
xkb = XKeyboard()
if xkb.groups_count < 2: return # nothing to doA
layouts = xkb.groups_symbols
idx = layouts.index(xkb.group_symbol)
xkb.group_symbol = str(layouts[(idx + rotation) % len(layouts)])
def current_layout(self, widget):
try:
xkb = XKeyboard()
log.debug("group num: {}".format(xkb.group_num))
name = xkb.group_name if bumblebee.util.asbool(self.parameter("showname")) else xkb.group_symbol
if self._show_variant:
return "{} ({})".format(name, xkb.group_variant) if xkb.group_variant else name
return name
except Exception:
return "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,40 +0,0 @@
"""Displays and changes the current keyboard layout
Requires the following executable:
* xkb-switch
"""
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.current_layout)
super(Module, self).__init__(engine, config, widget)
engine.input.register_callback(
self,
button=bumblebee.input.LEFT_MOUSE,
cmd=self._next_keymap)
self._current_layout = self._get_current_layout()
def current_layout(self, __):
return self._current_layout
def _next_keymap(self, event):
try:
bumblebee.util.execute("xkb-switch -n")
except RuntimeError:
pass
def _get_current_layout(self):
try:
res = bumblebee.util.execute("xkb-switch")
return res.split("\n")[0]
except RuntimeError:
return ["n/a"]
def update(self, __):
self._current_layout = self._get_current_layout()

View file

@ -1,30 +0,0 @@
"""Displays count of running libvirt VMs.
Required the following python packages:
* libvirt
* sys
"""
import sys
import libvirt
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.status)
)
self._status = self.status
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="virt-manager")
def update(self, widgets):
self._status = self.status
def status(self, _):
conn = None
conn = libvirt.openReadOnly(None)
if conn == None:
print ('Failed to open connection to the hypervisor')
return "VMs %s" % (conn.numOfDomains())

View file

@ -1,72 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays available RAM, total amount of RAM and percentage available.
Parameters:
* memory.warning : Warning threshold in % of memory used (defaults to 80%)
* memory.critical: Critical threshold in % of memory used (defaults to 90%)
* memory.format: Format string (defaults to "{used}/{total} ({percent:05.02f}%)")
* memory.usedonly: Only show the amount of RAM in use (defaults to False). Same as memory.format="{used}"
"""
import re
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Container(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.memory_usage)
)
self.update(None)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="gnome-system-monitor")
@property
def _format(self):
if bumblebee.util.asbool(self.parameter("usedonly", False)):
return "{used}"
else:
return self.parameter("format", "{used}/{total} ({percent:05.02f}%)")
def memory_usage(self, widget):
return self._format.format(**self._mem)
def update(self, widgets):
data = {}
with open("/proc/meminfo", "r") as f:
for line in f:
tmp = re.split(r"[:\s]+", line)
value = int(tmp[1])
if tmp[2] == "kB": value = value*1024
if tmp[2] == "mB": value = value*1024*1024
if tmp[2] == "gB": value = value*1024*1024*1024
data[tmp[0]] = value
if "MemAvailable" in data:
used = data["MemTotal"] - data["MemAvailable"]
else:
used = data["MemTotal"] - data["MemFree"] - data["Buffers"] - data["Cached"] - data["Slab"]
self._mem = {
"total": bumblebee.util.bytefmt(data["MemTotal"]),
"available": bumblebee.util.bytefmt(data["MemAvailable"]),
"free": bumblebee.util.bytefmt(data["MemFree"]),
"used": bumblebee.util.bytefmt(used),
"percent": float(used)/float(data["MemTotal"])*100.0
}
def state(self, widget):
if self._mem["percent"] > float(self.parameter("critical", 90)):
return "critical"
if self._mem["percent"] > float(self.parameter("warning", 80)):
return "warning"
return None
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,60 +0,0 @@
# pylint: disable=C0111,R0903
# -*- coding: utf-8 -*-
"""Displays information about the current song in mocp. Left click toggles play/pause. Right click toggles shuffle.
Requires the following executable:
* mocp
Parameters:
* mocp.format: Format string for the song information. Replace string sequences with the actual information:
%state State
%file File
%title Title, includes track, artist, song title and album
%artist Artist
%song SongTitle
%album Album
%tt TotalTime
%tl TimeLeft
%ts TotalSec
%ct CurrentTime
%cs CurrentSec
%b Bitrate
%r Sample rate
"""
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from bumblebee.output import scrollable
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(name="mocp.main", full_text=self.description)
)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="mocp -G")
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE,
cmd="mocp -t shuffle")
self._format = self.parameter("format", "%state %artist - %song | %ct/%tt")
self._running = 0
#@scrollable
def description(self, widget):
return self._info if self._running == 1 else "Music On Console Player"
def update(self, widgets):
self._load_song()
def _load_song(self):
try:
self._info = bumblebee.util.execute("mocp -Q '" + self._format + "'" ).strip()
self._running = 1
except RuntimeError:
self._running = 0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,181 +0,0 @@
# pylint: disable=C0111,R0903
# -*- coding: utf-8 -*-
"""Displays information about the current song in mpd.
Requires the following executable:
* mpc
Parameters:
* mpd.format: Format string for the song information.
Supported tags (see `man mpc` for additional information)
* {name}
* {artist}
* {album}
* {albumartist}
* {comment}
* {composer}
* {date}
* {originaldate}
* {disc}
* {genre}
* {performer}
* {title}
* {track}
* {time}
* {file}
* {id}
* {prio}
* {mtime}
* {mdate}
Additional tags:
* {position} - position of currently playing song
not to be confused with %position% mpc tag
* {duration} - duration of currently playing song
* {file1} - song file name without path prefix
if {file} = '/foo/bar.baz', then {file1} = 'bar.baz'
* {file2} - song file name without path prefix and extension suffix
if {file} = '/foo/bar.baz', then {file2} = 'bar'
* mpd.host: MPD host to connect to. (mpc behaviour by default)
* mpd.layout: Space-separated list of widgets to add. Possible widgets are the buttons/toggles mpd.prev, mpd.next, mpd.shuffle and mpd.repeat, and the main display with play/pause function mpd.main.
"""
from collections import defaultdict
import string
import os
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from bumblebee.output import scrollable
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config, [])
self._layout = self.parameter("layout", "mpd.prev mpd.main mpd.next mpd.shuffle mpd.repeat")
self._fmt = self.parameter("format", "{artist} - {title} {position}/{duration}")
self._status = None
self._shuffle = False
self._repeat = False
self._tags = defaultdict(lambda: '')
if not self.parameter("host"):
self._hostcmd = ""
else:
self._hostcmd = " -h " + self.parameter("host")
# Create widgets
widget_list = []
widget_map = {}
for widget_name in self._layout.split():
widget = bumblebee.output.Widget(name=widget_name)
widget_list.append(widget)
if widget_name == "mpd.prev":
widget_map[widget] = {"button": bumblebee.input.LEFT_MOUSE, "cmd": "mpc prev" + self._hostcmd}
elif widget_name == "mpd.main":
widget_map[widget] = {"button": bumblebee.input.LEFT_MOUSE, "cmd": "mpc toggle" + self._hostcmd}
widget.full_text(self.description)
elif widget_name == "mpd.next":
widget_map[widget] = {"button": bumblebee.input.LEFT_MOUSE, "cmd": "mpc next" + self._hostcmd}
elif widget_name == "mpd.shuffle":
widget_map[widget] = {"button": bumblebee.input.LEFT_MOUSE, "cmd": "mpc random" + self._hostcmd}
elif widget_name == "mpd.repeat":
widget_map[widget] = {"button": bumblebee.input.LEFT_MOUSE, "cmd": "mpc repeat" + self._hostcmd}
else:
raise KeyError("The mpd module does not support a {widget_name!r} widget".format(widget_name=widget_name))
self.widgets(widget_list)
# Register input callbacks
for widget, callback_options in widget_map.items():
engine.input.register_callback(widget, **callback_options)
def hidden(self):
return self._status is None
@scrollable
def description(self, widget):
return string.Formatter().vformat(self._fmt, (), self._tags)
def update(self, widgets):
self._load_song()
def state(self, widget):
if widget.name == "mpd.shuffle":
return "shuffle-on" if self._shuffle else "shuffle-off"
if widget.name == "mpd.repeat":
return "repeat-on" if self._repeat else "repeat-off"
if widget.name == "mpd.prev":
return "prev"
if widget.name == "mpd.next":
return "next"
return self._status
def _load_song(self):
info = ""
try:
tags = ['name',
'artist',
'album',
'albumartist',
'comment',
'composer',
'date',
'originaldate',
'disc',
'genre',
'performer',
'title',
'track',
'time',
'file',
'id',
'prio',
'mtime',
'mdate']
joinedtags = "\n".join(["tag {0} %{0}%".format(tag) for tag in tags])
info = bumblebee.util.execute('mpc -f ' + '"' + joinedtags + '"' + self._hostcmd)
except RuntimeError:
pass
self._tags = defaultdict(lambda: '')
self._status = None
for line in info.split("\n"):
if line.startswith("[playing]"):
self._status = "playing"
elif line.startswith("[paused]"):
self._status = "paused"
if line.startswith("["):
timer = line.split()[2]
position = timer.split("/")[0]
dur = timer.split("/")[1]
duration = dur.split(" ")[0]
self._tags.update({'position': position})
self._tags.update({'duration': duration})
if line.startswith("volume"):
value = line.split(" ", 2)[1:]
for option in value:
if option.startswith("repeat: on"):
self._repeat = True
elif option.startswith("repeat: off"):
self._repeat = False
elif option.startswith("random: on"):
self._shuffle = True
elif option.startswith("random: off"):
self._shuffle = False
if line.startswith("tag"):
key, value = line.split(" ", 2)[1:]
self._tags.update({key: value})
if key == "file":
self._tags.update({"file1": os.path.basename(value)})
self._tags.update(
{"file2":
os.path.splitext(os.path.basename(value))[0]})
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,114 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Displays network traffic
* No extra configuration needed
"""
import psutil
import netifaces
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
WIDGET_NAME = 'network_traffic'
class Module(bumblebee.engine.Module):
"""Bumblebee main module """
def __init__(self, engine, config):
super(Module, self).__init__(engine, config)
try:
self._bandwidth = BandwidthInfo()
self._bytes_recv = self._bandwidth.bytes_recv()
self._bytes_sent = self._bandwidth.bytes_sent()
except Exception:
""" We do not want do explode anything """
pass
@classmethod
def state(cls, widget):
"""Return the widget state"""
if widget.name == '{}.rx'.format(WIDGET_NAME):
return 'rx'
elif widget.name == '{}.tx'.format(WIDGET_NAME):
return 'tx'
return None
def update(self, widgets):
try:
bytes_recv = self._bandwidth.bytes_recv()
bytes_sent = self._bandwidth.bytes_sent()
download_rate = (bytes_recv - self._bytes_recv)
upload_rate = (bytes_sent - self._bytes_sent)
self.update_widgets(widgets, download_rate, upload_rate)
self._bytes_recv, self._bytes_sent = bytes_recv, bytes_sent
except Exception:
""" We do not want do explode anything """
pass
@classmethod
def update_widgets(cls, widgets, download_rate, upload_rate):
"""Update tx/rx widgets with new rates"""
del widgets[:]
widgets.extend((
TrafficWidget(text=download_rate, direction='rx'),
TrafficWidget(text=upload_rate, direction='tx')
))
class BandwidthInfo(object):
"""Get received/sent bytes from network adapter"""
def bytes_recv(self):
"""Return received bytes"""
return self.bandwidth().bytes_recv
def bytes_sent(self):
"""Return sent bytes"""
return self.bandwidth().bytes_sent
def bandwidth(self):
"""Return bandwidth information"""
io_counters = self.io_counters()
return io_counters[self.default_network_adapter()]
@classmethod
def default_network_adapter(cls):
"""Return default active network adapter"""
gateway = netifaces.gateways()['default']
if not gateway:
raise 'No default gateway found'
return gateway[netifaces.AF_INET][1]
@classmethod
def io_counters(cls):
"""Return IO counters"""
return psutil.net_io_counters(pernic=True)
class TrafficWidget(object):
"""Create a traffic widget with humanized bytes string with proper icon (up/down)"""
def __new__(cls, text, direction):
widget = bumblebee.output.Widget(name='{0}.{1}'.format(WIDGET_NAME, direction))
widget.set('theme.minwidth', '0000000KiB/s')
widget.full_text(cls.humanize(text))
return widget
@staticmethod
def humanize(text):
"""Return humanized bytes"""
humanized_byte_format = bumblebee.util.bytefmt(text)
return '{0}/s'.format(humanized_byte_format)

View file

@ -1,119 +0,0 @@
#pylint: disable=C0111,R0903
"""Displays the name, IP address(es) and status of each available network interface.
Requires the following python module:
* netifaces
Parameters:
* nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth,br")
* nic.include: Comma-separated list of interfaces to include
* nic.states: Comma-separated list of states to show (prefix with "^" to invert - i.e. ^down -> show all devices that are not in state down)
* nic.format: Format string (defaults to "{intf} {state} {ip} {ssid}")
"""
import netifaces
import subprocess
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = []
super(Module, self).__init__(engine, config, widgets)
self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth,br").split(",")))
self._include = self.parameter("include", "").split(",")
self._states = {}
self._states["include"] = []
self._states["exclude"] = []
for state in tuple(filter(len, self.parameter("states", "").split(","))):
if state[0] == "^":
self._states["exclude"].append(state[1:])
else:
self._states["include"].append(state)
self._format = self.parameter("format","{intf} {state} {ip} {ssid}");
self._update_widgets(widgets)
self.iwgetid = bumblebee.util.which("iwgetid")
def update(self, widgets):
self._update_widgets(widgets)
def state(self, widget):
states = []
if widget.get("state") == "down":
states.append("critical")
elif widget.get("state") != "up":
states.append("warning")
intf = widget.get("intf")
iftype = "wireless" if self._iswlan(intf) else "wired"
iftype = "tunnel" if self._istunnel(intf) else iftype
states.append("{}-{}".format(iftype, widget.get("state")))
return states
def _iswlan(self, intf):
# wifi, wlan, wlp, seems to work for me
if intf.startswith("w"): return True
return False
def _istunnel(self, intf):
return intf.startswith("tun") or intf.startswith("wg")
def get_addresses(self, intf):
retval = []
try:
for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []):
if ip.get("addr", "") != "":
retval.append(ip.get("addr"))
except Exception:
return []
return retval
def _update_widgets(self, widgets):
interfaces = [i for i in netifaces.interfaces() if not i.startswith(self._exclude)]
interfaces.extend([i for i in netifaces.interfaces() if i in self._include])
for widget in widgets:
widget.set("visited", False)
for intf in interfaces:
addr = []
state = "down"
for ip in self.get_addresses(intf):
addr.append(ip)
state = "up"
if len(self._states["exclude"]) > 0 and state in self._states["exclude"]: continue
if len(self._states["include"]) > 0 and state not in self._states["include"]: continue
widget = self.widget(intf)
if not widget:
widget = bumblebee.output.Widget(name=intf)
widgets.append(widget)
# join/split is used to get rid of multiple whitespaces (in case SSID is not available, for instance
widget.full_text(" ".join(self._format.format(ip=", ".join(addr),intf=intf,state=state,ssid=self.get_ssid(intf)).split()))
widget.set("intf", intf)
widget.set("state", state)
widget.set("visited", True)
for widget in widgets:
if widget.get("visited") is False:
widgets.remove(widget)
def get_ssid(self, intf):
if self._iswlan(intf):
try:
return subprocess.check_output([self.iwgetid,"-r",intf]).strip().decode('utf-8')
except:
return ""
return ""
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,51 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the result of a notmuch count query
default : unread emails which path do not contained "Trash" (notmuch count "tag:unread AND NOT path:/.*Trash.*/")
Parameters:
* notmuch_count.query: notmuch count query to show result
Errors:
if the notmuch query failed, the shown value is -1
Dependencies:
notmuch (https://notmuchmail.org/)
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import os
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.output)
)
self._notmuch_count_query = self.parameter("query", "tag:unread AND NOT path:/.*Trash.*/")
self._notmuch_count = self.count_notmuch()
def output(self, widget):
self._notmuch_count = self.count_notmuch()
return str(self._notmuch_count)
def state(self, widgets):
if self._notmuch_count == 0:
return "empty"
return "items"
def count_notmuch(self):
try:
notmuch_count_cmd = "notmuch count " + self._notmuch_count_query
notmuch_count = int(bumblebee.util.execute(notmuch_count_cmd))
return notmuch_count
except Exception:
return -1
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
"""Displays GPU name, temperature and memory usage.
Parameters:
* nvidiagpu.format: Format string (defaults to "{name}: {temp}°C %{usedmem}/{totalmem} MiB")
Available values are: {name} {temp} {mem_used} {mem_total} {fanspeed} {clock_gpu} {clock_mem}
Requires nvidia-smi
"""
import subprocess
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config, bumblebee.output.Widget(full_text=self.utilization))
self._utilization = "not found: 0°C 0/0 MiB"
def utilization(self, widget):
return self._utilization
def hidden(self):
hide = bumblebee.util.asbool(self.parameter("hide", False))
if hide and "not found" in self._utilization.startswith("not found"):
return True
return False
def update(self, widgets):
sp = subprocess.Popen(['nvidia-smi', '-q'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out_str = sp.communicate()
out_list = out_str[0].decode("utf-8").split('\n')
title = ""
usedMem = ""
totalMem = ""
temp = ""
name = "not found"
clockMem = ""
clockGpu = ""
fanspeed = ""
for item in out_list:
try:
key, val = item.split(':')
key, val = key.strip(), val.strip()
if title == "Clocks":
if key == "Graphics":
clockGpu = val.split(" ")[0]
elif key == "Memory":
clockMem = val.split(" ")[0]
if title == "FB Memory Usage":
if key == "Total":
totalMem = val.split(" ")[0]
elif key == "Used":
usedMem = val.split(" ")[0]
elif key == "GPU Current Temp":
temp = val.split(" ")[0]
elif key == "Product Name":
name = val
elif key == "Fan Speed":
fanspeed = val.split(" ")[0]
except:
title = item.strip()
str_format = self.parameter("format", '{name}: {temp}°C {mem_used}/{mem_total} MiB')
self._utilization = str_format.format(
name = name,
temp = temp,
mem_used = usedMem,
mem_total = totalMem,
clock_gpu = clockGpu,
clock_mem = clockMem,
fanspeed = fanspeed,
)

View file

@ -1,79 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays update information per repository for pacman.
Parameters:
* pacman.sum: If you prefere displaying updates with a single digit (defaults to "False")
Requires the following executables:
* fakeroot
* pacman
"""
import os
import threading
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
#list of repositories.
#the last one should always be other
repos = ["core", "extra", "community", "multilib", "testing", "other"]
def get_pacman_info(widget, path):
try:
cmd = "{}/../../bin/pacman-updates".format(path)
if not os.path.exists(cmd):
cmd = "/usr/share/bumblebee-status/bin/pacman-update"
result = bumblebee.util.execute(cmd)
except:
pass
count = len(repos)*[0]
for line in result.splitlines():
if line.startswith(("http", "rsync")):
for i in range(len(repos)-1):
if "/" + repos[i] + "/" in line:
count[i] += 1
break
else:
result[-1] += 1
for i in range(len(repos)):
widget.set(repos[i], count[i])
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.updates)
)
self._count = 0
def updates(self, widget):
if bumblebee.util.asbool(self.parameter("sum")):
return str(sum(map(lambda x: widget.get(x, 0), repos)))
return '/'.join(map(lambda x: str(widget.get(x, 0)), repos))
def update(self, widgets):
path = os.path.dirname(os.path.abspath(__file__))
if self._count == 0:
thread = threading.Thread(target=get_pacman_info, args=(widgets[0], path))
thread.start()
# TODO: improve this waiting mechanism a bit
self._count += 1
self._count = 0 if self._count > 300 else self._count
def state(self, widget):
weightedCount = sum(map(lambda x: (len(repos)-x[0]) * widget.get(x[1], 0), enumerate(repos)))
if weightedCount < 10:
return "good"
return self.threshold_state(weightedCount, 100, 150)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,68 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the pi-hole status (up/down) together with the number of ads that were blocked today
Parameters:
* pihole.address : pi-hole address (e.q: http://192.168.1.3)
* pihole.pwhash : pi-hole webinterface password hash (can be obtained from the /etc/pihole/SetupVars.conf file)
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import requests
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.pihole_status)
)
buttons = {"LEFT_CLICK":bumblebee.input.LEFT_MOUSE}
self._pihole_address = self.parameter("address", "")
self._pihole_pw_hash = self.parameter("pwhash", "")
self._pihole_status = None
self._ads_blocked_today = "-"
self.update_pihole_status()
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.toggle_pihole_status)
def pihole_status(self, widget):
if self._pihole_status is None:
return "pi-hole unknown"
return "pi-hole " + ("up/" + self._ads_blocked_today if self._pihole_status else "down")
def update_pihole_status(self):
try:
data = requests.get(self._pihole_address + "/admin/api.php?summary").json()
self._pihole_status = True if data["status"] == "enabled" else False
self._ads_blocked_today = data["ads_blocked_today"]
except:
self._pihole_status = None
def toggle_pihole_status(self, widget):
if self._pihole_status is not None:
try:
req = None
if self._pihole_status:
req = requests.get(self._pihole_address + "/admin/api.php?disable&auth=" + self._pihole_pw_hash)
else:
req = requests.get(self._pihole_address + "/admin/api.php?enable&auth=" + self._pihole_pw_hash)
if req is not None:
if req.status_code == 200:
status = req.json()["status"]
self._pihole_status = False if status == "disabled" else True
except:
pass
def update(self, widgets):
self.update_pihole_status()
def state(self, widget):
if self._pihole_status is None:
return []
elif self._pihole_status:
return ["enabled"]
return ["disabled", "warning"]

View file

@ -1,84 +0,0 @@
# pylint: disable=C0111,R0903
"""Periodically checks the RTT of a configurable host using ICMP echos
Requires the following executable:
* ping
Parameters:
* ping.interval: Time in seconds between two RTT checks (defaults to 60)
* ping.address : IP address to check
* ping.timeout : Timeout for waiting for a reply (defaults to 5.0)
* ping.probes : Number of probes to send (defaults to 5)
* ping.warning : Threshold for warning state, in seconds (defaults to 1.0)
* ping.critical: Threshold for critical state, in seconds (defaults to 2.0)
"""
import re
import time
import threading
import bumblebee.input
import bumblebee.output
import bumblebee.engine
def get_rtt(module, widget):
try:
widget.set("rtt-unreachable", False)
res = bumblebee.util.execute("ping -n -q -c {} -W {} {}".format(
widget.get("rtt-probes"), widget.get("rtt-timeout"), widget.get("address")
))
for line in res.split("\n"):
if line.startswith("{} packets transmitted".format(widget.get("rtt-probes"))):
m = re.search(r'(\d+)% packet loss', line)
widget.set('packet-loss', m.group(1))
if not line.startswith("rtt"): continue
m = re.search(r'([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)', line)
widget.set("rtt-min", float(m.group(1)))
widget.set("rtt-avg", float(m.group(2)))
widget.set("rtt-max", float(m.group(3)))
widget.set("rtt-unit", m.group(5))
except Exception as e:
widget.set("rtt-unreachable", True)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.rtt)
super(Module, self).__init__(engine, config, widget)
widget.set("address", self.parameter("address", "8.8.8.8"))
widget.set("interval", self.parameter("interval", 60))
widget.set("rtt-probes", self.parameter("probes", 5))
widget.set("rtt-timeout", self.parameter("timeout", 5.0))
widget.set("rtt-avg", 0.0)
widget.set("rtt-unit", "")
widget.set('packet-loss', 0)
self._next_check = 0
def rtt(self, widget):
if widget.get("rtt-unreachable"):
return "{}: unreachable".format(widget.get("address"))
return "{}: {:.1f}{} ({}%)".format(
widget.get("address"),
widget.get("rtt-avg"),
widget.get("rtt-unit"),
widget.get('packet-loss')
)
def state(self, widget):
if widget.get("rtt-unreachable"): return ["critical"]
return self.threshold_state(widget.get("rtt-avg"), 1000.0, 2000.0)
def update(self, widgets):
if int(time.time()) < self._next_check:
return
thread = threading.Thread(target=get_rtt, args=(self, widgets[0],))
thread.start()
self._next_check = int(time.time()) + int(widgets[0].get("interval"))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,103 +0,0 @@
"""
Show progress for cp, mv, dd, ...
Parameters:
* progress.placeholder: Text to display while no process is running (defaults to "n/a")
* progress.barwidth: Width of the progressbar if it is used (defaults to 8)
* progress.format: Format string (defaults to "{bar} {cmd} {arg}")
Available values are: {bar} {pid} {cmd} {arg} {percentage} {quantity} {speed} {time}
* progress.barfilledchar: Character used to draw the filled part of the bar (defaults to "#"), notice that it can be a string
* progress.baremptychar: Character used to draw the empty part of the bar (defaults to "-"), notice that it can be a string
Requires the following executable:
* progress
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import re
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.get_progress_text)
)
def get_progress_text(self, widget):
if self.update_progress_info(widget):
width = self.parameter("barwidth", 8)
count = round((width * widget.get("per")) / 100)
filledchar = self.parameter("barfilledchar", "#")
emptychar = self.parameter("baremptychar", "-")
bar = "[{}{}]".format(
filledchar * count,
emptychar * (width - count)
)
str_format = self.parameter("format", '{bar} {cmd} {arg}')
return str_format.format(
bar = bar,
pid = widget.get("pid"),
cmd = widget.get("cmd"),
arg = widget.get("arg"),
percentage = widget.get("per"),
quantity = widget.get("qty"),
speed = widget.get("spd"),
time = widget.get("tim")
)
else:
return self.parameter("placeholder", 'n/a')
def update_progress_info(self, widget):
"""Update widget's informations about the copy"""
# These regex extracts following groups:
# 1. pid
# 2. command
# 3. arguments
# 4. progress (xx.x formated)
# 5. quantity (.. unit / .. unit formated)
# 6. speed
# 7. time remaining
extract_nospeed = re.compile("\[ *(\d*)\] ([a-zA-Z]*) (.*)\n\t(\d*\.*\d*)% \((.*)\)\n.*")
extract_wtspeed = re.compile('\[ *(\d*)\] ([a-zA-Z]*) (.*)\n\t(\d*\.*\d*)% \((.*)\) (\d*\.\d .*) remaining (\d*:\d*:\d*)\n.*')
try:
raw = bumblebee.util.execute("progress -qW 0.1")
result = extract_wtspeed.match(raw)
if not result:
# Abord speed measures
raw = bumblebee.util.execute("progress -q")
result = extract_nospeed.match(raw)
widget.set("spd", "???.? B/s")
widget.set("tim", "??:??:??")
else:
widget.set("spd", result.group(6))
widget.set("tim", result.group(7))
widget.set("pid", int(result.group(1)))
widget.set("cmd", result.group(2))
widget.set("arg", result.group(3))
widget.set("per", float(result.group(4)))
widget.set("qty", result.group(5))
return True
except Exception:
return False
def state(self, widget):
if self._active():
return "copying"
return "pending"
def _active(self):
"""Checks wether a copy is running or not"""
raw = bumblebee.util.execute("progress -q")
return bool(raw)

View file

@ -1,47 +0,0 @@
"""Displays public IP address
Requires the following python packages:
* requests
Parameters:
* publicip.region: us-central (default), us-east, us-west, uk, de, pl, nl
* publicip.service: web address that returns plaintext ip address (ex. "http://l2.io/ip")
"""
try:
from requests import get
except ImportError:
pass
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.public_ip)
)
self._avail_regions = {"us-east":"http://checkip.amazonaws.com",
"us-central":"http://whatismyip.akamai.com",
"us-west":"http://ipv4bot.whatismyipaddress.com",
"pl":"http://ip.42.pl/raw",
"de":"http://myexternalip.com/raw",
"nl":"http://tnx.nl/ip",
"uk":"http://ident.me"}
self._region = self.parameter("region", "us-central")
self._service = self.parameter("service", "")
self._ip = ""
def public_ip(self, widget):
return self._ip
def update(self, widgets):
try:
if self._service:
self.address = self._service
else:
self.address = self._avail_regions[self._region]
self._ip = get(self.address).text.rstrip()
except Exception:
self._ip = "No Connection"

View file

@ -1,186 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays volume and mute status and controls for PulseAudio devices. Use wheel up and down to change volume, left click mutes, right click opens pavucontrol.
Aliases: pasink (use this to control output instead of input), pasource
Parameters:
* pulseaudio.autostart: If set to "true" (default is "false"), automatically starts the pulseaudio daemon if it is not running
* pulseaudio.percent_change: How much to change volume by when scrolling on the module (default is 2%)
* pulseaudio.limit: Upper limit for setting the volume (default is 0%, which means "no limit")
Note: If the left and right channels have different volumes, the limit might not be reached exactly.
* pulseaudio.showbars: 1 for showing volume bars, requires --markup=pango;
0 for not showing volume bars (default)
Requires the following executable:
* pulseaudio
* pactl
* pavucontrol
"""
import re
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
def autostart_daemon():
try:
bumblebee.util.execute("pulseaudio --check")
except Exception:
try:
bumblebee.util.execute("pulseaudio --start")
except:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.volume)
)
if bumblebee.util.asbool(self.parameter("autostart", False)):
autostart_daemon()
self._change = 2
self._change = int(self.parameter("percent_change", "2%").strip("%"))
if self._change < 0 or self._change > 100:
self._change = 2
self._limit = 0
self._limit = int(self.parameter("limit", "0%").strip("%s"))
if self._limit < 0:
self._limit = 0
self._left = 0
self._right = 0
self._mono = 0
self._mute = False
self._failed = False
self._channel = "sink" if self.name == "pasink" else "source"
self._showbars = bumblebee.util.asbool(self.parameter("showbars", 0))
self._patterns = [
{"expr": "Name:", "callback": (lambda line: False)},
{"expr": "Mute:", "callback": (lambda line: self.mute(False if " no" in line.lower() else True))},
{"expr": "Volume:", "callback": self.getvolume},
]
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd="pavucontrol")
events = [
{"type": "mute", "action": self.toggle, "button": bumblebee.input.LEFT_MOUSE},
{"type": "volume", "action": self.increase_volume, "button": bumblebee.input.WHEEL_UP},
{"type": "volume", "action": self.decrease_volume, "button": bumblebee.input.WHEEL_DOWN},
]
for event in events:
engine.input.register_callback(self, button=event["button"], cmd=event["action"])
def set_volume(self, amount):
bumblebee.util.execute("pactl set-{}-{} @DEFAULT_{}@ {}".format(
self._channel, "volume", self._channel.upper(), amount))
def increase_volume(self, event):
if self._limit > 0: # we need to check the limit
left = int(self._left)
right = int(self._right)
if left + self._change >= self._limit or right + self._change >= self._limit:
if left == right:
# easy case, just set to limit
self.set_volume("{}%".format(self._limit))
return
else:
# don't adjust anymore, since i don't know how to update only one channel
return
self.set_volume("+{}%".format(self._change))
def decrease_volume(self, event):
self.set_volume("-{}%".format(self._change))
def toggle(self, event):
bumblebee.util.execute("pactl set-{}-{} @DEFAULT_{}@ {}".format(
self._channel, "mute", self._channel.upper(), "toggle"))
def mute(self, value):
self._mute = value
def getvolume(self, line):
if "mono" in line:
m = re.search(r'mono:.*\s*\/\s*(\d+)%', line)
if m:
self._mono = m.group(1)
else:
m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line)
if m:
self._left = m.group(1)
self._right = m.group(2)
return True
def _default_device(self):
output = bumblebee.util.execute("pactl info")
pattern = "Default {}: ".format("Sink" if self.name == "pasink" else "Source")
for line in output.split("\n"):
if line.startswith(pattern):
return line.replace(pattern, "")
return "n/a"
def volume(self, widget):
if self._failed == True:
return "n/a"
if int(self._mono) > 0:
vol = "{}%".format(self._mono)
if self._showbars:
vol = "{} {}".format(
vol, bumblebee.output.hbar(float(self._mono)))
return vol
elif self._left == self._right:
vol = "{}%".format(self._left)
if self._showbars:
vol = "{} {}".format(
vol, bumblebee.output.hbar(float(self._left)))
return vol
else:
vol = "{}%/{}%".format(self._left, self._right)
if self._showbars:
vol = "{} {}{}".format(
vol,
bumblebee.output.hbar(float(self._left)),
bumblebee.output.hbar(float(self._right)))
return vol
def update(self, widgets):
try:
self._failed = False
channel = "sinks" if self.name == "pasink" else "sources"
device = self._default_device()
result = bumblebee.util.execute("pactl list {}".format(channel))
found = False
for line in result.split("\n"):
if "Name: {}".format(device) in line:
found = True
continue
if found is False:
continue
for pattern in self._patterns:
if not pattern["expr"] in line:
continue
if pattern["callback"](line) is False and found == True:
return
except Exception:
self._failed = True
if bumblebee.util.asbool(self.parameter("autostart", False)):
autostart_daemon()
self.update(widgets)
def state(self, widget):
if self._mute:
return ["warning", "muted"]
if int(self._left) > int(100):
return ["critical", "unmuted"]
return ["unmuted"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,129 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the current color temperature of redshift
Requires the following executable:
* redshift
Parameters:
* redshift.location : location provider, either of "geoclue2" (default), \
"ipinfo" (requires the requests package), or "manual"
* redshift.lat : latitude if location is set to "manual"
* redshift.lon : longitude if location is set to "manual"
"""
import threading
try:
import requests
except ImportError:
pass
import bumblebee.input
import bumblebee.output
import bumblebee.engine
def is_terminated():
for thread in threading.enumerate():
if thread.name == "MainThread" and not thread.is_alive():
return True
return False
def get_redshift_value(widget, location, lat, lon):
while True:
if is_terminated():
return
widget.get("condition").acquire()
while True:
try:
widget.get("condition").wait(1)
except RuntimeError:
continue
break
widget.get("condition").release()
command = ["redshift", "-p", "-l"]
if location == "manual":
command.append(lat + ":" + lon)
else:
command.append("geoclue2")
try:
res = bumblebee.util.execute(" ".join(command))
except Exception:
res = ""
widget.set("temp", "n/a")
widget.set("transition", None)
widget.set("state", "day")
for line in res.split("\n"):
line = line.lower()
if "temperature" in line:
widget.set("temp", line.split(" ")[2])
if "period" in line:
state = line.split(" ")[1]
if "day" in state:
widget.set("state", "day")
elif "night" in state:
widget.set("state", "night")
else:
widget.set("state", "transition")
widget.set("transition", " ".join(line.split(" ")[2:]))
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.text)
super(Module, self).__init__(engine, config, widget)
self._location = self.parameter("location", "geoclue2")
self._lat = self.parameter("lat", None)
self._lon = self.parameter("lon", None)
# Even if location method is set to manual, if we have no lat or lon,
# fall back to the geoclue2 method.
if self._location == "manual" and (self._lat is None
or self._lon is None):
self._location == "geoclue2"
if self._location == "ipinfo":
try:
location_url = "http://ipinfo.io/json"
location = requests.get(location_url).json()
self._lat, self._lon = location["loc"].split(",")
self._lat = str(float(self._lat))
self._lon = str(float(self._lon))
self._location = "manual"
except Exception:
# Fall back to geoclue2.
self._location = "geoclue2"
self._text = ""
self._condition = threading.Condition()
widget.set("condition", self._condition)
self._thread = threading.Thread(target=get_redshift_value,
args=(widget, self._location,
self._lat, self._lon))
self._thread.start()
self._condition.acquire()
self._condition.notify()
self._condition.release()
def text(self, widget):
return "{}".format(self._text)
def update(self, widgets):
widget = widgets[0]
self._condition.acquire()
self._condition.notify()
self._condition.release()
temp = widget.get("temp", "n/a")
self._text = temp
transition = widget.get("transition", None)
if transition:
self._text = "{} {}".format(temp, transition)
def state(self, widget):
return widget.get("state", None)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,128 +0,0 @@
# -*- coding: UTF-8 -*-
# pylint: disable=C0111,R0903
"""Displays sensor temperature
Parameters:
* sensors.path: path to temperature file (default /sys/class/thermal/thermal_zone0/temp).
* sensors.json: if set to "true", interpret sensors.path as JSON "path" in the output
of "sensors -j" (i.e. <key1>/<key2>/.../<value>), for example, path could
be: "coretemp-isa-00000/Core 0/temp1_input" (defaults to "false")
* sensors.match: (fallback) Line to match against output of 'sensors -u' (default: temp1_input)
* sensors.match_pattern: (fallback) Line to match against before temperature is read (no default)
* sensors.match_number: (fallback) which of the matches you want (default -1: last match).
* sensors.show_freq: whether to show CPU frequency. (default: true)
"""
import re
import json
import logging
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
log = logging.getLogger(__name__)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.temperature))
self._temperature = "unknown"
self._mhz = "n/a"
self._match_number = int(self.parameter("match_number", "-1"))
self._match_pattern = self.parameter("match_pattern", None)
self._pattern = re.compile(r"^\s*{}:\s*([\d.]+)$".format(self.parameter("match", "temp1_input")), re.MULTILINE)
self._json = bumblebee.util.asbool(self.parameter("json", "false"))
self._freq = bumblebee.util.asbool(self.parameter("show_freq", "true"))
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd="xsensors")
self.determine_method()
def determine_method(self):
if self.parameter("path") != None and self._json == False:
self.use_sensors = False # use thermal zone
else:
# try to use output of sensors -u
try:
output = bumblebee.util.execute("sensors -u")
self.use_sensors = True
log.debug("Sensors command available")
except FileNotFoundError as e:
log.info("Sensors command not available, using /sys/class/thermal/thermal_zone*/")
self.use_sensors = False
def _get_temp_from_sensors(self):
if self._json == True:
try:
output = json.loads(bumblebee.util.execute("sensors -j"))
for key in self.parameter("path").split("/"):
output = output[key]
return int(float(output))
except Exception as e:
logging.error("unable to read sensors: {}".format(str(e)))
return "unknown"
else:
output = bumblebee.util.execute("sensors -u")
if self._match_pattern:
temp_pattern = self.parameter("match", "temp1_input")
match = re.search(r"{}.+{}:\s*([\d.]+)$".format(self._match_pattern, temp_pattern), output.replace("\n", ""))
if match:
return int(float(match.group(1)))
else:
return "unknown"
match = self._pattern.findall(output)
if match:
return int(float(match[self._match_number]))
return "unknown"
def get_temp(self):
if self.use_sensors:
temperature = self._get_temp_from_sensors()
log.debug("Retrieve temperature from sensors -u")
else:
try:
temperature = open(self.parameter("path", "/sys/class/thermal/thermal_zone0/temp")).read()[:2]
log.debug("retrieved temperature from /sys/class/")
# TODO: Iterate through all thermal zones to determine the correct one and use its value
# https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal
except IOError:
temperature = "unknown"
log.info("Can not determine temperature, please install lm-sensors")
return temperature
def get_mhz(self):
mhz = None
try:
output = open("/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq").read()
mhz = int(float(output)/1000.0)
except:
output = open("/proc/cpuinfo").read()
m = re.search(r"cpu MHz\s+:\s+(\d+)", output)
if m:
mhz = int(m.group(1))
else:
m = re.search(r"BogoMIPS\s+:\s+(\d+)", output)
if m:
return "{} BogoMIPS".format(int(m.group(1)))
if not mhz:
return "n/a"
if mhz < 1000:
return "{} MHz".format(mhz)
else:
return "{:0.01f} GHz".format(float(mhz)/1000.0)
def temperature(self, _):
if self._freq:
return u"{}°c @ {}".format(self._temperature, self._mhz)
else:
return u"{}°c".format(self._temperature)
def update(self, widgets):
self._temperature = self.get_temp()
if self._freq:
self._mhz = self.get_mhz()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,203 +0,0 @@
# -*- coding: UTF-8 -*-
"""Displays sensor temperature and CPU frequency
Parameters:
* sensors2.chip: "sensors -u" compatible filter for chip to display (default to empty - show all chips)
* sensors2.showcpu: Enable or disable CPU frequency display (default: true)
* sensors2.showtemp: Enable or disable temperature display (default: true)
* sensors2.showfan: Enable or disable fan display (default: true)
* sensors2.showother: Enable or display "other" sensor readings (default: false)
* sensors2.showname: Enable or disable show of sensor name (default: false)
* sensors2.chip_include: Comma-separated list of chip to include (defaults to "" will include all by default, example: "coretemp,bat")
* sensors2.chip_exclude:Comma separated list of chip to exclude (defaults to "" will exlude none by default)
* sensors2.field_include: Comma separated list of chip to include (defaults to "" will include all by default, example: "temp,fan")
* sensors2.field_exclude: Comma separated list of chip to exclude (defaults to "" will exclude none by default)
* sensors2.chip_field_exclude: Comma separated list of chip field to exclude (defaults to "" will exclude none by default, example: "coretemp-isa-0000.temp1,coretemp-isa-0000.fan1")
* sensors2.chip_field_include: Comma-separated list of adaper field to include (defaults to "" will include all by default)
"""
import re
import bumblebee.util
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config, None)
self._chip = self.parameter("chip", "")
self._data = {}
self._update()
self.widgets(self._create_widgets())
def update(self, widgets):
self._update()
for widget in widgets:
self._update_widget(widget)
def state(self, widget):
widget_type = widget.get("type", "")
try:
data = self._data[widget.get("adapter")][widget.get("package")][widget.get("field")]
if "crit" in data and float(data["input"]) > float(data["crit"]):
return ["critical", widget_type]
if "max" in data and float(data["input"]) > float(data["max"]):
return ["warning", widget_type]
except:
pass
return [widget_type]
def _create_widgets(self):
widgets = []
show_temp = bumblebee.util.asbool(self.parameter("showtemp", "true"))
show_fan = bumblebee.util.asbool(self.parameter("showfan", "true"))
show_other = bumblebee.util.asbool(self.parameter("showother", "false"))
include_chip = tuple(filter(len, self.parameter("chip_include", "").split(",")))
exclude_chip = tuple(filter(len, self.parameter("chip_exclude", "").split(",")))
include_field = tuple(filter(len, self.parameter("field_include", "").split(",")))
exclude_field = tuple(filter(len, self.parameter("field_exclude", "").split(",")))
include_chip_field = tuple(filter(len, self.parameter("chip_field_include", "").split(",")))
exclude_chip_field = tuple(filter(len, self.parameter("chip_field_exclude", "").split(",")))
if bumblebee.util.asbool(self.parameter("showcpu", "true")):
widget = bumblebee.output.Widget(full_text=self._cpu)
widget.set("type", "cpu")
widgets.append(widget)
for adapter in self._data:
if include_chip or exclude_chip:
if include_chip:
if all([chip not in adapter for chip in include_chip]):
continue
else:
if any([chip in adapter for chip in exclude_chip]):
continue
if include_chip_field:
try:
if all([i.split('.')[0] not in adapter for i in include_chip_field]):
continue
except:
pass
for package in self._data[adapter]:
if bumblebee.util.asbool(self.parameter("showname", "false")):
widget = bumblebee.output.Widget(full_text=package)
widget.set("data", self._data[adapter][package])
widget.set("package", package)
widget.set("field", "")
widget.set("adapter", adapter)
widgets.append(widget)
for field in self._data[adapter][package]:
if include_field or exclude_field:
if include_field:
if all([included not in field for included in include_field]):
continue
else:
if any([excluded in field for excluded in exclude_field]):
continue
try:
if include_chip_field or exclude_chip_field:
if include_chip_field:
if all([i.split('.')[1] not in field for i in include_chip_field if i.split('.')[0] in adapter]):
continue
else:
if any([i.split('.')[1] in field for i in exclude_chip_field if i.split('.')[0] in adapter]):
continue
except:
pass
widget = bumblebee.output.Widget()
widget.set("package", package)
widget.set("field", field)
widget.set("adapter", adapter)
if "temp" in field and show_temp:
# seems to be a temperature
widget.set("type", "temp")
widgets.append(widget)
if "fan" in field and show_fan:
# seems to be a fan
widget.set("type", "fan")
widgets.append(widget)
elif show_other:
# everything else
widget.set("type", "other")
widgets.append(widget)
return widgets
def _update_widget(self, widget):
if widget.get("field", "") == "":
return # nothing to do
data = self._data[widget.get("adapter")][widget.get("package")][widget.get("field")]
if "temp" in widget.get("field"):
widget.full_text(u"{:0.01f}°C".format(data["input"]))
elif "fan" in widget.get("field"):
widget.full_text(u"{:0.0f}RPM".format(data["input"]))
else:
widget.full_text(u"{:0.0f}".format(data["input"]))
def _update(self):
output = bumblebee.util.execute("sensors -u {}".format(self._chip))
self._data = self._parse(output)
def _parse(self, data):
output = {}
package = ""
adapter = None
chip = None
for line in data.split("\n"):
if "Adapter" in line:
# new adapter
line = line.replace("Adapter: ", "")
output[chip + " " + line] = {}
adapter = chip + " " + line
chip = line #default - line before adapter is always the chip
if not adapter: continue
key, value = (line.split(":") + ["", ""])[:2]
if not line.startswith(" "):
# assume this starts a new package
if package in output[adapter] and output[adapter][package] == {}:
del output[adapter][package]
output[adapter][key] = {}
package = key
else:
# feature for this chip
try:
name, variant = (key.strip().split("_", 1) + ["",""])[:2]
if not name in output[adapter][package]:
output[adapter][package][name] = { }
if variant:
output[adapter][package][name][variant] = {}
output[adapter][package][name][variant] = float(value)
except Exception as e:
pass
return output
def _cpu(self, _):
mhz = None
try:
output = open("/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq").read()
mhz = int(float(output)/1000.0)
except:
output = open("/proc/cpuinfo").read()
m = re.search(r"cpu MHz\s+:\s+(\d+)", output)
if m:
mhz = int(m.group(1))
else:
m = re.search(r"BogoMIPS\s+:\s+(\d+)", output)
if m:
return "{} BogoMIPS".format(int(m.group(1)))
if not mhz:
return "n/a"
if mhz < 1000:
return "{} MHz".format(mhz)
else:
return "{:0.01f} GHz".format(float(mhz)/1000.0)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,93 +0,0 @@
# pylint: disable=C0111,R0903,W1401
""" Execute command in shell and print result
Few command examples:
'ping 1.1.1.1 -c 1 | grep -Po "(?<=time=)\d+(\.\d+)? ms"'
'echo "BTC=$(curl -s rate.sx/1BTC | grep -Po \"^\d+\")USD"'
'curl -s https://wttr.in/London?format=%l+%t+%h+%w'
'pip3 freeze | wc -l'
'any_custom_script.sh | grep arguments'
Parameters:
* shell.command: Command to execute
Use single parentheses if evaluating anything inside (sh-style)
For example shell.command='echo $(date +"%H:%M:%S")'
But NOT shell.command="echo $(date +'%H:%M:%S')"
Second one will be evaluated only once at startup
* shell.interval: Update interval in seconds
(defaults to 1s == every bumblebee-status update)
* shell.async: Run update in async mode. Won't run next thread if
previous one didn't finished yet. Useful for long
running scripts to avoid bumblebee-status freezes
(defaults to False)
"""
import os
import subprocess
import threading
import bumblebee.engine
import bumblebee.input
import bumblebee.output
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widget = bumblebee.output.Widget(full_text=self.get_output)
super(Module, self).__init__(engine, config, widget)
if self.parameter('interval'):
self.interval(self.parameter('interval'))
self._command = self.parameter('command')
self._async = bumblebee.util.asbool(self.parameter('async'))
if self._async:
self._output = 'Computing...'
self._current_thread = None
else:
self._output = ''
# LMB and RMB will update output regardless of timer
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd=self.update)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd=self.update)
def get_output(self, _):
return self._output
def update(self, _):
# if requested then run not async version and just execute command in this thread
if not self._async:
self._output = self._get_command_output_or_error(self._command)
return
# if previous thread didn't end yet then don't do anything
if self._current_thread:
return
# spawn new thread to execute command and pass callback method to get output from it
self._current_thread = threading.Thread(target=self._run_command_in_thread,
args=(self._command, self._output_function))
self._current_thread.start()
@staticmethod
def _get_command_output_or_error(command):
try:
command_output = subprocess.check_output(command,
executable=os.environ.get('SHELL'),
shell=True,
stderr=subprocess.STDOUT)
return command_output.decode('utf-8').strip()
except subprocess.CalledProcessError as exception:
exception_output = exception.output.decode('utf-8').replace('\n', '')
return 'Status:{} output:{}'.format(exception.returncode, exception_output)
def _run_command_in_thread(self, command, output_callback):
output_callback(self._get_command_output_or_error(command))
def _output_function(self, text):
self._output = text
# clear this thread data, so next update will spawn a new one
self._current_thread = None
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,70 +0,0 @@
# pylint: disable=C0112,R0903
"""Shows a widget per user-defined shortcut and allows to define the behaviour
when clicking on it.
For more than one shortcut, the commands and labels are strings separated by
a demiliter (; semicolon by default).
For example in order to create two shortcuts labeled A and B with commands
cmdA and cmdB you could do:
./bumblebee-status -m shortcut -p shortcut.cmd="ls;ps" shortcut.label="A;B"
Parameters:
* shortcut.cmds : List of commands to execute
* shortcut.labels: List of widgets' labels (text)
* shortcut.delim : Commands and labels delimiter (; semicolon by default)
"""
import logging
import bumblebee.engine
import bumblebee.output
import bumblebee.input
LINK = "https://github.com/tobi-wan-kenobi/bumblebee-status/wiki"
LABEL = "Click me"
class Module(bumblebee.engine.Module):
""" Shortcut module."""
def __init__(self, engine, config):
widgets = []
self._engine = engine
super(Module, self).__init__(engine, config, widgets)
self._labels = self.parameter("labels", "{}".format(LABEL))
self._cmds = self.parameter("cmds", "firefox {}".format(LINK))
self._delim = self.parameter("delim", ";")
self.update_widgets(widgets)
def update_widgets(self, widgets):
""" Creates a set of widget per user define shortcut."""
cmds = self._cmds.split(self._delim)
labels = self._labels.split(self._delim)
# to be on the safe side create as many widgets as there are data (cmds or labels)
num_shortcuts = min(len(cmds), len(labels))
# report possible problem as a warning
if len(cmds) is not len(labels):
logging.warning("shortcut: the number of commands does not match "\
"the number of provided labels.")
logging.warning("cmds : %s, labels : %s", cmds, labels)
for idx in range(0, num_shortcuts):
cmd = cmds[idx]
label = labels[idx]
widget = bumblebee.output.Widget(full_text=label)
self._engine.input.register_callback(widget, button=bumblebee.input.LEFT_MOUSE, cmd=cmd)
widgets.append(widget)
def update(self, widgets):
if len(widgets) <= 0:
self.update_widgets(widgets)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,119 +0,0 @@
# -*- coding: UTF-8 -*-
# smart function inspired by py-SMART https://github.com/freenas/py-SMART
# under Copyright (C) 2015 Marc Herndon and GPL2
"""Displays HDD smart status of different drives or all drives
Parameters:
* smartstatus.display: how to display (defaults to "combined", other choices: "seperate" or "singles")
* smartstauts.drives: in the case of singles which drives to display, separated comma list value, multiple accepted (defaults to "sda", example:"sda,sdc")
"""
import os
import bumblebee.util
import bumblebee.output
import bumblebee.engine
from shutil import which
from subprocess import Popen, PIPE
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config, None)
self.devices = self.list_devices()
self.display = self.parameter('display', 'combined')
self.drives = self.parameter('drives', 'sda')
self.widgets(self.create_widgets())
def create_widgets(self):
widgets = []
if self.display == 'combined':
widget = bumblebee.output.Widget()
widget.set('device', 'combined')
widget.set('assessment', self.combined())
self.output(widget)
widgets.append(widget)
else:
for device in self.devices:
if self.display == "singles" and device not in self.drives:
continue
widget = bumblebee.output.Widget()
widget.set('device', device)
widget.set('assessment', self.smart(device))
self.output(widget)
widgets.append(widget)
return widgets
def update(self, widgets):
for widget in widgets:
device = widget.get('device')
if device == 'combined':
widget.set('assessment', self.combined())
self.output(widget)
else:
widget.set('assessment', self.smart(device))
self.output(widget)
def output(self, widget):
device = widget.get('device')
assessment = widget.get('assessment')
widget.full_text("{}: {}".format(device, assessment))
def state(self, widget):
states = []
assessment = widget.get('assessment')
if assessment == 'Pre-fail':
states.append('warning')
if assessment == 'Fail':
states.append('critical')
return states
def combined(self):
for device in self.devices:
result = self.smart(device)
if result == 'Fail':
return 'Fail'
if result == 'Pre-fail':
return 'Pre-fail'
return 'OK'
def list_devices(self):
for (root, folders, files) in os.walk('/dev'):
if root == '/dev':
devices = {"".join(filter(lambda i: i.isdigit() == False, file)) for file in files if 'sd' in file}
nvme = {file for file in files if('nvme0n' in file and 'p' not in file)}
devices.update(nvme)
return devices
def smart(self, disk_name):
SMARTCTL_PATH = which('smartctl')
assessment = None
cmd = Popen(
['sudo', SMARTCTL_PATH, '--health', os.path.join('/dev/', disk_name)],
stdout=PIPE,
stderr=PIPE,
)
_stdout, _stderr = [i.decode('utf8') for i in cmd.communicate()]
_stdout = _stdout.split('\n')
line = _stdout[4]
if 'SMART' in line:
if any([i in line for i in ['PASSED', 'OK']]):
assessment = 'OK'
else:
assessment = 'Fail'
if assessment == 'OK':
cmd = Popen(
['sudo', SMARTCTL_PATH, '-A', os.path.join('/dev/', disk_name)],
stdout=PIPE,
stderr=PIPE,
)
_stdout, _stderr = [i.decode('utf8') for i in cmd.communicate()]
_stdout = _stdout.split('\n')
for line in _stdout:
if "Pre-fail" in line:
assessment = "Pre-fail"
return assessment

View file

@ -1,23 +0,0 @@
# pylint: disable=C0111,R0903
"""Draws a widget with configurable text content.
Parameters:
* spacer.text: Widget contents (defaults to empty string)
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.text)
)
self._text = self.parameter("text", "")
def text(self, widget):
return self._text
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,87 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the current song being played
Requires the following library:
* python-dbus
Parameters:
* spotify.format: Format string (defaults to "{artist} - {title}")
Available values are: {album}, {title}, {artist}, {trackNumber}, {playbackStatus}
* spotify.previous: Change binding for previous song (default is left click)
* spotify.next: Change binding for next song (default is right click)
* spotify.pause: Change binding for toggling pause (default is middle click)
Available options for spotify.previous, spotify.next and spotify.pause are:
LEFT_CLICK, RIGHT_CLICK, MIDDLE_CLICK, SCROLL_UP, SCROLL_DOWN
"""
import sys
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from bumblebee.output import scrollable
try:
import dbus
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.spotify)
)
buttons = {"LEFT_CLICK":bumblebee.input.LEFT_MOUSE,
"RIGHT_CLICK":bumblebee.input.RIGHT_MOUSE,
"MIDDLE_CLICK":bumblebee.input.MIDDLE_MOUSE,
"SCROLL_UP":bumblebee.input.WHEEL_UP,
"SCROLL_DOWN":bumblebee.input.WHEEL_DOWN,
}
self._song = ""
self._format = self.parameter("format", "{artist} - {title}")
prev_button = self.parameter("previous", "LEFT_CLICK")
next_button = self.parameter("next", "RIGHT_CLICK")
pause_button = self.parameter("pause", "MIDDLE_CLICK")
cmd = "dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.spotify \
/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player."
engine.input.register_callback(self, button=buttons[prev_button],
cmd=cmd + "Previous")
engine.input.register_callback(self, button=buttons[next_button],
cmd=cmd + "Next")
engine.input.register_callback(self, button=buttons[pause_button],
cmd=cmd + "PlayPause")
@scrollable
def spotify(self, widget):
return self.string_song
def hidden(self):
return self.string_song == ""
def update(self, widgets):
try:
bus = dbus.SessionBus()
spotify = bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
spotify_iface = dbus.Interface(spotify, 'org.freedesktop.DBus.Properties')
props = spotify_iface.Get('org.mpris.MediaPlayer2.Player', 'Metadata')
playback_status = str(spotify_iface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus'))
self._song = self._format.format(album=str(props.get('xesam:album')),
title=str(props.get('xesam:title')),
artist=','.join(props.get('xesam:artist')),
trackNumber=str(props.get('xesam:trackNumber')),
playbackStatus=u"\u25B6" if playback_status=="Playing" else u"\u258D\u258D" if playback_status=="Paused" else "",)
except Exception:
self._song = ""
@property
def string_song(self):
if sys.version_info.major < 3:
return unicode(self._song)
return str(self._song)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,61 +0,0 @@
# -*- coding: UTF-8 -*-
# pylint: disable=C0111,R0903
"""Display a stock quote from worldtradingdata.com
Requires the following python packages:
* requests
Parameters:
* stock.symbols : Comma-separated list of symbols to fetch
* stock.change : Should we fetch change in stock value (defaults to True)
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
import json
import requests
import logging
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.value)
)
self._symbols = self.parameter('symbols', '')
self._change = bumblebee.util.asbool(self.parameter('change', True))
self._value = None
self.interval_factor(60)
self.interval(60)
def value(self, widget):
results = []
if not self._value:
return 'n/a'
data = json.loads(self._value)
for symbol in data['quoteResponse']['result']:
valkey = 'regularMarketChange' if self._change else 'regularMarketPrice'
sym = 'n/a' if not 'symbol' in symbol else symbol['symbol']
currency = 'USD' if not 'currency' in symbol else symbol['currency']
val = 'n/a' if not valkey in symbol else '{:.2f}'.format(symbol[valkey])
results.append('{} {} {}'.format(sym, val, currency))
return u' '.join(results)
def fetch(self):
if self._symbols:
url = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols='
url += self._symbols + '&fields=regularMarketPrice,currency,regularMarketChange'
return requests.get(url).text.strip()
else:
logging.error('unable to retrieve stock exchange rate')
return None
def update(self, widgets):
self._value = self.fetch()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,117 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays sunrise and sunset times
Requires the following python packages:
* requests
* suntime
Parameters:
* cpu.lat : Latitude of your location
* cpu.lon : Longitude of your location
"""
try:
from suntime import Sun, SunTimeException
except ImportError:
pass
try:
import requests
except ImportError:
pass
try:
from dateutil.tz import tzlocal
except ImportError:
pass
import datetime
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(
engine, config,
bumblebee.output.Widget(full_text=self.suntimes)
)
self.interval(3600)
self._lat = self.parameter("lat", None)
self._lon = self.parameter("lon", None)
try:
if not self._lat or not self._lon:
location_url = "http://ipinfo.io/json"
location = requests.get(location_url).json()
self._lat, self._lon = location["loc"].split(",")
self._lat = float(self._lat)
self._lon = float(self._lon)
except Exception:
pass
self.update(None)
def suntimes(self, _):
if self._sunset and self._sunrise:
if self._isup:
return u"\u21A7{} \u21A5{}".format(
self._sunset.strftime('%H:%M'),
self._sunrise.strftime('%H:%M'))
return u"\u21A5{} \u21A7{}".format(self._sunrise.strftime('%H:%M'),
self._sunset.strftime('%H:%M'))
return "?"
def _calculate_times(self):
self._isup = False
try:
if not self._lat or not self._lon:
raise()
sun = Sun(self._lat, self._lon)
except Exception:
self._sunrise = None
self._sunset = None
return
order_matters = True
try:
self._sunrise = sun.get_local_sunrise_time()
except SunTimeException:
self._sunrise = "no sunrise"
order_matters = False
try:
self._sunset = sun.get_local_sunset_time()
except SunTimeException:
self._sunset = "no sunset"
order_matters = False
if not order_matters:
return
now = datetime.datetime.now(tz=tzlocal())
if now > self._sunset:
tomorrow = (now + datetime.timedelta(days=1)).date()
try:
self._sunrise = sun.get_local_sunrise_time(tomorrow)
self._sunset = sun.get_local_sunset_time(tomorrow)
except SunTimeException:
self._sunrise = "no sunrise"
self._sunset = "no sunset"
elif now > self._sunrise:
tomorrow = (now + datetime.timedelta(days=1)).date()
try:
self._sunrise = sun.get_local_sunrise_time(tomorrow)
except SunTimeException:
self._sunrise = "no sunrise"
return
self._isup = True
def update(self, widgets):
if not self._lat or not self._lon:
self._sunrise = None
self._sunset = None
self._calculate_times()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,98 +0,0 @@
# -*- coding: utf-8 -*-
# pylint: disable=C0111,R0903
""" system module
adds the possibility to
* shutdown
* reboot
the system.
Per default a confirmation dialog is shown before the actual action is performed.
Parameters:
* system.confirm: show confirmation dialog before performing any action (default: true)
* system.reboot: specify a reboot command (defaults to 'reboot')
* system.shutdown: specify a shutdown command (defaults to 'shutdown -h now')
* system.logout: specify a logout command (defaults to 'i3exit logout')
* system.switch_user: specify a command for switching the user (defaults to 'i3exit switch_user')
* system.lock: specify a command for locking the screen (defaults to 'i3exit lock')
* system.suspend: specify a command for suspending (defaults to 'i3exit suspend')
* system.hibernate: specify a command for hibernating (defaults to 'i3exit hibernate')
"""
import logging
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.popup_v2
import functools
try:
import Tkinter as tk
import tkMessageBox as tkmessagebox
except ImportError:
# python 3
try:
import tkinter as tk
from tkinter import messagebox as tkmessagebox
except ImportError:
logging.warning("failed to import tkinter - bumblebee popups won't work!")
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.text)
)
self._confirm = True
if self.parameter("confirm", "true") == "false":
self._confirm = False
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.popup)
def update(self, widgets):
pass
def text(self, widget):
return ""
def _on_command(self, header, text, command):
do_it = True
if self._confirm:
root = tk.Tk()
root.withdraw()
root.focus_set()
do_it = tkmessagebox.askyesno(header, text)
root.destroy()
if do_it:
bumblebee.util.execute(command)
def popup(self, widget):
menu = bumblebee.popup_v2.PopupMenu()
reboot_cmd = self.parameter("reboot", "reboot")
shutdown_cmd = self.parameter("shutdown", "shutdown -h now")
logout_cmd = self.parameter("logout", "i3exit logout")
switch_user_cmd = self.parameter("switch_user", "i3exit switch_user")
lock_cmd = self.parameter("lock", "i3exit lock")
suspend_cmd = self.parameter("suspend", "i3exit suspend")
hibernate_cmd = self.parameter("hibernate", "i3exit hibernate")
menu.add_menuitem("shutdown", callback=functools.partial(self._on_command, "Shutdown", "Shutdown?", shutdown_cmd))
menu.add_menuitem("reboot", callback=functools.partial(self._on_command, "Reboot", "Reboot?", reboot_cmd))
menu.add_menuitem("log out", callback=functools.partial(self._on_command, "Log out", "Log out?", "i3exit logout"))
# don't ask for these
menu.add_menuitem("switch user", callback=functools.partial(bumblebee.util.execute, switch_user_cmd))
menu.add_menuitem("lock", callback=functools.partial(bumblebee.util.execute, lock_cmd))
menu.add_menuitem("suspend", callback=functools.partial(bumblebee.util.execute, suspend_cmd))
menu.add_menuitem("hibernate", callback=functools.partial(bumblebee.util.execute, hibernate_cmd))
menu.show(widget)
def state(self, widget):
return []

View file

@ -1,42 +0,0 @@
"""Displays the number of pending tasks in TaskWarrior.
Requires the following library:
* taskw
Parameters:
* taskwarrior.taskrc : path to the taskrc file (defaults to ~/.taskrc)
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
try:
from taskw import TaskWarrior
except:
pass
class Module(bumblebee.engine.Module):
"""TaskWarrior module."""
def __init__(self, engine, config):
"""Initialize taskwarrior module."""
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(
full_text=self.output))
self._pending_tasks_count = "0"
def update(self, widgets):
"""Return a string with the number of pending tasks from TaskWarrior."""
try:
taskrc = self.parameter("taskrc", "~/.taskrc")
w = TaskWarrior(config_filename=taskrc)
pending_tasks = w.filter_tasks({'status': 'pending'})
self._pending_tasks_count = str(len(pending_tasks))
except:
self._pending_tasks_count = 'Error'
def output(self, _):
"""Format the task counter to output in bumblebee."""
return "{}".format(self._pending_tasks_count)

View file

@ -1,14 +0,0 @@
# pylint: disable=C0111,R0903
"""Test module
"""
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text="test")
)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,85 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays focused i3 window title.
Requirements:
* i3ipc
Parameters:
* title.max : Maximum character length for title before truncating. Defaults to 64.
* title.placeholder : Placeholder text to be placed if title was truncated. Defaults to "...".
* title.scroll : Boolean flag for scrolling title. Defaults to False
"""
import threading
try:
import i3ipc
except ImportError:
pass
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from bumblebee.output import scrollable
_no_title = "n/a"
class Module(bumblebee.engine.Module):
"""Window title module."""
def __init__(self, engine, config):
super(Module, self).__init__(
engine,
config
)
# parsing of parameters
self._scroll = bumblebee.util.asbool(self.parameter("scroll", False))
self._max = int(self.parameter("max", 64))
self._placeholder = self.parameter("placeholder", "...")
# set output of the module
self.widgets(bumblebee.output.Widget(full_text=
self._scrolling_focused_title if self._scroll else self._focused_title))
# create a connection with i3ipc
try:
self._i3 = i3ipc.Connection()
# event is called both on focus change and title change
self._i3.on("window", lambda _p_i3, _p_e: self._pollTitle())
# begin listening for events
threading.Thread(target=self._i3.main).start()
except:
pass
# initialize the first title
self._pollTitle()
def _focused_title(self, widget):
return self._title
@scrollable
def _scrolling_focused_title(self, widget):
return self._full_title
def _pollTitle(self):
"""Updating current title."""
try:
self._full_title = self._i3.get_tree().find_focused().name
except:
self._full_title = _no_title
if self._full_title is None:
self._full_title = _no_title
if not self._scroll:
# cut the text if it is too long
if len(self._full_title) > self._max:
self._title = self._full_title[0:self._max - len(self._placeholder)]
self._title = "{}{}".format(self._title, self._placeholder)
else:
self._title = self._full_title
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,49 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the number of todo items from a text file
Requires the following executable:
* xdg-open
Parameters:
* todo.file: File to read TODOs from (defaults to ~/Documents/todo.txt)
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import os.path
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.output)
)
self._doc = os.path.expanduser(self.parameter("file", "~/Documents/todo.txt"))
self._todos = self.count_items()
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd="xdg-open {0}".format(self._doc))
def output(self, widget):
self._todos = self.count_items()
return str(self._todos)
def state(self, widgets):
if self._todos == 0:
return "empty"
return "items"
def count_items(self):
try:
i = -1
with open(self._doc) as f:
for i, l in enumerate(f):
pass
return i+1
except Exception:
return 0

View file

@ -1,39 +0,0 @@
#pylint: disable=C0111,R0903
"""Toggle twmn notifications."""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text="")
)
self._paused = False
# Make sure that twmn is currently not paused
try:
bumblebee.util.execute("killall -SIGUSR2 twmnd")
except:
pass
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.toggle_status
)
def toggle_status(self, event):
self._paused = not self._paused
try:
if self._paused:
bumblebee.util.execute("systemctl --user start twmnd")
else:
bumblebee.util.execute("systemctl --user stop twmnd")
except:
self._paused = not self._paused # toggling failed
def state(self, widget):
if self._paused:
return ["muted"]
return ["unmuted"]

View file

@ -1,30 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the system uptime."""
# Use absolute_import because there's already a datatime module
# in the same directory
from __future__ import absolute_import
import bumblebee.input
import bumblebee.output
import bumblebee.engine
from datetime import timedelta
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.output)
)
self._uptime = ""
def output(self, _):
return "{}".format(self._uptime)
def update(self, widgets):
with open('/proc/uptime', 'r') as f:
uptime_seconds = int(float(f.readline().split()[0]))
self._uptime = timedelta(seconds = uptime_seconds)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,81 +0,0 @@
# pylint: disable=C0111,R0903
"""Copy passwords from a password store into the clipboard (currently supports only "pass")
Many thanks to [@bbernhard](https://github.com/bbernhard) for the idea!
Parameters:
* vault.duration: Duration until password is cleared from clipboard (defaults to 30)
* vault.location: Location of the password store (defaults to ~/.password-store)
* vault.offx: x-axis offset of popup menu (defaults to 0)
* vault.offy: y-axis offset of popup menu (defaults to 0)
"""
# TODO:
# - support multiple backends by abstracting the menu structure into a tree
# - build the menu and the actions based on that abstracted tree
#
import os
import time
import threading
import bumblebee.util
import bumblebee.popup_v2
import bumblebee.input
import bumblebee.output
import bumblebee.engine
def build_menu(parent, current_directory, callback):
with os.scandir(current_directory) as it:
for entry in it:
if entry.name.startswith("."): continue
if entry.is_file():
name = entry.name[:entry.name.rfind(".")]
parent.add_menuitem(name, callback=lambda : callback(os.path.join(current_directory, name)))
else:
submenu = bumblebee.popup_v2.PopupMenu(parent, leave=False)
build_menu(submenu, os.path.join(current_directory, entry.name), callback)
parent.add_cascade(entry.name, submenu)
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.text)
)
self._duration = int(self.parameter("duration", 30))
self._offx = int(self.parameter("offx", 0))
self._offy = int(self.parameter("offy", 0))
self._path = os.path.expanduser(self.parameter("location", "~/.password-store/"))
self._reset()
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.popup)
def popup(self, widget):
menu = bumblebee.popup_v2.PopupMenu(leave=False)
build_menu(menu, self._path, self._callback)
menu.show(widget, offset_x=self._offx, offset_y=self._offy)
def _reset(self):
self._timer = None
self._text = str(self.parameter("text", "<click-for-password>"))
def _callback(self, secret_name):
secret_name = secret_name.replace(self._path, "") # remove common path
if self._timer:
self._timer.cancel()
# bumblebee.util.execute hangs for some reason
os.system("PASSWORD_STORE_CLIP_TIME={} pass -c {} > /dev/null 2>&1".format(self._duration, secret_name))
self._timer = threading.Timer(self._duration, self._reset)
self._timer.start()
self._start = int(time.time())
self._text = secret_name
def text(self, widget):
if self._timer:
return "{} ({}s)".format(self._text, self._duration - (int(time.time()) - self._start))
return self._text
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,102 +0,0 @@
# pylint: disable=C0111,R0903
""" Displays the VPN profile that is currently in use.
Left click opens a popup menu that lists all available VPN profiles and allows to establish
a VPN connection using that profile.
Prerequisites:
* tk python library (usually python-tk or python3-tk, depending on your distribution)
* nmcli needs to be installed and configured properly.
To quickly test, whether nmcli is working correctly, type "nmcli -g NAME,TYPE,DEVICE con" which
lists all the connection profiles that are configured. Make sure that your VPN profile is in that list!
e.g: to import a openvpn profile via nmcli:
sudo nmcli connection import type openvpn file </path/to/your/openvpn/profile.ovpn>
"""
import logging
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.popup_v2
import functools
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.vpn_status)
)
self._connected_vpn_profile = None
self._selected_vpn_profile = None
res = bumblebee.util.execute("nmcli -g NAME,TYPE c")
lines = res.splitlines()
self._vpn_profiles = []
for line in lines:
info = line.split(':')
try:
if self._isvpn(info[1]):
self._vpn_profiles.append(info[0])
except:
pass
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.popup)
def _isvpn(self, connection_type):
return connection_type in ["vpn", "wireguard"]
def update(self, widgets):
try:
res = bumblebee.util.execute("nmcli -g NAME,TYPE,DEVICE con")
lines = res.splitlines()
self._connected_vpn_profile = None
for line in lines:
info = line.split(':')
if self._isvpn(info[1]) and info[2] != "":
self._connected_vpn_profile = info[0]
except Exception as e:
logging.exception("Couldn't get VPN status")
self._connected_vpn_profile = None
def vpn_status(self, widget):
if self._connected_vpn_profile is None:
return "off"
return self._connected_vpn_profile
def _on_vpn_disconnect(self):
try:
bumblebee.util.execute("nmcli c down \"{vpn}\""
.format(vpn=self._connected_vpn_profile))
self._connected_vpn_profile = None
except Exception as e:
logging.exception("Couldn't disconnect VPN connection")
def _on_vpn_connect(self, name):
self._selected_vpn_profile = name
try:
bumblebee.util.execute("nmcli c up \"{vpn}\""
.format(vpn=self._selected_vpn_profile))
self._connected_vpn_profile = name
except Exception as e:
logging.exception("Couldn't establish VPN connection")
self._connected_vpn_profile = None
def popup(self, widget):
menu = bumblebee.popup_v2.PopupMenu()
if self._connected_vpn_profile is not None:
menu.add_menuitem("Disconnect", callback=self._on_vpn_disconnect)
for vpn_profile in self._vpn_profiles:
if self._connected_vpn_profile is not None and self._connected_vpn_profile == vpn_profile:
continue
menu.add_menuitem(vpn_profile, callback=functools.partial(self._on_vpn_connect, vpn_profile))
menu.show(widget)
def state(self, widget):
return []

View file

@ -1,57 +0,0 @@
# pylint: disable=C0111,R0903
"""Displays the status of watson (time-tracking tool)
Requires the following executable:
* watson
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import bumblebee.util
import bumblebee.popup_v2
import logging
import re
import functools
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.text))
self._tracking = False
self._project = ""
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd=self.toggle)
def toggle(self, widget):
self._project = "hit"
if self._tracking:
bumblebee.util.execute("watson stop")
else:
bumblebee.util.execute("watson restart")
self._tracking = not self._tracking
def text(self, widget):
if self._tracking:
return self._project
else:
return "Paused"
def update(self, widgets):
output = bumblebee.util.execute("watson status")
if re.match('No project started', output):
self._tracking = False
return
self._tracking = True
m = re.search(r'Project (.+) started', output)
self._project = m.group(1)
#
def state(self, widget):
return "on" if self._tracking else "off"
# return [widget.get("status", None), widget.get("period", None)]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,138 +0,0 @@
# -*- coding: UTF-8 -*-
# pylint: disable=C0111,R0903
"""Displays the temperature on the current location based on the ip
Requires the following python packages:
* requests
Parameters:
* weather.location: Set location, defaults to 'auto' for getting location from http://ipinfo.io
If set to a comma-separated list, left-click and right-click can be used to rotate the locations.
Locations should be city names or city ids.
* weather.unit: metric (default), kelvin, imperial
* weather.showcity: If set to true, show location information, otherwise hide it (defaults to true)
* weather.showminmax: If set to true, show the minimum and maximum temperature, otherwise hide it (defaults to false)
* weather.apikey: API key from http://api.openweathermap.org
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
import re
try:
import requests
from requests.exceptions import RequestException
except ImportError:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.output)
)
self._temperature = 0
self._apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088")
self._location = self.parameter("location", "auto")
if "," in self._location:
self._location = self._location.split(",")
else:
self._location = [self._location]
self._index = 0
self._showcity = bumblebee.util.asbool(self.parameter("showcity", True))
self._showminmax = bumblebee.util.asbool(self.parameter("showminmax", False))
self._unit = self.parameter("unit", "metric")
self._valid = False
self.interval_factor(60)
self.interval(15)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd=self._next_location)
engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd=self._prev_location)
def _next_location(self, event):
self._index = 0 if self._index >= len(self._location) - 1 else self._index + 1
self.update(self.widgets())
def _prev_location(self, event):
self._index = len(self._location)-1 if self._index <= 0 else self._index - 1
self.update(self.widgets())
def _unit_suffix(self):
if self._unit == "metric":
return "C"
if self._unit == "kelvin":
return "K"
if self._unit == "imperial":
return "F"
return ""
def temperature(self):
return u"{}°{}".format(self._temperature, self._unit_suffix())
def tempmin(self):
return u"{}°{}".format(self._tempmin, self._unit_suffix())
def tempmax(self):
return u"{}°{}".format(self._tempmax, self._unit_suffix())
def city(self):
city = re.sub('[_-]', ' ', self._city)
return u"{} ".format(city)
def output(self, widget):
if not self._valid:
return u"?"
if self._showminmax:
self._showcity=False
return self.city() + self.temperature() + " Hi:" + self.tempmax() + " Lo:" + self.tempmin()
elif self._showcity:
return self.city() + self.temperature()
else:
return self.temperature()
def state(self, widget):
if self._valid:
if "thunderstorm" in self._weather:
return ['thunder']
elif "drizzle" in self._weather:
return ['rain']
elif "rain" in self._weather:
return ['rain']
elif "snow" in self._weather:
return ['snow']
elif "sleet" in self._weather:
return ['sleet']
elif "clear" in self._weather:
return ['clear']
elif "cloud" in self._weather:
return ['clouds']
else:
return []
return []
def update(self, widgets):
try:
weather_url = "http://api.openweathermap.org/data/2.5/weather?appid={}".format(self._apikey)
weather_url = "{}&units={}".format(weather_url, self._unit)
if self._location[self._index] == "auto":
location_url = "http://ipinfo.io/json"
location = requests.get(location_url).json()
coord = location["loc"].split(",")
weather_url = "{url}&lat={lat}&lon={lon}".format(url=weather_url, lat=coord[0], lon=coord[1])
elif self._location[self._index].isdigit():
weather_url = "{url}&id={id}".format(url=weather_url, id=self._location[self._index])
else:
weather_url = "{url}&q={city}".format(url=weather_url, city=self._location[self._index])
weather = requests.get(weather_url).json()
self._city = weather['name']
self._temperature = int(weather['main']['temp'])
self._tempmin = int(weather['main']['temp_min'])
self._tempmax = int(weather['main']['temp_max'])
self._weather = weather['weather'][0]['main'].lower()
self._valid = True
except Exception:
self._valid = False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,17 +0,0 @@
#pylint: disable=C0111,R0903
"""Opens a random xkcd comic in the browser."""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text="xkcd")
)
engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
cmd="xdg-open https://c.xkcd.com/random/comic/"
)

View file

@ -1,118 +0,0 @@
# pylint: disable=C0111,R0903
"""Shows a widget for each connected screen and allows the user to enable/disable screens.
Parameters:
* xrandr.overwrite_i3config: If set to 'true', this module assembles a new i3 config
every time a screen is enabled or disabled by taking the file "~/.config/i3/config.template"
and appending a file "~/.config/i3/config.<screen name>" for every screen.
* xrandr.autoupdate: If set to 'false', does *not* invoke xrandr automatically. Instead, the
module will only refresh when displays are enabled or disabled (defaults to true)
Requires the following python module:
* (optional) i3 - if present, the need for updating the widget list is auto-detected
Requires the following executable:
* xrandr
"""
import os
import re
import sys
import bumblebee.util
import bumblebee.input
import bumblebee.output
import bumblebee.engine
try:
import i3
except:
pass
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
widgets = []
self._engine = engine
super(Module, self).__init__(engine, config, widgets)
self._autoupdate = bumblebee.util.asbool(self.parameter("autoupdate", True))
self._needs_update = True
try:
i3.Subscription(self._output_update, "output")
except:
pass
def _output_update(self, event, data, _):
self._needs_update = True
def update_widgets(self, widgets):
new_widgets = []
if self._autoupdate == False and self._needs_update == False:
return
self._needs_update = False
for line in bumblebee.util.execute("xrandr -q").split("\n"):
if not " connected" in line:
continue
display = line.split(" ", 2)[0]
m = re.search(r'\d+x\d+\+(\d+)\+\d+', line)
widget = self.widget(display)
if not widget:
widget = bumblebee.output.Widget(full_text=display, name=display)
self._engine.input.register_callback(widget, button=1, cmd=self._toggle)
self._engine.input.register_callback(widget, button=3, cmd=self._toggle)
new_widgets.append(widget)
widget.set("state", "on" if m else "off")
widget.set("pos", int(m.group(1)) if m else sys.maxsize)
while len(widgets) > 0:
del widgets[0]
for widget in new_widgets:
widgets.append(widget)
if self._autoupdate == False:
widget = bumblebee.output.Widget(full_text="")
widget.set("state", "refresh")
self._engine.input.register_callback(widget, button=1, cmd=self._refresh)
widgets.append(widget)
def update(self, widgets):
self.update_widgets(widgets)
def state(self, widget):
return widget.get("state", "off")
def _refresh(self, event):
self._needs_update = True
def _toggle(self, event):
self._needs_update = True
path = os.path.dirname(os.path.abspath(__file__))
if bumblebee.util.asbool(self.parameter("overwrite_i3config", False)) == True:
toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
else:
toggle_cmd = "xrandr"
widget = self.widget_by_id(event["instance"])
if widget.get("state") == "on":
bumblebee.util.execute("{} --output {} --off".format(toggle_cmd, widget.name))
else:
first_neighbor = next((widget for widget in self.widgets() if widget.get("state") == "on"), None)
last_neighbor = next((widget for widget in reversed(self.widgets()) if widget.get("state") == "on"), None)
neighbor = first_neighbor if event["button"] == bumblebee.input.LEFT_MOUSE else last_neighbor
if neighbor is None:
bumblebee.util.execute("{} --output {} --auto".format(toggle_cmd, widget.name))
else:
bumblebee.util.execute("{} --output {} --auto --{}-of {}".format(toggle_cmd, widget.name,
"left" if event.get("button") == bumblebee.input.LEFT_MOUSE else "right",
neighbor.name))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,33 +0,0 @@
# pylint: disable=C0111,R0903
"""Shows yubikey information
Requires: https://github.com/Yubico/python-yubico
The output indicates that a YubiKey is not connected or it displays
the corresponding serial number.
"""
import yubico
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.keystate))
self._keystate = "No YubiKey"
def keystate(self, widget):
return self._keystate
def update(self, widget):
try:
self._keystate = "YubiKey: " + str(yubico.find_yubikey(debug=False).serial())
except yubico.yubico_exception.YubicoError:
self._keystate = "No YubiKey"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,527 +0,0 @@
# pylint: disable=R0201
"""Output classes"""
import sys
import json
import uuid
import logging
import xml.etree.ElementTree
import bumblebee.store
import bumblebee.util
MAX_PERCENTS = 100.
CHARS = 8
HBARS = [
u"\u2581",
u"\u2582",
u"\u2583",
u"\u2584",
u"\u2585",
u"\u2586",
u"\u2587",
u"\u2588"]
VBARS = [
u"\u258f",
u"\u258e",
u"\u258d",
u"\u258c",
u"\u258b",
u"\u258a",
u"\u2589",
u"\u2588"]
BRAILLE = {
(0, 0): u" ",
(1, 0): u"\u2840",
(2, 0): u"\u2844",
(3, 0): u"\u2846",
(4, 0): u"\u2847",
(0, 1): u"\u2880",
(0, 2): u"\u28a0",
(0, 3): u"\u28b0",
(0, 4): u"\u28b8",
(1, 1): u"\u28c0",
(2, 1): u"\u28c4",
(3, 1): u"\u28c6",
(4, 1): u"\u28c7",
(1, 2): u"\u28e0",
(2, 2): u"\u28e4",
(3, 2): u"\u28e6",
(4, 2): u"\u28e7",
(1, 3): u"\u28f0",
(2, 3): u"\u28f4",
(3, 3): u"\u28f6",
(4, 3): u"\u28f7",
(1, 4): u"\u28f8",
(2, 4): u"\u28fc",
(3, 4): u"\u28fe",
(4, 4): u"\u28ff"
}
log = logging.getLogger(__name__)
def scrollable(func):
def wrapper(module, widget):
text = func(module, widget)
if not text:
return text
width = widget.get("theme.width", int(module.parameter("width", 30)))
if bumblebee.util.asbool(module.parameter("scrolling.makewide", "true")):
widget.set("theme.minwidth", "A"*width)
if width < 0:
return text
if len(text) <= width:
# do alignment
align = module.parameter("theme.align", "left")
if align == "right":
text = "{:>{}}".format(text, width)
if align == "center":
text = "{:^{}}".format(text, width)
return text
# we need to shorten
try:
bounce = int(module.parameter("scrolling.bounce", 1))
except ValueError:
bounce = 1
try:
scroll_speed = int(module.parameter("scrolling.speed", 1))
except ValueError:
scroll_speed = 1
start = widget.get("scrolling.start", -1)
direction = widget.get("scrolling.direction", "right")
start += scroll_speed if direction == "right" else -(scroll_speed)
if width + start > len(text) + (scroll_speed -1):
if bounce:
widget.set("scrolling.direction", "left")
else:
start = 0
elif start <= 0:
if bounce:
widget.set("scrolling.direction", "right")
else:
start = len(text)
widget.set("scrolling.start", start)
text = text[start:width+start]
return text
return wrapper
class Bar(object):
"""superclass"""
bars = None
def __init__(self, value):
"""
Args:
value (float): value between 0. and 100. meaning percents
"""
self.value = value
class HBar(Bar):
"""horizontal bar (1 char)"""
bars = HBARS
def __init__(self, value):
"""
Args:
value (float): value between 0. and 100. meaning percents
"""
super(HBar, self).__init__(value)
self.step = MAX_PERCENTS / CHARS
def get_char(self):
"""
Decide which char to draw
Return: str
"""
for i in range(CHARS):
left = i * self.step
right = (i + 1) * self.step
if left <= self.value < right:
return self.bars[i]
return self.bars[-1]
def hbar(value):
"""wrapper function"""
return HBar(value).get_char()
class VBar(Bar):
"""vertical bar (can be more than 1 char)"""
bars = VBARS
def __init__(self, value, width=1):
"""
Args:
value (float): value between 0. and 100. meaning percents
width (int): width
"""
super(VBar, self).__init__(value)
self.step = MAX_PERCENTS / (CHARS * width)
self.width = width
def get_chars(self):
"""
Decide which char to draw
Return: str
"""
if self.value == 100:
return self.bars[-1] * self.width
if self.width == 1:
for i in range(CHARS):
left = i * self.step
right = (i + 1) * self.step
if left <= self.value < right:
return self.bars[i]
else:
full_parts = int(self.value // (self.step * CHARS))
remainder = self.value - full_parts * self.step * CHARS
empty_parts = self.width - full_parts
if remainder >= 0:
empty_parts -= 1
part_vbar = VBar(remainder * self.width) # scale to width
chars = self.bars[-1] * full_parts
chars += part_vbar.get_chars()
chars += " " * empty_parts
return chars
def vbar(value, width):
"""wrapper function"""
return VBar(value, width).get_chars()
class BrailleGraph(object):
"""
graph using Braille chars
scaled to passed values
"""
def __init__(self, values):
"""
Args:
values (list): list of values
"""
self.values = values
# length of values list must be even
# because one Braille char displays two values
if len(self.values) % 2 == 1:
self.values.append(0)
self.steps = self.get_steps()
self.parts = [tuple(self.steps[i:i+2])
for i in range(len(self.steps))[::2]]
@staticmethod
def get_height(value, unit):
"""
Compute height of a value relative to unit
Args:
value (number): value
unit (number): unit
"""
if value < unit / 10.:
return 0
elif value <= unit:
return 1
elif value <= unit * 2:
return 2
elif value <= unit * 3:
return 3
else:
return 4
def get_steps(self):
"""
Convert the list of values to a list of steps
Return: list
"""
maxval = max(self.values)
unit = maxval / 4.
if unit == 0:
return [0] * len(self.values)
stepslist = []
for value in self.values:
stepslist.append(self.get_height(value, unit))
return stepslist
def get_chars(self):
"""
Decide which chars to draw
Return: str
"""
chars = []
for part in self.parts:
chars.append(BRAILLE[part])
return "".join(chars)
def bgraph(values):
"""wrapper function"""
return BrailleGraph(values).get_chars()
class Widget(bumblebee.store.Store):
"""Represents a single visible block in the status bar"""
def __init__(self, full_text="", name=""):
super(Widget, self).__init__()
self._full_text = full_text
self.module = None
self._module = None
self._minimized = False
self.name = name
self.id = str(uuid.uuid4())
def get_module(self):
return self._module
def toggle_minimize(self):
self._minimized = not self._minimized
def link_module(self, module):
"""Set the module that spawned this widget
This is done outside the constructor to avoid having to
pass in the module name in every concrete module implementation"""
self.module = module.name
self._module = module
def cls(self):
if not self._module:
return None
return self._module.__module__.replace("bumblebee.modules.", "")
def state(self):
"""Return the widget's state"""
if self._module and hasattr(self._module, "state"):
states = self._module.state(self)
if not isinstance(states, list):
return [states]
return states
return []
def full_text(self, value=None):
"""Set or retrieve the full text to display in the widget"""
if value:
self._full_text = value
else:
if self._minimized:
return u"\u2026"
if callable(self._full_text):
return self._full_text(self)
else:
return self._full_text
class WidgetDrawer(object):
"""
Wrapper for I3BarOutput.draw(),
because that function is getting too big
"""
def __init__(self, theme, config=None):
"""
Keep the same signature as I3BarOutput.__init__()
"""
self._theme = theme
self._config = config
self._widgets = []
self._markup = None
self._full_text = None
self._prefix = None
self._prefix_fg = None
self._prefix_bg = None
self._iconmarkup = None
self._suffix = None
def add_separator(self, widget, separator):
"""Add separator (if theme has one)"""
if separator:
self._widgets.append({
u"full_text": separator,
"separator": False,
"color": self._theme.separator_fg(widget),
"background": self._theme.separator_bg(widget),
"separator_block_width": self._theme.separator_block_width(widget),
})
def add_prefix_iconmarkup(self, widget):
"""add custom Pango markup for prefix"""
element = xml.etree.ElementTree.XML(self._iconmarkup)
# if the custom markup has neither 'foreground' or 'fgcolor'
# attributes, but theme has prefixfg, merge it
if 'foreground' not in element.keys() and 'fgcolor' not in element.keys() and self._prefix_fg is not None:
element.set("foreground", self._prefix_fg)
# if the custom markup has neither 'background' or 'bgcolor'
# attributes, but theme has prefixbg, merge it
if 'background' not in element.keys() and 'bgcolor' not in element.keys() and self._prefix_bg is not None:
element.set("background", self._prefix_bg)
self._prefix = xml.etree.ElementTree.tostring(element).decode("utf-8").format(self._prefix)
def add_prefix_colors(self, widget):
"""add custom theme colors for prefix"""
self._prefix = "<span {} {}>{}</span>".format(
"foreground='{}'".format(self._prefix_fg) if self._prefix_fg else "",
"background='{}'".format(self._prefix_bg) if self._prefix_bg else "",
self._prefix
)
def add_prefix(self, widget, padding):
"""add prefix to full_text"""
self._prefix = self._theme.prefix(widget, padding)
if self._markup == "pango":
# add prefix/suffix colors
self._prefix_fg = self._theme.prefix_fg(widget)
self._prefix_bg = self._theme.prefix_bg(widget)
self._iconmarkup = self._config.iconmarkup()
if self._iconmarkup != "none":
self.add_prefix_iconmarkup(widget)
else:
self.add_prefix_colors(widget)
if self._prefix:
self._full_text = u"{}{}".format(self._prefix, self._full_text)
return self._prefix
def add_suffix_iconmarkup(self, widget):
"""add custom Pango markup for suffix"""
self._suffix = self._iconmarkup.format(self._suffix)
def add_suffix(self, widget, padding):
"""add suffix to full_text"""
self._suffix = self._theme.suffix(widget, padding)
if self._markup == "pango":
if self._iconmarkup != "none":
self.add_suffix_iconmarkup(widget)
if self._suffix:
self._full_text = u"{}{}".format(self._full_text, self._suffix)
return self._suffix
def escape_amp(self):
"""escape & in full_text, because pango requires it"""
if self._markup == "pango":
self._full_text = self._full_text.replace("&", "&amp;")
def draw(self, widget, module=None, engine=None):
"""
Keep the same argument signature as I3BarOutput.draw()
Return: list
list[0] - optional if the theme has a separator
list[1] - JSON text for the widget
"""
if widget.get_module() and widget.get_module().hidden():
return []
if widget.get_module() and widget.get_module().name in self._config.autohide():
if not any(state in widget.state() for state in ["warning", "critical"]):
return []
separator = self._theme.separator(widget)
self.add_separator(widget, separator)
self._markup = "none" if not self._config else self._config.markup()
self._full_text = widget.full_text()
raw = self._full_text
padding = self._theme.padding(widget)
prefix = self.add_prefix(widget, padding)
suffix = self.add_suffix(widget, padding)
width = self._theme.minwidth(widget)
if width:
self._full_text = self._full_text.ljust(len(width) + len(self._prefix) + len(self._suffix))
raw = raw.ljust(len(width))
self.escape_amp()
self._widgets.append({
u"full_text": self._full_text,
"color": self._theme.fg(widget),
"background": self._theme.bg(widget),
"separator_block_width": self._theme.separator_block_width(widget),
"separator": self._theme.default_separators(widget),
"min_width": None,
# "min_width": width + "A"*(len(self._prefix) + len(self._suffix)) if width else None,
"align": self._theme.align(widget),
"instance": widget.id,
"name": module.id,
"markup": self._markup,
"_raw": raw,
"_prefix": prefix,
"_suffix": suffix,
})
return self._widgets
class I3BarOutput(object):
"""Manage output according to the i3bar protocol"""
def __init__(self, theme, config=None):
self._theme = theme
self._widgets = []
self._started = False
self._config = config
def started(self):
return self._started
def start(self):
"""Print start preamble for i3bar protocol"""
self._started = True
sys.stdout.write(json.dumps({"version": 1, "click_events": True}) + "\n[\n")
def stop(self):
"""Finish i3bar protocol"""
sys.stdout.write("]\n")
def draw(self, widget, module=None, engine=None):
"""
Draw a single widget
Note: technically, this method doesn't draw anything. It only adds
blocks of JSON text to self._widgets: one for separator, if the
theme contains a separator and one for the widget itself
"""
widget_drawer = WidgetDrawer(self._theme, self._config)
self._widgets.extend(widget_drawer.draw(widget, module, engine))
def begin(self):
"""Start one output iteration"""
self._widgets = []
self._theme.reset()
def flush(self):
"""Flushes output"""
widgets = self._widgets
if self._config and self._config.reverse():
widgets = list(reversed(widgets))
sys.stdout.write(json.dumps(widgets))
if len(self._config.unused_keys()) > 0:
for key in self._config.unused_keys():
log.warning("unused parameter {} - please check the documentation of the affected module to ensure the parameter exists".format(key))
def end(self):
"""Finalizes output"""
sys.stdout.write(",\n")
sys.stdout.flush()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,80 +0,0 @@
"""Pop-up menus."""
import logging
try:
import Tkinter as tk
except ImportError:
# python 3
try:
import tkinter as tk
except ImportError:
logging.warning("failed to import tkinter - bumblebee popups won't work!")
class PopupMenu:
"""The popup-menu."""
def __init__(self):
"""Initialize."""
# menu widget
self.root = tk.Tk()
self.root.withdraw()
self.menu = tk.Menu(self.root)
# internal state
self._item_count = 0
self._clicked_item = None
self._active = False
# bind event of popup getting closed by clicking outside of its area
self.menu.bind('<Unmap>',
lambda event: self.root.after_idle(
self._dismiss_callback))
def add_menuitem(self, menuitem, callback=None):
"""Add menu items."""
item_count = self._item_count
def click_callback():
# call internal callback with item index
self._item_callback(item_count)
# default to internal callback
if callback is None:
callback = click_callback
self.menu.add_command(label=menuitem,
command=callback)
# track item index
self._item_count += 1
def _item_callback(self, which_item):
"""Menu item click callback."""
logging.debug('popup: item callback: {}'.format(which_item))
self._clicked_item = which_item
self.root.destroy()
self._active = False
def _dismiss_callback(self):
"""Menu dismissed."""
logging.debug('popup: menu dismissed')
if self._active is True:
self._clicked_item = None
self.root.destroy()
def show(self, event):
"""Show popup."""
self._clicked_item = None
self.menu.tk_popup(event['x'], event['y']-50)
self._active = True
self.root.mainloop()
return self._clicked_item
def create_and_show_menu(event, *menuitems):
"""Create a menu object and show."""
menu_obj = PopupMenu()
for menuitem in menuitems:
menu_obj.add_menuitem(*menuitem)
return menu_obj.show(event)

View file

@ -1,56 +0,0 @@
"""Pop-up menus."""
import logging
try:
import Tkinter as tk
except ImportError:
# python 3
try:
import tkinter as tk
except ImportError:
logging.warning("failed to import tkinter - bumblebee popups won't work!")
import functools
class PopupMenu(object):
def __init__(self, parent=None, leave=True):
if not parent:
self._root = tk.Tk()
self._root.withdraw()
self._menu = tk.Menu(self._root, tearoff=0)
self._menu.bind("<FocusOut>", self._on_focus_out)
else:
self._root = parent.root()
self._root.withdraw()
self._menu = tk.Menu(self._root, tearoff=0)
self._menu.bind("<FocusOut>", self._on_focus_out)
if leave:
self._menu.bind("<Leave>", self._on_focus_out)
def root(self):
return self._root
def menu(self):
return self._menu
def _on_focus_out(self, event=None):
self._root.destroy()
def _on_click(self, callback):
self._root.destroy()
callback()
def add_cascade(self, menuitem, submenu):
self._menu.add_cascade(label=menuitem, menu=submenu.menu())
def add_menuitem(self, menuitem, callback):
self._menu.add_command(label=menuitem, command=functools.partial(self._on_click, callback))
def show(self, event, offset_x=0, offset_y=0):
try:
self._menu.tk_popup(event['x'] + offset_x, event['y'] + offset_y)
finally:
self._menu.grab_release()
self._root.mainloop()

Some files were not shown because too many files have changed in this diff Show more