├── .codeclimate.yml ├── .coveragerc ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── Bug.md │ └── Feature.md ├── PULL_REQUEST_TEMPLATE │ ├── Improvement.md │ └── Other.md └── workflows │ ├── aurpublish.yml │ ├── autotest.yml │ ├── codeql-analysis.yml │ └── pythonpublish.yml ├── .gitignore ├── .readthedocs.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── PKGBUILD.template ├── README.md ├── bin ├── load-i3-bars.sh ├── pacman-updates └── toggle-display.sh ├── bumblebee-ctl ├── bumblebee-status ├── bumblebee_status ├── __init__.py ├── _version.py ├── core │ ├── __init__.py │ ├── config.py │ ├── decorators.py │ ├── event.py │ ├── input.py │ ├── module.py │ ├── output.py │ ├── theme.py │ └── widget.py ├── discover.py ├── modules │ ├── __init__.py │ ├── contrib │ │ ├── __init__.py │ │ ├── aerlive.py │ │ ├── amixer.py │ │ ├── apt.py │ │ ├── arandr.py │ │ ├── arch-update.py │ │ ├── arch_update.py │ │ ├── aur-update.py │ │ ├── battery-upower.py │ │ ├── battery.py │ │ ├── battery_upower.py │ │ ├── bluetooth.py │ │ ├── bluetooth2.py │ │ ├── blugon.py │ │ ├── brightness.py │ │ ├── caffeine.py │ │ ├── calendar.py │ │ ├── cmus.py │ │ ├── cpu2.py │ │ ├── cpu3.py │ │ ├── currency.py │ │ ├── datetimetz.py │ │ ├── datetz.py │ │ ├── deadbeef.py │ │ ├── deezer.py │ │ ├── dnf.py │ │ ├── docker_ps.py │ │ ├── dunst.py │ │ ├── dunstctl.py │ │ ├── emerge_status.py │ │ ├── epoch.py │ │ ├── gcalendar.py │ │ ├── getcrypto.py │ │ ├── github.py │ │ ├── gitlab.py │ │ ├── gpmdp.py │ │ ├── hddtemp.py │ │ ├── hostname.py │ │ ├── http_status.py │ │ ├── indicator.py │ │ ├── kernel.py │ │ ├── layout-xkbswitch.py │ │ ├── layout.py │ │ ├── layout_xkbswitch.py │ │ ├── libvirtvms.py │ │ ├── messagereceiver.py │ │ ├── mocp.py │ │ ├── mpd.py │ │ ├── network.py │ │ ├── network_traffic.py │ │ ├── notmuch_count.py │ │ ├── nvidiagpu.py │ │ ├── octoprint.py │ │ ├── oled_offset.py │ │ ├── optman.py │ │ ├── otp.py │ │ ├── pacman.py │ │ ├── pamixer.py │ │ ├── persian_date.py │ │ ├── pihole.py │ │ ├── pipewire.py │ │ ├── playerctl.py │ │ ├── pomodoro.py │ │ ├── portage_status.py │ │ ├── power-profile.py │ │ ├── power_profile.py │ │ ├── prime.py │ │ ├── progress.py │ │ ├── publicip.py │ │ ├── rofication.py │ │ ├── rotation.py │ │ ├── rss.py │ │ ├── scratchpad.py │ │ ├── sensors.py │ │ ├── shell.py │ │ ├── shortcut.py │ │ ├── smartstatus.py │ │ ├── solaar.py │ │ ├── spaceapi.py │ │ ├── spotify.py │ │ ├── stock.py │ │ ├── sun.py │ │ ├── system.py │ │ ├── taskwarrior.py │ │ ├── thunderbird.py │ │ ├── timetz.py │ │ ├── title.py │ │ ├── todo.py │ │ ├── todo_org.py │ │ ├── todoist.py │ │ ├── traffic.py │ │ ├── twmn.py │ │ ├── uhnode.py │ │ ├── uptime.py │ │ ├── usage.py │ │ ├── vpn.py │ │ ├── wakatime.py │ │ ├── watson.py │ │ ├── weather.py │ │ ├── xkcd.py │ │ ├── yubikey.py │ │ └── zpool.py │ └── core │ │ ├── __init__.py │ │ ├── cpu.py │ │ ├── date.py │ │ ├── datetime.py │ │ ├── debug.py │ │ ├── disk.py │ │ ├── error.py │ │ ├── git.py │ │ ├── keys.py │ │ ├── layout-xkb.py │ │ ├── layout.py │ │ ├── layout_xkb.py │ │ ├── load.py │ │ ├── memory.py │ │ ├── nic.py │ │ ├── pasink.py │ │ ├── pasource.py │ │ ├── ping.py │ │ ├── pulseaudio.py │ │ ├── pulsectl.py │ │ ├── pulsein.py │ │ ├── pulseout.py │ │ ├── redshift.py │ │ ├── scroll.py │ │ ├── sensors2.py │ │ ├── spacer.py │ │ ├── speedtest.py │ │ ├── test.py │ │ ├── time.py │ │ ├── upower.py │ │ ├── vault.py │ │ └── xrandr.py └── util │ ├── __init__.py │ ├── algorithm.py │ ├── cli.py │ ├── format.py │ ├── graph.py │ ├── location.py │ ├── popup.py │ ├── rofi.py │ ├── store.py │ └── xresources.py ├── coverage.sh ├── create-pkgbuild.py ├── docs ├── FAQ.rst ├── Makefile ├── api.rst ├── conf.py ├── development │ ├── general.rst │ ├── index.rst │ ├── module.rst │ ├── testing.rst │ └── theme.rst ├── docstring.tmpl ├── favicon.ico ├── features.rst ├── index.rst ├── introduction.rst ├── logo.png ├── make.bat ├── modules.rst ├── other │ └── NOTES.md ├── requirements.txt ├── src │ ├── bumblebee_status.core.rst │ ├── bumblebee_status.rst │ └── bumblebee_status.util.rst └── themes.rst ├── generate-base-tests.py ├── man ├── bumblebee-ctl.1 └── bumblebee-status.1 ├── requirements ├── base.txt └── modules │ ├── aerlive.txt │ ├── battery.txt │ ├── battery_upower_reqs.txt │ ├── calendar.txt │ ├── cpu.txt │ ├── cpu2.txt │ ├── cpu3.txt │ ├── currency.txt │ ├── docker_ps.txt │ ├── getcrypto.txt │ ├── git.txt │ ├── github.txt │ ├── gitlab.txt │ ├── layout_xkb.txt │ ├── libvirtvms.txt │ ├── memory.txt │ ├── network_traffic.txt │ ├── nic.txt │ ├── octoprint.txt │ ├── otp.txt │ ├── pihole.txt │ ├── power-profile.txt │ ├── rss.txt │ ├── scratchpad.txt │ ├── spaceapi.txt │ ├── speedtest.txt │ ├── spotify.txt │ ├── stock.txt │ ├── sun.txt │ ├── system.txt │ ├── taskwarrior.txt │ ├── title.txt │ ├── traffic.txt │ ├── weather.txt │ ├── yubikey.txt │ └── zpool.txt ├── screenshots ├── amixer.png ├── arch-update.png ├── aur-update.png ├── battery.png ├── bluetooth.png ├── brightness.png ├── caffeine.png ├── calendar.png ├── cmus.png ├── cpu.png ├── currency.png ├── date.png ├── datetime.png ├── datetimetz.gif ├── disk.png ├── dnf.png ├── dunst.png ├── dunstctl.png ├── emerge_status.png ├── getcrypto.png ├── git.png ├── github.png ├── gitlab.png ├── http_status.png ├── indicator.png ├── kernel.png ├── layout.png ├── load.png ├── memory.png ├── mpd.png ├── network_traffic.gif ├── nic.png ├── pacman.png ├── pasink.png ├── pasource.png ├── ping.png ├── playerctl.png ├── pulseaudio.png ├── redshift.png ├── scratchpad.png ├── sensors.png ├── sensors2.png ├── shortcut.png ├── spacer.png ├── spotify.png ├── stock.png ├── taskwarrior.png ├── themes │ ├── default.png │ ├── dracula-powerline.png │ ├── gruvbox-light.png │ ├── gruvbox-powerline-light.png │ ├── gruvbox.png │ ├── iceberg-contrast.png │ ├── iceberg-dark-powerline.png │ ├── iceberg-powerline.png │ ├── iceberg-rainbow.png │ ├── iceberg.png │ ├── moonlight-powerline.png │ ├── night-powerline.png │ ├── nord-powerline.png │ ├── onedark-powerline.png │ ├── powerline-greyish.png │ ├── powerline-gruvbox.png │ ├── powerline-solarized.png │ ├── powerline.png │ └── solarized.png ├── thunderbird.png ├── time.png ├── title.png ├── todo.png ├── todoist.png ├── traffic.png ├── uptime.png ├── vault.png ├── wakatime.png ├── weather.png ├── xrandr.png └── zpool.png ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── core │ ├── __init__.py │ ├── test_config.py │ ├── test_decorators.py │ ├── test_event.py │ ├── test_input.py │ ├── test_module.py │ ├── test_output.py │ ├── test_theme.py │ └── test_widget.py ├── modules │ ├── __init__.py │ ├── contrib │ │ ├── __init__.py │ │ ├── test_aerlive.py │ │ ├── test_amixer.py │ │ ├── test_apt.py │ │ ├── test_arandr.py │ │ ├── test_arch-update.py │ │ ├── test_battery-upower.py │ │ ├── test_battery.py │ │ ├── test_bluetooth.py │ │ ├── test_bluetooth2.py │ │ ├── test_blugon.py │ │ ├── test_brightness.py │ │ ├── test_caffeine.py │ │ ├── test_calendar.py │ │ ├── test_cmus.py │ │ ├── test_cpu2.py │ │ ├── test_cpu3.py │ │ ├── test_currency.py │ │ ├── test_datetimetz.py │ │ ├── test_datetz.py │ │ ├── test_deadbeef.py │ │ ├── test_deezer.py │ │ ├── test_dnf.py │ │ ├── test_docker_ps.py │ │ ├── test_dunst.py │ │ ├── test_dunstctl.py │ │ ├── test_getcrypto.py │ │ ├── test_github.py │ │ ├── test_gitlab.py │ │ ├── test_gpmdp.py │ │ ├── test_hddtemp.py │ │ ├── test_hostname.py │ │ ├── test_http_status.py │ │ ├── test_indicator.py │ │ ├── test_kernel.py │ │ ├── test_layout-xkbswitch.py │ │ ├── test_layout.py │ │ ├── test_libvirtvms.py │ │ ├── test_messagereceiver.py │ │ ├── test_mocp.py │ │ ├── test_mpd.py │ │ ├── test_network.py │ │ ├── test_network_traffic.py │ │ ├── test_notmuch_count.py │ │ ├── test_nvidiagpu.py │ │ ├── test_octoprint.py │ │ ├── test_oled_offset.py │ │ ├── test_pacman.py │ │ ├── test_pihole.py │ │ ├── test_playerctl.py │ │ ├── test_pomodoro.py │ │ ├── test_portage_status.py │ │ ├── test_power-profile.py │ │ ├── test_prime.py │ │ ├── test_progress.py │ │ ├── test_publicip.py │ │ ├── test_rotation.py │ │ ├── test_rss.py │ │ ├── test_scratchpad.py │ │ ├── test_sensors.py │ │ ├── test_shell.py │ │ ├── test_shortcut.py │ │ ├── test_smartstatus.py │ │ ├── test_solaar.py │ │ ├── test_spaceapi.py │ │ ├── test_spotify.py │ │ ├── test_stock.py │ │ ├── test_sun.py │ │ ├── test_system.py │ │ ├── test_taskwarrior.py │ │ ├── test_timetz.py │ │ ├── test_title.py │ │ ├── test_todo.py │ │ ├── test_todoist.py │ │ ├── test_traffic.py │ │ ├── test_twmn.py │ │ ├── test_uptime.py │ │ ├── test_vpn.py │ │ ├── test_wakatime.py │ │ ├── test_watson.py │ │ ├── test_weather.py │ │ ├── test_xkcd.py │ │ ├── test_yubikey.py │ │ └── test_zpool.py │ └── core │ │ ├── __init__.py │ │ ├── test_cpu.py │ │ ├── test_date.py │ │ ├── test_datetime.py │ │ ├── test_debug.py │ │ ├── test_disk.py │ │ ├── test_error.py │ │ ├── test_git.py │ │ ├── test_layout-xkb.py │ │ ├── test_load.py │ │ ├── test_memory.py │ │ ├── test_nic.py │ │ ├── test_pasink.py │ │ ├── test_pasource.py │ │ ├── test_ping.py │ │ ├── test_pulseaudio.py │ │ ├── test_redshift.py │ │ ├── test_sensors2.py │ │ ├── test_spacer.py │ │ ├── test_speedtest.py │ │ ├── test_test.py │ │ ├── test_time.py │ │ ├── test_vault.py │ │ └── test_xrandr.py └── util │ ├── test_algorithm.py │ ├── test_cli.py │ ├── test_format.py │ ├── test_location.py │ ├── test_rofi.py │ └── test_store.py ├── themes ├── albiceleste-powerline.json ├── default.json ├── dracula-powerline.json ├── firefox-dark-powerline.json ├── greyish-powerline.json ├── gruvbox-light.json ├── gruvbox-powerline-light.json ├── gruvbox-powerline.json ├── gruvbox.json ├── iceberg-contrast.json ├── iceberg-dark-powerline.json ├── iceberg-powerline.json ├── iceberg-rainbow.json ├── iceberg.json ├── icons │ ├── ascii.json │ ├── awesome-fonts.json │ ├── ionicons.json │ ├── paxy97.json │ └── test.json ├── moonlight-powerline.json ├── night-powerline.json ├── nord-colorful.json ├── nord-powerline.json ├── onedark-powerline.json ├── powerline-pango.json ├── powerline.json ├── rastafari-powerline.json ├── rose-pine.json ├── sac_red.json ├── solarized-dark-awesome.json ├── solarized-powerline.json ├── solarized.json ├── srcery.json ├── test.json ├── test_cycle.json ├── test_invalid.json ├── wal-powerline.json ├── zengarden-powerline-light.json └── zengarden.json ├── util └── Makefile └── versioneer.py /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | plugins: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | python: 8 | fixme: 9 | enabled: true 10 | radon: 11 | enabled: true 12 | config: 13 | python_version: 3 14 | threshold: "D" 15 | exclude_patterns: 16 | - "tests/" 17 | - "versioneer.py" 18 | - "bumblebee_status/_version.py" 19 | - "setup.py" 20 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | versioneer.* 4 | bumblebee_status/_version.* 5 | setup.* 6 | tests/* 7 | pytests/* 8 | *mock* 9 | *funcsigs* 10 | *pbr* 11 | *six* 12 | /usr/lib* 13 | 14 | [report] 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Affected module: 2 | 3 | Description: 4 | 5 | 6 | If you are having problems with fonts, please read: 7 | https://github.com/tobi-wan-kenobi/bumblebee-status/issues/228 8 | https://github.com/tobi-wan-kenobi/bumblebee-status/issues/210 9 | https://github.com/tobi-wan-kenobi/bumblebee-status/issues/197 10 | https://github.com/tobi-wan-kenobi/bumblebee-status/issues/233 11 | 12 | Please note FontAwesome 5 is currently not supported: 13 | https://github.com/tobi-wan-kenobi/bumblebee-status/issues/239 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Something doesn't work as expected 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Bug Report 11 | 12 | #### Description 13 | Affected module: 14 | Version used: 15 | 16 | 17 | 18 | #### How to reproduce 19 | 20 | 21 | 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: You have a neat idea that should be implemented? 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Feature Request 11 | 12 | 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/Improvement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Improvement 3 | about: You have some improvement to make bumblebee-status bar better? 4 | --- 5 | 6 | ### Improvement 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/Other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: You have some other ideas you want to introduce? 4 | --- 5 | 6 | ### Description 7 | 8 | 9 | 10 | **What kind of change does this PR introduce?** 11 | 12 | 13 | **Summary** 14 | 15 | 16 | 17 | **Does this PR introduce a breaking change?** 18 | 19 | 20 | **Other information** 21 | -------------------------------------------------------------------------------- /.github/workflows/aurpublish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Upload AUR Package 3 | 4 | on: 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | aur-publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Install dependencies 14 | run: | 15 | python -m pip install --upgrade pip 16 | pip install requests 17 | - name: Create PKGBUILD 18 | run: | 19 | python ./create-pkgbuild.py > ./PKGBUILD 20 | - name: Publish AUR package 21 | uses: KSXGitHub/github-actions-deploy-aur@v2.5.0 22 | with: 23 | pkgname: bumblebee-status 24 | pkgbuild: ./PKGBUILD 25 | commit_username: ${{ secrets.AUR_USERNAME }} 26 | commit_email: ${{ secrets.AUR_EMAIL }} 27 | ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} 28 | commit_message: Update AUR package 29 | ssh_keyscan_types: rsa,dsa,ecdsa,ed25519 30 | -------------------------------------------------------------------------------- /.github/workflows/autotest.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | types: [ opened, reopened, edited ] 6 | push: 7 | 8 | env: 9 | CC_TEST_REPORTER_ID: 40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: ['3.8', '3.9', '3.10', '3.11'] 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | cache: 'pip' 25 | - name: Update Ubuntu 26 | run: sudo apt-get update 27 | - name: Install Ubuntu dependencies 28 | run: sudo apt-get install -y libdbus-1-dev libgit2-dev libvirt-dev taskwarrior libglib2.0-dev rofi 29 | - name: Install Python dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install -U coverage pytest pytest-mock freezegun dbus-python 33 | pip install 'pygit2<1' 'libvirt-python<6.3' 'feedparser<6' || true 34 | pip install $(cat requirements/modules/*.txt | grep -v power | cut -d ' ' -f 1 | sort -u) 35 | - name: Install Code Climate dependency 36 | run: | 37 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 38 | chmod +x ./cc-test-reporter 39 | ./cc-test-reporter before-build 40 | - name: Run tests 41 | run: | 42 | coverage run --source=. -m pytest tests -v 43 | - name: Report coverage 44 | uses: paambaati/codeclimate-action@v3.2.0 45 | with: 46 | coverageCommand: coverage3 xml 47 | debug: true 48 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Upload Python Package 3 | 4 | on: 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Set up Python 14 | uses: actions/setup-python@v1 15 | with: 16 | python-version: '3.x' 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install setuptools wheel twine 21 | - name: Build and publish 22 | env: 23 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 24 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 25 | run: | 26 | python setup.py sdist bdist_wheel 27 | twine upload dist/* 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | 3 | # Vim swap files 4 | *swp 5 | *~ 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | build/ 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .hypothesis/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # IPython Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # Visual studio project files 98 | .vscode/ 99 | 100 | # mypy cache 101 | .mypy_cache 102 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | python: 4 | install: 5 | - requirements: docs/requirements.txt 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3" 10 | sphinx: 11 | builder: html 12 | configuration: docs/conf.py 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Most importantly: Many thanks for considering contributing to bumblebee-status! I am continuously amazed by the quality and creativity of the Pull Requests I get - many thanks! 2 | 3 | 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 :-) 4 | 5 | ### Filing a bug 6 | If you want to file a bug, simply open an issue and describe your problem. Things that help narrow down the problem are: 7 | - Steps to reproduce 8 | - Relevant section of the i3 configuration 9 | - Debug logs and console output of bumblebee-status 10 | 11 | But even if you can't provide those, any indicator that something is not working as it should is much appreciated! 12 | 13 | ### Adding a new module or theme 14 | 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. 15 | 16 | Thanks for reading until here! :) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 tobi-wan-kenobi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | include bumblebee_status/_version.py 3 | include requirements/* 4 | include requirements/modules/* 5 | include themes/* 6 | include themes/icons/* 7 | -------------------------------------------------------------------------------- /bin/load-i3-bars.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ ! -f ~/.config/i3/config.template ]; then 4 | cp ~/.config/i3/config ~/.config/i3/config.template 5 | else 6 | cp ~/.config/i3/config.template ~/.config/i3/config 7 | fi 8 | 9 | if [ -f ~/.config/i3/config.template.private ]; then 10 | cat ~/.config/i3/config.template.private >> ~/.config/i3/config 11 | fi 12 | 13 | screens=$(xrandr -q|grep ' connected'| grep -P '\d+x\d+' |cut -d' ' -f1) 14 | 15 | echo "screens: $screens" 16 | 17 | while read -r line; do 18 | screen=$(echo $line | cut -d' ' -f1) 19 | others=$(echo $screens|tr ' ' '\n'|grep -v $screen|tr '\n' '-'|sed 's/.$//') 20 | 21 | if [ -f ~/.config/i3/config.$screen-$others ]; then 22 | cat ~/.config/i3/config.$screen-$others >> ~/.config/i3/config 23 | else 24 | if [ -f ~/.config/i3/config.$screen ]; then 25 | cat ~/.config/i3/config.$screen >> ~/.config/i3/config 26 | fi 27 | fi 28 | done <<< "$screens" 29 | 30 | i3-msg restart 31 | -------------------------------------------------------------------------------- /bin/pacman-updates: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | if ! type -P fakeroot >/dev/null; then 4 | error 'Cannot find the fakeroot binary.' 5 | exit 1 6 | fi 7 | 8 | if [[ -z $CHECKUPDATES_DB ]]; then 9 | CHECKUPDATES_DB="${TMPDIR:-/tmp}/checkup-db-${USER}/" 10 | fi 11 | 12 | trap 'rm -f $CHECKUPDATES_DB/db.lck' INT TERM EXIT 13 | 14 | DBPath="${DBPath:-/var/lib/pacman/}" 15 | eval $(awk -F' *= *' '$1 ~ /DBPath/ { print $1 "=" $2 }' /etc/pacman.conf) 16 | 17 | mkdir -p "$CHECKUPDATES_DB" 18 | ln -s "${DBPath}/local" "$CHECKUPDATES_DB" &> /dev/null 19 | fakeroot -- pacman -Sy --dbpath "$CHECKUPDATES_DB" --logfile /dev/null &> /dev/null 20 | fakeroot pacman -Su -p --dbpath "$CHECKUPDATES_DB" 21 | 22 | exit 0 23 | -------------------------------------------------------------------------------- /bin/toggle-display.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo $(dirname $(readlink -f "$0")) 4 | 5 | i3bar_update=$(dirname $(readlink -f "$0"))/load-i3-bars.sh 6 | 7 | xrandr "$@" 8 | 9 | if [ -f $i3bar_update ]; then 10 | sleep 1 11 | if [ -f ~/.config/i3/images/background.png ]; then 12 | feh --bg-fill ~/.config/i3/images/background.png 13 | fi 14 | $i3bar_update 15 | fi 16 | -------------------------------------------------------------------------------- /bumblebee-ctl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import argparse 5 | import json 6 | import glob 7 | import socket 8 | 9 | button = { 10 | "left-mouse": 1, 11 | "middle-mouse": 2, 12 | "right-mouse": 3, 13 | "wheel-up": 4, 14 | "wheel-down": 5, 15 | "update": -1, 16 | } 17 | 18 | 19 | def main(): 20 | parser = argparse.ArgumentParser(description="send commands to bumblebee-status") 21 | parser.add_argument( 22 | "-b", 23 | "--button", 24 | choices=["left-mouse", "right-mouse", "middle-mouse", "wheel-up", "wheel-down", "update"], 25 | help="button to emulate", 26 | default="left-mouse", 27 | ) 28 | parser.add_argument("-i", "--id", help="ID of widget to trigger") 29 | parser.add_argument( 30 | "-m", "--module", help="name of the module to trigger", required=True 31 | ) 32 | 33 | args = parser.parse_args() 34 | 35 | for f in glob.glob("/tmp/.bumblebee-status.*"): 36 | s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 37 | try: 38 | s.connect(f) 39 | s.sendall( 40 | json.dumps( 41 | { 42 | "name": args.module, 43 | "instance": args.id, 44 | "button": button[args.button], 45 | } 46 | ).encode("ascii") 47 | ) 48 | except Exception as e: 49 | os.remove(f) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | 55 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 56 | -------------------------------------------------------------------------------- /bumblebee_status/__init__.py: -------------------------------------------------------------------------------- 1 | import bumblebee_status.discover 2 | 3 | bumblebee_status.discover.discover() 4 | -------------------------------------------------------------------------------- /bumblebee_status/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/bumblebee_status/core/__init__.py -------------------------------------------------------------------------------- /bumblebee_status/core/event.py: -------------------------------------------------------------------------------- 1 | __callbacks = {} 2 | 3 | 4 | def register(event, callback, *args, **kwargs): 5 | cb = callback 6 | if args or kwargs: 7 | cb = lambda: callback(*args, **kwargs) 8 | 9 | __callbacks.setdefault(event, []).append(cb) 10 | 11 | def register_exclusive(event, callback, *args, **kwargs): 12 | cb = callback 13 | if args or kwargs: 14 | cb = lambda: callback(*args, **kwargs) 15 | 16 | __callbacks[event] = [cb] 17 | 18 | def unregister(event): 19 | if event in __callbacks: 20 | del __callbacks[event] 21 | 22 | def clear(): 23 | __callbacks.clear() 24 | 25 | 26 | def trigger(event, *args, **kwargs): 27 | cb = __callbacks.get(event, []) 28 | if len(cb) == 0: 29 | return False 30 | 31 | for callback in cb: 32 | if args or kwargs: 33 | callback(*args, **kwargs) 34 | else: 35 | callback() 36 | return True 37 | 38 | 39 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 40 | -------------------------------------------------------------------------------- /bumblebee_status/discover.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | def discover(): 6 | libdir = os.path.abspath( 7 | os.path.join(os.path.dirname(__file__), "..", "bumblebee_status") 8 | ) 9 | sys.path.append(libdir) 10 | 11 | 12 | def utility(name): 13 | current_path = os.path.dirname(os.path.abspath(__file__)) 14 | 15 | for path in [ 16 | os.path.join(current_path, "..", "bin"), 17 | os.path.join( 18 | current_path, "..", "..", "..", "..", "share", "bumblebee-status", "utility" 19 | ), 20 | "/usr/share/bumblebee-status/bin/", 21 | ]: 22 | if os.path.exists(os.path.abspath(os.path.join(path, name))): 23 | return os.path.abspath(os.path.join(path, name)) 24 | raise Exception("{} not found".format(name)) 25 | 26 | 27 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 28 | -------------------------------------------------------------------------------- /bumblebee_status/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/bumblebee_status/modules/__init__.py -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/bumblebee_status/modules/contrib/__init__.py -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/arch-update.py: -------------------------------------------------------------------------------- 1 | """Check updates to Arch Linux. 2 | 3 | Requires the following executable: 4 | * checkupdates (from pacman-contrib) 5 | 6 | contributed by `lucassouto `_ - many thanks! 7 | """ 8 | 9 | import logging 10 | from time import sleep 11 | 12 | import core.module 13 | import core.widget 14 | import core.decorators 15 | 16 | import util.cli 17 | 18 | 19 | class Module(core.module.Module): 20 | @core.decorators.every(minutes=60) 21 | def __init__(self, config, theme): 22 | super().__init__(config, theme, core.widget.Widget(self.utilization)) 23 | self.background = True 24 | self.__packages = 0 25 | self.__error = False 26 | 27 | @property 28 | def __format(self): 29 | return self.parameter("format", "Update Arch: {}") 30 | 31 | def utilization(self, widget): 32 | return self.__format.format(self.__packages) 33 | 34 | def hidden(self): 35 | return self.__packages == 0 and not self.__error 36 | 37 | def update(self): 38 | self.__error = False 39 | sleep(1) 40 | code, result = util.cli.execute( 41 | "checkupdates", ignore_errors=True, return_exitcode=True 42 | ) 43 | 44 | if code == 0: 45 | self.__packages = len(result.strip().split("\n")) 46 | elif code == 2: 47 | self.__packages = 0 48 | else: 49 | self.__error = True 50 | logging.error("checkupdates exited with {}: {}".format(code, result)) 51 | 52 | def state(self, widget): 53 | if self.__error: 54 | return "warning" 55 | return self.threshold_state(self.__packages, 1, 100) 56 | 57 | 58 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 59 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/arch_update.py: -------------------------------------------------------------------------------- 1 | arch-update.py -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/aur-update.py: -------------------------------------------------------------------------------- 1 | """Check updates for AUR. 2 | 3 | Requires the following executable: 4 | * yay (https://github.com/Jguer/yay) 5 | 6 | contributed by `ishaanbhimwal `_ - many thanks! 7 | """ 8 | 9 | import logging 10 | 11 | import core.module 12 | import core.widget 13 | import core.decorators 14 | 15 | import util.cli 16 | 17 | 18 | class Module(core.module.Module): 19 | @core.decorators.every(minutes=60) 20 | def __init__(self, config, theme): 21 | super().__init__(config, theme, core.widget.Widget(self.utilization)) 22 | self.background = True 23 | self.__packages = 0 24 | self.__error = False 25 | 26 | @property 27 | def __format(self): 28 | return self.parameter("format", "Update AUR: {}") 29 | 30 | def utilization(self, widget): 31 | return self.__format.format(self.__packages) 32 | 33 | def hidden(self): 34 | return self.__packages == 0 35 | 36 | def update(self): 37 | self.__error = False 38 | code, result = util.cli.execute( 39 | "yay -Qum", ignore_errors=True, return_exitcode=True 40 | ) 41 | 42 | if code == 0: 43 | if result == "": 44 | self.__packages = 0 45 | else: 46 | self.__packages = len(result.strip().split("\n")) 47 | else: 48 | self.__error = True 49 | logging.error("aur-update exited with {}: {}".format(code, result)) 50 | 51 | def state(self, widget): 52 | if self.__error: 53 | return "warning" 54 | return self.threshold_state(self.__packages, 1, 100) 55 | 56 | 57 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 58 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/battery_upower.py: -------------------------------------------------------------------------------- 1 | battery-upower.py -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/datetz.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the current date and time. 4 | 5 | Parameters: 6 | * date.format: strftime()-compatible formatting string 7 | * date.locale: locale to use rather than the system default 8 | """ 9 | 10 | import core.decorators 11 | from .datetimetz import Module 12 | 13 | 14 | class Module(Module): 15 | @core.decorators.every(hours=1) 16 | def __init__(self, config, theme): 17 | super().__init__(config, theme) 18 | 19 | def default_format(self): 20 | return "%x %Z" 21 | 22 | 23 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 24 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/docker_ps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Displays the number of docker containers running 4 | 5 | Requires the following python packages: 6 | * docker 7 | 8 | contributed by `jlopezzarza `_ - many thanks! 9 | """ 10 | 11 | import docker 12 | 13 | from requests.exceptions import ConnectionError 14 | 15 | import core.module 16 | import core.widget 17 | import core.decorators 18 | 19 | 20 | class Module(core.module.Module): 21 | @core.decorators.every(seconds=5) 22 | def __init__(self, config, theme): 23 | super().__init__(config, theme, core.widget.Widget(self.docker_info)) 24 | self.__info = "" 25 | 26 | def state(self, widget): 27 | state = [] 28 | if self.__info == "OK - 0": 29 | state.append("warning") 30 | elif self.__info in ["n/a", "off"]: 31 | state.append("critical") 32 | return state 33 | 34 | def docker_info(self, widget): 35 | try: 36 | cli = docker.DockerClient(base_url="unix://var/run/docker.sock") 37 | cli.ping() 38 | self.__info = "OK - {}".format( 39 | len(cli.containers.list(filters={"status": "running"})) 40 | ) 41 | except ConnectionError: 42 | self.__info = "off" 43 | except Exception: 44 | self.__info = "n/a" 45 | return self.__info 46 | 47 | 48 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 49 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/dunst.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Toggle dunst notifications. 4 | 5 | contributed by `eknoes `_ - many thanks! 6 | """ 7 | 8 | import core.module 9 | import core.widget 10 | import core.input 11 | 12 | import util.cli 13 | 14 | 15 | class Module(core.module.Module): 16 | def __init__(self, config, theme): 17 | super().__init__(config, theme, core.widget.Widget("")) 18 | self._paused = False 19 | # Make sure that dunst is currently not paused 20 | util.cli.execute("killall -s SIGUSR2 dunst", ignore_errors=True) 21 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_status) 22 | 23 | def toggle_status(self, event): 24 | self._paused = not self._paused 25 | 26 | try: 27 | if self._paused: 28 | util.cli.execute("killall -s SIGUSR1 dunst") 29 | else: 30 | util.cli.execute("killall -s SIGUSR2 dunst") 31 | except: 32 | self._paused = not self._paused # toggling failed 33 | 34 | def state(self, widget): 35 | if self._paused: 36 | return ["muted", "warning"] 37 | return ["unmuted"] 38 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/dunstctl.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Toggle dunst notifications using dunstctl. 4 | 5 | When notifications are paused using this module dunst doesn't get killed and 6 | you'll keep getting notifications on the background that will be displayed when 7 | unpausing. This is specially useful if you're using dunst's scripting 8 | (https://wiki.archlinux.org/index.php/Dunst#Scripting), which requires dunst to 9 | be running. Scripts will be executed when dunst gets unpaused. 10 | 11 | Requires: 12 | * dunst v1.5.0+ 13 | 14 | contributed by `cristianmiranda `_ - many thanks! 15 | contributed by `joachimmathes `_ - many thanks! 16 | """ 17 | 18 | import core.module 19 | import core.widget 20 | import core.input 21 | import util.cli 22 | 23 | 24 | class Module(core.module.Module): 25 | def __init__(self, config, theme): 26 | super().__init__(config, theme, core.widget.Widget("")) 27 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_state) 28 | self.__states = {"unknown": ["unknown", "critical"], 29 | "true": ["muted", "warning"], 30 | "false": ["unmuted"]} 31 | if util.format.asbool(self.parameter("disabled", False)): 32 | util.cli.execute("dunstctl set-paused true", ignore_errors=True) 33 | 34 | def toggle_state(self, event): 35 | util.cli.execute("dunstctl set-paused toggle", ignore_errors=True) 36 | 37 | def state(self, widget): 38 | return self.__states[self.__is_dunst_paused()] 39 | 40 | def __is_dunst_paused(self): 41 | result = util.cli.execute("dunstctl is-paused", 42 | return_exitcode=True, 43 | ignore_errors=True) 44 | return result[1].rstrip() if result[0] == 0 else "unknown" 45 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/epoch.py: -------------------------------------------------------------------------------- 1 | """Displays the current epoch timestamp 2 | 3 | * Requires: 4 | * xclip for click to copy to clipboard 5 | 6 | contributed by `theymightbetim ` 7 | """ 8 | 9 | import core.module 10 | import core.widget 11 | import core.input 12 | 13 | import time 14 | 15 | 16 | class Module(core.module.Module): 17 | 18 | def __init__(self, config, theme): 19 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 20 | core.input.register( 21 | self, button=core.input.LEFT_MOUSE, cmd=self.copy_to_clipboard 22 | ) 23 | 24 | def full_text(self, widget): 25 | return int(time.time()) 26 | 27 | def copy_to_clipboard(self, event): 28 | import subprocess 29 | 30 | epoch = str(int(time.time())) 31 | subprocess.Popen(["xclip", "-i"], stdin=subprocess.PIPE).communicate( 32 | epoch.encode("utf-8") 33 | ) 34 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/gpmdp.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays information about the current song in Google Play music player. 4 | 5 | Requires the following executable: 6 | * gpmdp-remote 7 | 8 | contributed by `TheEdgeOfRage `_ - many thanks! 9 | """ 10 | 11 | import core.module 12 | import core.widget 13 | import core.input 14 | 15 | import util.cli 16 | 17 | 18 | class Module(core.module.Module): 19 | def __init__(self, config, theme): 20 | widgets = [ 21 | core.widget.Widget(name="gpmdp.prev"), 22 | core.widget.Widget(name="gpmdp.main", full_text=self.description), 23 | core.widget.Widget(name="gpmdp.next"), 24 | ] 25 | super().__init__(config, theme, widgets) 26 | 27 | core.input.register( 28 | widgets[0], button=core.input.LEFT_MOUSE, cmd="playerctl previous" 29 | ) 30 | core.input.register( 31 | widgets[1], button=core.input.LEFT_MOUSE, cmd="playerctl play-pause" 32 | ) 33 | core.input.register( 34 | widgets[2], button=core.input.LEFT_MOUSE, cmd="playerctl next" 35 | ) 36 | 37 | self.__status = None 38 | self.__tags = None 39 | 40 | def description(self, widget): 41 | return self.__tags if self.__tags else "n/a" 42 | 43 | def update(self): 44 | self.__load_song() 45 | 46 | def state(self, widget): 47 | if widget.name == "gpmdp.prev": 48 | return "prev" 49 | if widget.name == "gpmdp.next": 50 | return "next" 51 | return self.__status 52 | 53 | def __load_song(self): 54 | info = util.cli.execute("gpmdp-remote current", ignore_errors=True) 55 | status = util.cli.execute("gpmdp-remote status", ignore_errors=True) 56 | self.__status = status.split("\n")[0].lower() 57 | self.__tags = info.split("\n")[0] 58 | 59 | 60 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 61 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/hostname.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the system hostname. 4 | 5 | contributed by `varkokonyi `_ - many thanks! 6 | """ 7 | 8 | import platform 9 | 10 | import core.module 11 | import core.widget 12 | import core.decorators 13 | 14 | 15 | class Module(core.module.Module): 16 | @core.decorators.every(minutes=60) 17 | def __init__(self, config, theme): 18 | super().__init__(config, theme, core.widget.Widget(self.output)) 19 | self.__hname = "" 20 | 21 | def output(self, _): 22 | return self.__hname + " " + "\uf233" 23 | 24 | def update(self): 25 | self.__hname = platform.node() 26 | 27 | 28 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 29 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/kernel.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Shows Linux kernel version information 4 | 5 | contributed by `pierre87 `_ - many thanks! 6 | """ 7 | 8 | import platform 9 | 10 | import core.module 11 | import core.widget 12 | import core.decorators 13 | 14 | 15 | class Module(core.module.Module): 16 | @core.decorators.every(minutes=60) 17 | def __init__(self, config, theme): 18 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 19 | 20 | def full_text(self, widgets): 21 | return platform.release() 22 | 23 | 24 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 25 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/layout-xkbswitch.py: -------------------------------------------------------------------------------- 1 | """Displays and changes the current keyboard layout 2 | 3 | Requires the following executable: 4 | * xkb-switch 5 | 6 | contributed by `somospocos `_ - many thanks! 7 | """ 8 | 9 | import core.module 10 | import core.widget 11 | import core.decorators 12 | import core.input 13 | 14 | import util.cli 15 | 16 | 17 | class Module(core.module.Module): 18 | @core.decorators.every(seconds=60) 19 | def __init__(self, config, theme): 20 | super().__init__(config, theme, core.widget.Widget(self.current_layout)) 21 | 22 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.next_keymap) 23 | self.__current_layout = self.__get_current_layout() 24 | 25 | def current_layout(self, _): 26 | return self.__current_layout 27 | 28 | def next_keymap(self, event): 29 | util.cli.execute("xkb-switch -n", ignore_errors=True) 30 | 31 | def __get_current_layout(self): 32 | try: 33 | res = util.cli.execute("xkb-switch") 34 | return res.split("\n")[0] 35 | except RuntimeError: 36 | return ["n/a"] 37 | 38 | def update(self): 39 | self.__current_layout = self.__get_current_layout() 40 | 41 | 42 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 43 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/layout_xkbswitch.py: -------------------------------------------------------------------------------- 1 | layout-xkbswitch.py -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/libvirtvms.py: -------------------------------------------------------------------------------- 1 | """Displays count of running libvirt VMs. 2 | 3 | Required the following python packages: 4 | * libvirt 5 | 6 | contributed by `maxpivo `_ - many thanks! 7 | """ 8 | 9 | import sys 10 | import libvirt 11 | 12 | import core.module 13 | import core.widget 14 | import core.input 15 | import core.decorators 16 | 17 | 18 | class Module(core.module.Module): 19 | @core.decorators.every(seconds=10) 20 | def __init__(self, config, theme): 21 | super().__init__(config, theme, core.widget.Widget(self.status)) 22 | 23 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd="virt-manager") 24 | 25 | def status(self, _): 26 | conn = libvirt.openReadOnly(None) 27 | if conn == None: 28 | return "Failed to open connection to the hypervisor" 29 | return "VMs %s" % (conn.numOfDomains()) 30 | 31 | 32 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 33 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/mocp.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | # -*- coding: utf-8 -*- 3 | 4 | """Displays information about the current song in mocp. Left click toggles play/pause. Right click toggles shuffle. 5 | 6 | Requires the following executable: 7 | * mocp 8 | 9 | Parameters: 10 | * mocp.format: Format string for the song information. Replace string sequences with the actual information: 11 | 12 | * %state State 13 | * %file File 14 | * %title Title, includes track, artist, song title and album 15 | * %artist Artist 16 | * %song SongTitle 17 | * %album Album 18 | * %tt TotalTime 19 | * %tl TimeLeft 20 | * %ts TotalSec 21 | * %ct CurrentTime 22 | * %cs CurrentSec 23 | * %b Bitrate 24 | * %r Sample rate 25 | 26 | contributed by `chrugi `_ - many thanks! 27 | """ 28 | 29 | import core.module 30 | import core.widget 31 | import core.input 32 | 33 | import util.cli 34 | 35 | 36 | class Module(core.module.Module): 37 | def __init__(self, config, theme): 38 | super().__init__(config, theme, core.widget.Widget(self.description)) 39 | 40 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd="mocp -G") 41 | core.input.register(self, button=core.input.RIGHT_MOUSE, cmd="mocp -t shuffle") 42 | self.__format = self.parameter("format", "%state %artist - %song | %ct/%tt") 43 | self.__running = False 44 | 45 | def description(self, widget): 46 | return self.__info if self.__running == True else "Music On Console Player" 47 | 48 | def update(self): 49 | self.__load_song() 50 | 51 | def __load_song(self): 52 | try: 53 | self.__info = util.cli.execute("mocp -Q '{}'".format(self.__format)).strip() 54 | self.__running = True 55 | except RuntimeError: 56 | self.__running = False 57 | 58 | 59 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 60 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/notmuch_count.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the result of a notmuch count query 4 | default : unread emails which path do not contained 'Trash' (notmuch count 'tag:unread AND NOT path:/.*Trash.*/') 5 | 6 | Parameters: 7 | * notmuch_count.query: notmuch count query to show result 8 | 9 | Errors: 10 | if the notmuch query failed, the shown value is -1 11 | 12 | Dependencies: 13 | notmuch (https://notmuchmail.org/) 14 | 15 | contributed by `abdoulayeYATERA `_ - many thanks! 16 | """ 17 | 18 | import os 19 | 20 | import core.module 21 | import core.widget 22 | 23 | import util.cli 24 | 25 | 26 | class Module(core.module.Module): 27 | def __init__(self, config, theme): 28 | super().__init__(config, theme, core.widget.Widget(self.output)) 29 | 30 | self.__notmuch_count_query = self.parameter( 31 | "query", "tag:unread AND NOT path:/.*Trash.*/" 32 | ) 33 | 34 | def output(self, widget): 35 | return self.__notmuch_count 36 | 37 | def state(self, widgets): 38 | if self.__notmuch_count == 0: 39 | return "empty" 40 | return "items" 41 | 42 | def update(self): 43 | try: 44 | self.__notmuch_count = util.cli.execute( 45 | "notmuch count {}".format(self.__notmuch_count_query) 46 | ).strip() 47 | except Exception: 48 | self.__notmuch_count = "n/a" 49 | 50 | 51 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 52 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/oled_offset.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Creates an empty widget that changes width on a timer, 4 | to reduce changes o OLED burn-in from other bumblebee modules. 5 | 6 | You should put this module as the last one, 7 | so all the other modules are moved when this one changes in width 8 | 9 | contributed by `TheEdgeOfRage `_ - many thanks! 10 | """ 11 | 12 | import core.module 13 | import core.widget 14 | import core.decorators 15 | 16 | 17 | class Module(core.module.Module): 18 | @core.decorators.every(minutes=1) 19 | def __init__(self, config, theme): 20 | super().__init__(config, theme, core.widget.Widget(self.content)) 21 | self.__offset = 0 22 | 23 | def content(self, _): 24 | return self.__offset * " " 25 | 26 | def update(self): 27 | self.__offset = (self.__offset + 1) % 3 28 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/optman.py: -------------------------------------------------------------------------------- 1 | """Displays currently active gpu by optimus-manager 2 | Requires the following packages: 3 | 4 | * optimus-manager 5 | 6 | """ 7 | 8 | import core.module 9 | import core.widget 10 | 11 | import util.cli 12 | 13 | class Module(core.module.Module): 14 | def __init__(self, config, theme): 15 | super().__init__(config, theme, core.widget.Widget(self.output)) 16 | self.__gpumode = "" 17 | 18 | def output(self, _): 19 | return "GPU: {}".format(self.__gpumode) 20 | 21 | def update(self): 22 | cmd = "optimus-manager --print-mode" 23 | output = util.cli.execute(cmd).strip() 24 | 25 | if "intel" in output: 26 | self.__gpumode = "Intel" 27 | elif "nvidia" in output: 28 | self.__gpumode = "Nvidia" 29 | elif "amd" in output: 30 | self.__gpumode = "AMD" 31 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/otp.py: -------------------------------------------------------------------------------- 1 | """One Time Pin Generator 2 | 3 | * Requires: 4 | * pyotpi 5 | * keyring 6 | * xclip for click to copy to clipboard 7 | 8 | * Parameters(both required): 9 | * opt.service_name: service name where secret_key is stored in keyring 10 | * opt.user_name: username for keyring where secret_key is store 11 | 12 | contributed by `theymightbetim ` 13 | """ 14 | 15 | import core.module 16 | import core.widget 17 | import core.input 18 | 19 | import pyotp 20 | import keyring 21 | 22 | 23 | class Module(core.module.Module): 24 | 25 | def __init__(self, config, theme): 26 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 27 | self.__username = self.parameter("user_name", "not set") 28 | self.__service_name = self.parameter("service_name", "not set") 29 | self.__key = keyring.get_password(self.__service_name, self.__username) 30 | self.__totp = pyotp.TOTP(self.__key) 31 | core.input.register( 32 | self, button=core.input.LEFT_MOUSE, cmd=self.copy_to_clipboard 33 | ) 34 | 35 | def full_text(self, widget): 36 | if self.__service_name == "not set": 37 | return "ConfigError: otp.service_name not set" 38 | if self.__username == "not set": 39 | return "ConfigError: otp.user_name not set" 40 | return self.__totp.now() 41 | 42 | def copy_to_clipboard(self, event): 43 | import subprocess 44 | 45 | mfa_code = str(self.__totp.now()) 46 | subprocess.Popen(["xclip", "-i"], stdin=subprocess.PIPE).communicate( 47 | mfa_code.encode("utf-8") 48 | ) 49 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/persian_date.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the current date and time in Persian(Jalali) Calendar. 4 | 5 | Requires the following python packages: 6 | * jdatetime 7 | 8 | Parameters: 9 | * datetime.format: strftime()-compatible formatting string. default: "%A %d %B" e.g., "جمعه ۱۳ اسفند" 10 | * datetime.locale: locale to use. default: "fa_IR" 11 | """ 12 | 13 | import jdatetime 14 | 15 | import core.decorators 16 | from modules.core.datetime import Module as dtmodule 17 | 18 | 19 | class Module(dtmodule): 20 | @core.decorators.every(minutes=1) 21 | def __init__(self, config, theme): 22 | super().__init__(config, theme, dtlibrary=jdatetime) 23 | 24 | def default_format(self): 25 | return "%A %d %B" 26 | 27 | def default_locale(self): 28 | return ("fa_IR", "UTF-8") 29 | 30 | 31 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 32 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/power-profile.py: -------------------------------------------------------------------------------- 1 | power_profile.py -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/scratchpad.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays a count of windows on the scratchpad, Left click to launch a rofi window picker for scratchpads 4 | 5 | Requirements: 6 | * i3ipc 7 | * python-rofi 8 | 9 | contributed by `theymightbetim ` 10 | """ 11 | 12 | import threading 13 | 14 | try: 15 | import i3ipc 16 | except ImportError: 17 | pass 18 | 19 | import core.module 20 | import core.widget 21 | import core.input 22 | 23 | from util.rofi import showScratchpads 24 | 25 | 26 | class Module(core.module.Module): 27 | def __init__(self, config, theme): 28 | super().__init__(config, theme, core.widget.Widget(self.__getTitle)) 29 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd=showScratchpads) 30 | 31 | self.__scratchpads = 0 32 | self.__title = f"{self.__scratchpads}" 33 | # create a connection with i3ipc 34 | self.__i3 = i3ipc.Connection() 35 | self.__pollScratchpads() 36 | # event is called both on fFocus change and title change, and on workspace change 37 | for event in ["window::move", "window::urgent"]: 38 | self.__i3.on(event, self.__pollScratchpads) 39 | # begin listening for events 40 | threading.Thread(target=self.__i3.main).start() 41 | 42 | def __getTitle(self, widget): 43 | return self.__title 44 | 45 | def __pollScratchpads(self, *args, **kwargs): 46 | root = self.__i3.get_tree() 47 | scratchpad = root.scratchpad() 48 | if not scratchpad: 49 | return 50 | 51 | leaves = getattr(scratchpad, "floating_nodes", []) 52 | 53 | count = len(leaves) 54 | 55 | if count != self.__scratchpads: 56 | self.__scratchpads = count 57 | self.__title = f"{self.__scratchpads}" 58 | 59 | 60 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 61 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/solaar.py: -------------------------------------------------------------------------------- 1 | """Shows status and load percentage of logitech's unifying device 2 | 3 | Requires the following executable: 4 | * solaar (from community) 5 | 6 | contributed by `cambid `_ - many thanks! 7 | """ 8 | 9 | import logging 10 | 11 | import core.module 12 | import core.widget 13 | import core.decorators 14 | 15 | import util.cli 16 | 17 | 18 | class Module(core.module.Module): 19 | @core.decorators.every(seconds=30) 20 | def __init__(self, config, theme): 21 | super().__init__(config, theme, core.widget.Widget(self.utilization)) 22 | self.__battery = self.parameter("device", "") 23 | self.background = True 24 | self.__battery_status = "" 25 | self.__error = False 26 | if self.__battery != "": 27 | self.__cmd = f"solaar show '{self.__battery}'" 28 | else: 29 | self.__cmd = "solaar show" 30 | 31 | @property 32 | def __format(self): 33 | return self.parameter("format", "{}") 34 | 35 | def utilization(self, widget): 36 | return self.__format.format(self.__battery_status) 37 | 38 | def update(self): 39 | self.__error = False 40 | code, result = util.cli.execute( 41 | self.__cmd, ignore_errors=True, return_exitcode=True 42 | ) 43 | 44 | if code == 0: 45 | for line in result.split('\n'): 46 | if line.count('Battery') > 0: 47 | self.__battery_status = line.split(':')[1].strip() 48 | else: 49 | self.__error = True 50 | logging.error(f"solaar exited with {code}: {result}") 51 | 52 | def state(self, widget): 53 | if self.__error: 54 | return "warning" 55 | return "okay" 56 | 57 | 58 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 59 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/timetz.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the current date and time. 4 | 5 | Parameters: 6 | * time.format: strftime()-compatible formatting string 7 | * time.locale: locale to use rather than the system default 8 | """ 9 | 10 | import core.decorators 11 | from .datetimetz import Module 12 | 13 | 14 | class Module(Module): 15 | @core.decorators.every(seconds=59) # ensures one update per minute 16 | def __init__(self, config, theme): 17 | super().__init__(config, theme) 18 | 19 | def default_format(self): 20 | return "%X %Z" 21 | 22 | 23 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 24 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/todo.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the number of todo items from a text file 4 | 5 | Parameters: 6 | * todo.file: File to read TODOs from (defaults to ~/Documents/todo.txt) 7 | 8 | 9 | contributed by `codingo `_ - many thanks! 10 | """ 11 | 12 | import os.path 13 | 14 | import core.module 15 | import core.widget 16 | import core.input 17 | 18 | 19 | class Module(core.module.Module): 20 | def __init__(self, config, theme): 21 | super().__init__(config, theme, core.widget.Widget(self.output)) 22 | 23 | self.__doc = os.path.expanduser(self.parameter("file", "~/Documents/todo.txt")) 24 | self.__editor = self.parameter("editor", "xdg-open") 25 | self.__todos = self.count_items() 26 | core.input.register( 27 | self, button=core.input.LEFT_MOUSE, cmd="{} {}".format(self.__editor, self.__doc) 28 | ) 29 | 30 | def output(self, widget): 31 | return str(self.__todos) 32 | 33 | def update(self): 34 | self.__todos = self.count_items() 35 | 36 | def state(self, widgets): 37 | if self.__todos == 0: 38 | return "empty" 39 | return "items" 40 | 41 | def count_items(self): 42 | try: 43 | i = 0 44 | with open(self.__doc) as f: 45 | for l in f.readlines(): 46 | if l.strip() != '': 47 | i += 1 48 | return i 49 | except Exception: 50 | return 0 51 | 52 | 53 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 54 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/twmn.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Toggle twmn notifications. 4 | 5 | Requires the following executable: 6 | * systemctl 7 | 8 | contributed by `Pseudonick47 `_ - many thanks! 9 | """ 10 | 11 | import core.module 12 | import core.widget 13 | import core.input 14 | import core.decorators 15 | 16 | import util.cli 17 | 18 | 19 | class Module(core.module.Module): 20 | @core.decorators.every(minutes=60) 21 | def __init__(self, config, theme): 22 | super().__init__(config, theme, core.widget.Widget("")) 23 | 24 | self.__paused = False 25 | # Make sure that twmn is currently not paused 26 | util.cli.execute("killall -SIGUSR2 twmnd", ignore_errors=True) 27 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_status) 28 | 29 | def toggle_status(self, event): 30 | self.__paused = not self.__paused 31 | 32 | try: 33 | if self.__paused: 34 | util.cli.execute("systemctl --user start twmnd") 35 | else: 36 | util.cli.execute("systemctl --user stop twmnd") 37 | except: 38 | self.__paused = not self.__paused # toggling failed 39 | 40 | def state(self, widget): 41 | if self.__paused: 42 | return ["muted"] 43 | return ["unmuted"] 44 | 45 | 46 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 47 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/uhnode.py: -------------------------------------------------------------------------------- 1 | """Shows User and Hostname in linux 2 | 3 | contributed by `Jakepys ` 4 | 5 | - with much love :) 6 | """ 7 | 8 | import core.module 9 | import core.widget 10 | import getpass 11 | import platform 12 | 13 | 14 | class Module(core.module.Module): 15 | def __init__(self, config, theme): 16 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 17 | self.__username = getpass.getuser() 18 | self.__hostname = platform.node() 19 | 20 | def full_text(self, widgets): 21 | return "{}/{}".format(self.__hostname, self.__username) 22 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/uptime.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the system uptime. 4 | 5 | contributed by `ccoors `_ - many thanks! 6 | """ 7 | 8 | from datetime import timedelta 9 | 10 | import core.module 11 | import core.widget 12 | 13 | 14 | class Module(core.module.Module): 15 | def __init__(self, config, theme): 16 | super().__init__(config, theme, core.widget.Widget(self.output)) 17 | self.__uptime = "" 18 | 19 | def output(self, _): 20 | return "{}".format(self.__uptime) 21 | 22 | def update(self): 23 | with open("/proc/uptime", "r") as f: 24 | uptime_seconds = int(float(f.readline().split()[0])) 25 | self.__uptime = timedelta(seconds=uptime_seconds) 26 | 27 | 28 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 29 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/xkcd.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Opens a random xkcd comic in the browser. 4 | 5 | contributed by `whzup `_ - many thanks! 6 | """ 7 | 8 | import core.module 9 | import core.widget 10 | import core.input 11 | import core.decorators 12 | 13 | 14 | class Module(core.module.Module): 15 | @core.decorators.never 16 | def __init__(self, config, theme): 17 | super().__init__(config, theme, core.widget.Widget("xkcd")) 18 | core.input.register( 19 | self, 20 | button=core.input.LEFT_MOUSE, 21 | cmd="xdg-open https://c.xkcd.com/random/comic/", 22 | ) 23 | 24 | 25 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 26 | -------------------------------------------------------------------------------- /bumblebee_status/modules/contrib/yubikey.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Shows yubikey information 4 | 5 | Requires: https://github.com/Yubico/python-yubico 6 | 7 | The output indicates that a YubiKey is not connected or it displays 8 | the corresponding serial number. 9 | 10 | 11 | contributed by `EmmaTinten `_ - many thanks! 12 | """ 13 | 14 | import yubico 15 | 16 | import core.module 17 | import core.widget 18 | import core.decorators 19 | 20 | 21 | class Module(core.module.Module): 22 | @core.decorators.every(seconds=5) 23 | def __init__(self, config, theme): 24 | super().__init__(config, theme, core.widget.Widget(self.keystate)) 25 | self.__keystate = "No YubiKey" 26 | 27 | def keystate(self, widget): 28 | return self.__keystate 29 | 30 | def update(self): 31 | try: 32 | self.__keystate = "YubiKey: " + str( 33 | yubico.find_yubikey(debug=False).serial() 34 | ) 35 | except yubico.yubico_exception.YubicoError: 36 | self.__keystate = "No YubiKey" 37 | except Exception: 38 | self.__keystate = "n/a" 39 | 40 | 41 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 42 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/bumblebee_status/modules/core/__init__.py -------------------------------------------------------------------------------- /bumblebee_status/modules/core/date.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the current date and time. 4 | 5 | Parameters: 6 | * date.format: strftime()-compatible formatting string 7 | * date.locale: locale to use rather than the system default 8 | """ 9 | 10 | import core.decorators 11 | from .datetime import Module 12 | 13 | 14 | class Module(Module): 15 | @core.decorators.every(hours=1) 16 | def __init__(self, config, theme): 17 | super().__init__(config, theme) 18 | 19 | def default_format(self): 20 | return "%x" 21 | 22 | 23 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 24 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/datetime.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the current date and time. 4 | 5 | Parameters: 6 | * datetime.format: strftime()-compatible formatting string 7 | * datetime.locale: locale to use rather than the system default 8 | """ 9 | 10 | from __future__ import absolute_import 11 | import datetime 12 | import locale 13 | 14 | import core.module 15 | import core.widget 16 | import core.input 17 | 18 | 19 | class Module(core.module.Module): 20 | def __init__(self, config, theme, dtlibrary=None): 21 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 22 | 23 | core.input.register(self, button=core.input.LEFT_MOUSE, cmd="calendar") 24 | self.dtlibrary = dtlibrary or datetime 25 | 26 | def set_locale(self): 27 | l = self.default_locale() 28 | if not l or l == (None, None): 29 | l = ("en_US", "UTF-8") 30 | lcl = self.parameter("locale", ".".join(l)) 31 | try: 32 | locale.setlocale(locale.LC_ALL, lcl.split(".")) 33 | except Exception as e: 34 | locale.setlocale(locale.LC_ALL, ("en_US", "UTF-8")) 35 | 36 | def default_format(self): 37 | return "%x %X" 38 | 39 | def default_locale(self): 40 | return locale.getdefaultlocale() 41 | 42 | def full_text(self, widget): 43 | self.set_locale() 44 | enc = locale.getpreferredencoding() 45 | fmt = self.parameter("format", self.default_format()) 46 | retval = self.dtlibrary.datetime.now().strftime(fmt) 47 | if hasattr(retval, "decode"): 48 | return retval.decode(enc) 49 | return retval 50 | 51 | 52 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 53 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/debug.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Shows that debug is enabled""" 4 | 5 | import platform 6 | 7 | import core.module 8 | import core.widget 9 | import core.decorators 10 | 11 | 12 | class Module(core.module.Module): 13 | @core.decorators.every(minutes=60) 14 | def __init__(self, config, theme): 15 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 16 | 17 | def full_text(self, widgets): 18 | return "debug" 19 | 20 | def state(self, widget): 21 | return "warning" 22 | 23 | 24 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 25 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/error.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Shows bumblebee-status errors""" 4 | 5 | import platform 6 | 7 | import core.module 8 | import core.widget 9 | import core.event 10 | 11 | 12 | class Module(core.module.Module): 13 | def __init__(self, config, theme): 14 | super().__init__(config, theme, core.widget.Widget(self.full_text)) 15 | self.__error = "" 16 | self.__state = "critical" 17 | 18 | core.event.register("error", self.__set_error) 19 | 20 | def full_text(self, widgets): 21 | return self.__error 22 | 23 | def __set_error(self, error="n/a", state="critical"): 24 | self.__error = error 25 | self.__state = state 26 | 27 | def state(self, widget): 28 | if self.__error: 29 | return [self.__state] 30 | return [] 31 | 32 | 33 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 34 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/keys.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Shows when a key is pressed 4 | 5 | Parameters: 6 | * keys.keys: Comma-separated list of keys to monitor (defaults to "") 7 | """ 8 | 9 | import core.module 10 | import core.widget 11 | import core.decorators 12 | import core.event 13 | 14 | import util.format 15 | 16 | from pynput.keyboard import Listener 17 | 18 | NAMES = { 19 | "Key.cmd": "cmd", 20 | "Key.ctrl": "ctrl", 21 | "Key.shift": "shift", 22 | "Key.alt": "alt", 23 | } 24 | 25 | class Module(core.module.Module): 26 | @core.decorators.never 27 | def __init__(self, config, theme): 28 | super().__init__(config, theme, []) 29 | 30 | self._listener = Listener(on_press=self._key_press, on_release=self._key_release) 31 | 32 | self._keys = util.format.aslist(self.parameter("keys", "Key.cmd,Key.ctrl,Key.alt,Key.shift")) 33 | 34 | for k in self._keys: 35 | self.add_widget(name=k, full_text=self._display_name(k), hidden=True) 36 | self._listener.start() 37 | 38 | def _display_name(self, key): 39 | return NAMES.get(key, key) 40 | 41 | def _key_press(self, key): 42 | key = str(key) 43 | if not key in self._keys: return 44 | self.widget(key).hidden = False 45 | core.event.trigger("update", [self.id], redraw_only=False) 46 | 47 | def _key_release(self, key): 48 | key = str(key) 49 | if not key in self._keys: return 50 | self.widget(key).hidden = True 51 | core.event.trigger("update", [self.id], redraw_only=False) 52 | 53 | def state(self, widget): 54 | return widget.name 55 | 56 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 57 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/layout.py: -------------------------------------------------------------------------------- 1 | layout-xkb.py -------------------------------------------------------------------------------- /bumblebee_status/modules/core/layout_xkb.py: -------------------------------------------------------------------------------- 1 | layout-xkb.py -------------------------------------------------------------------------------- /bumblebee_status/modules/core/load.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays system load. 4 | 5 | By default, opens `gnome-system-monitor` on left mouse click. 6 | 7 | Requirements: 8 | * gnome-system-monitor for default mouse click action 9 | 10 | Parameters: 11 | * load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs) 12 | * load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs) 13 | """ 14 | 15 | import os 16 | import multiprocessing 17 | 18 | import core.module 19 | import core.input 20 | 21 | 22 | class Module(core.module.Module): 23 | def __init__(self, config, theme): 24 | super().__init__(config, theme, core.widget.Widget(self.load)) 25 | self._load = [0, 0, 0] 26 | try: 27 | self._cpus = multiprocessing.cpu_count() 28 | except NotImplementedError as e: 29 | self._cpus = 1 30 | 31 | core.input.register( 32 | self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor" 33 | ) 34 | 35 | def load(self, widget): 36 | return "{:.02f}/{:.02f}/{:.02f}".format( 37 | self._load[0], self._load[1], self._load[2] 38 | ) 39 | 40 | def update(self): 41 | self._load = os.getloadavg() 42 | 43 | def state(self, widget): 44 | return self.threshold_state(self._load[0], self._cpus * 0.7, self._cpus * 0.8) 45 | 46 | 47 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 48 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/pasink.py: -------------------------------------------------------------------------------- 1 | from .pulseaudio import Module 2 | 3 | 4 | class Module(Module): 5 | def __init__(self, config, theme): 6 | super().__init__(config, theme, "sink") 7 | 8 | 9 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 10 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/pasource.py: -------------------------------------------------------------------------------- 1 | from .pulseaudio import Module 2 | 3 | 4 | class Module(Module): 5 | def __init__(self, config, theme): 6 | super().__init__(config, theme, "source") 7 | 8 | 9 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 10 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/pulsein.py: -------------------------------------------------------------------------------- 1 | from .pulsectl import Module 2 | 3 | 4 | class Module(Module): 5 | def __init__(self, config, theme): 6 | super().__init__(config, theme, "source") 7 | 8 | 9 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 10 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/pulseout.py: -------------------------------------------------------------------------------- 1 | from .pulsectl import Module 2 | 3 | 4 | class Module(Module): 5 | def __init__(self, config, theme): 6 | super().__init__(config, theme, "sink") 7 | 8 | 9 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 10 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/scroll.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays two widgets that can be used to scroll the whole status bar 4 | 5 | Parameters: 6 | * scroll.width: Width (in number of widgets) to display 7 | """ 8 | 9 | import core.module 10 | import core.widget 11 | import core.input 12 | import core.event 13 | 14 | import util.format 15 | 16 | class Module(core.module.Module): 17 | def __init__(self, config, theme): 18 | super().__init__(config, theme, []) 19 | self.__offset = 0 20 | self.__widgetcount = 0 21 | w = self.add_widget(full_text = "<") 22 | core.input.register(w, button=core.input.LEFT_MOUSE, cmd=self.scroll_left) 23 | w = self.add_widget(full_text = ">") 24 | core.input.register(w, button=core.input.LEFT_MOUSE, cmd=self.scroll_right) 25 | self.__width = util.format.asint(self.parameter("width")) 26 | config.set("output.width", self.__width) 27 | core.event.register("output.done", self.update_done) 28 | 29 | 30 | def scroll_left(self, _): 31 | if self.__offset > 0: 32 | core.event.trigger("output.scroll-left") 33 | 34 | def scroll_right(self, _): 35 | if self.__offset + self.__width < self.__widgetcount: 36 | core.event.trigger("output.scroll-right") 37 | 38 | def update_done(self, offset, widgetcount): 39 | self.__offset = offset 40 | self.__widgetcount = widgetcount 41 | 42 | def scroll(self): 43 | return False 44 | 45 | def state(self, widget): 46 | if widget.id == self.widgets()[0].id: 47 | if self.__offset == 0: 48 | return ["warning"] 49 | elif self.__offset + self.__width >= self.__widgetcount: 50 | return ["warning"] 51 | return [] 52 | 53 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 54 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/spacer.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Draws a widget with configurable text content. 4 | 5 | Parameters: 6 | * spacer.text: Widget contents (defaults to empty string) 7 | """ 8 | 9 | import core.module 10 | import core.widget 11 | import core.decorators 12 | import core.input 13 | 14 | class Module(core.module.Module): 15 | @core.decorators.every(minutes=60) 16 | def __init__(self, config, theme): 17 | super().__init__(config, theme, core.widget.Widget(self.text)) 18 | self.__text = self.parameter("text", "") 19 | 20 | def text(self, _): 21 | return self.__text 22 | 23 | def update_text(self, event): 24 | self.__text = core.input.button_name(event["button"]) 25 | 26 | 27 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 28 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/speedtest.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Performs a speedtest - only updates when the "play" button is clicked 4 | 5 | Requires the following python module: 6 | * speedtest-cli 7 | 8 | """ 9 | 10 | import sys 11 | 12 | import core.module 13 | import core.widget 14 | import core.input 15 | import core.event 16 | import core.decorators 17 | 18 | import speedtest 19 | 20 | 21 | class Module(core.module.Module): 22 | @core.decorators.never 23 | def __init__(self, config, theme): 24 | super().__init__(config, theme, []) 25 | 26 | self.background = True 27 | self.__result = "" 28 | self.__running = False 29 | 30 | start = self.add_widget(name="start") 31 | main = self.add_widget(name="main", full_text=self.result) 32 | 33 | core.input.register(start, button=core.input.LEFT_MOUSE, cmd=self.update_event) 34 | 35 | def result(self, _): 36 | return self.__result 37 | 38 | def update_event(self, _): 39 | self.__running = True 40 | self.update() 41 | 42 | def update(self): 43 | if not self.__running: 44 | return 45 | core.event.trigger("update", [self.id], redraw_only=True) 46 | s = speedtest.Speedtest() 47 | s.get_best_server() 48 | s.download(threads=None) 49 | s.upload(threads=None) 50 | 51 | self.__result = "ping: {:.2f}ms down: {:.2f}Mbps up: {:.2f}Mbps".format( 52 | s.results.ping, 53 | s.results.download / 1024 / 1024, 54 | s.results.upload / 1024 / 1024, 55 | ) 56 | self.__running = False 57 | core.event.trigger("update", [self.id], redraw_only=True) 58 | 59 | def state(self, widget): 60 | if widget.name == "start": 61 | return "running" if self.__running else "not-running" 62 | return None 63 | 64 | 65 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 66 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/test.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Test module 4 | """ 5 | 6 | import core.widget 7 | import core.module 8 | 9 | 10 | class Module(core.module.Module): 11 | def __init__(self, config, theme): 12 | super().__init__(config=config, theme=theme, widgets=core.widget.Widget("test")) 13 | 14 | 15 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 16 | -------------------------------------------------------------------------------- /bumblebee_status/modules/core/time.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=C0111,R0903 2 | 3 | """Displays the current date and time. 4 | 5 | Parameters: 6 | * time.format: strftime()-compatible formatting string 7 | * time.locale: locale to use rather than the system default 8 | """ 9 | 10 | import core.decorators 11 | from .datetime import Module 12 | 13 | 14 | class Module(Module): 15 | def __init__(self, config, theme): 16 | super().__init__(config, theme) 17 | 18 | def default_format(self): 19 | return "%X" 20 | 21 | 22 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 23 | -------------------------------------------------------------------------------- /bumblebee_status/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/bumblebee_status/util/__init__.py -------------------------------------------------------------------------------- /bumblebee_status/util/algorithm.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | 4 | def merge(target, *args): 5 | """Merges arbitrary data - copied from http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts 6 | 7 | :param target: the data structure to fill 8 | :param args: a list of data structures to merge into target 9 | 10 | :return: target, with all data in args merged into it 11 | :rtype: whatever type was originally passed in 12 | """ 13 | if len(args) > 1: 14 | for item in args: 15 | merge(target, item) 16 | return target 17 | 18 | item = args[0] 19 | if not isinstance(item, dict): 20 | return item 21 | for key, value in item.items(): 22 | if key in target and isinstance(target[key], dict): 23 | merge(target[key], value) 24 | else: 25 | if not key in target: 26 | target[key] = copy.deepcopy(value) 27 | return target 28 | 29 | 30 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 31 | -------------------------------------------------------------------------------- /bumblebee_status/util/rofi.py: -------------------------------------------------------------------------------- 1 | import i3ipc 2 | from rofi import Rofi 3 | 4 | 5 | def showScratchpads(self): 6 | i3 = i3ipc.Connection() 7 | scratchpad_windows = [] 8 | for leaf in i3.get_tree().scratchpad().leaves(): 9 | scratchpad_windows.append(leaf) 10 | 11 | if len(scratchpad_windows): 12 | # sort by window's name 13 | scratchpad_windows = sorted(scratchpad_windows, key=lambda x: x.ipc_data['name']) 14 | r = Rofi() 15 | scratchpad_windows_name = list(map(lambda x: x.ipc_data['name'], scratchpad_windows)) 16 | index, _ = r.select('Select Window in Scratchpad', scratchpad_windows_name) 17 | 18 | # select == -1 means nothing select 19 | if index != -1: 20 | scratchpad_windows[index].command('focus') -------------------------------------------------------------------------------- /bumblebee_status/util/store.py: -------------------------------------------------------------------------------- 1 | """Store interface 2 | 3 | Allows arbitrary classes to offer a simple get/set 4 | store interface by deriving from the Store class in 5 | this module 6 | """ 7 | 8 | 9 | class Store(object): 10 | """Interface for storing and retrieving simple values""" 11 | 12 | def __init__(self): 13 | super(Store, self).__init__() 14 | self._data = {} 15 | 16 | def set(self, key, value): 17 | """Sets key to value, overwriting any existing data for that key 18 | 19 | :param key: the name of the parameter to set 20 | :param value: the value to be set 21 | """ 22 | self._data[key] = {"value": value, "used": False} 23 | 24 | def unused_keys(self): 25 | """Returns a list of unused keys 26 | 27 | :return: a list of keys that are set, but never used 28 | :rtype: list of strings 29 | """ 30 | return [key for key, value in self._data.items() if value["used"] == False] 31 | 32 | def get(self, key, default=None): 33 | """Returns the current value for the specified key, or a default value, 34 | if the key is not set 35 | 36 | :param key: the name of the parameter to retrieve 37 | :param default: the default value to return, defaults to None 38 | """ 39 | if key in self._data: 40 | self._data[key]["used"] = True 41 | return self._data.get(key, {"value": default})["value"] 42 | 43 | 44 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 45 | -------------------------------------------------------------------------------- /bumblebee_status/util/xresources.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import shutil 3 | 4 | def query(key): 5 | if shutil.which("xgetres"): 6 | return subprocess.run(["xgetres", key], 7 | capture_output=True).stdout.decode("utf-8").strip() 8 | else: 9 | raise Exception("xgetres must be installed for this theme") 10 | 11 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | coverage run --source=. -m pytest tests -v 4 | coverage report -m 5 | -------------------------------------------------------------------------------- /create-pkgbuild.py: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | import sys 4 | import json 5 | import hashlib 6 | import requests 7 | 8 | rv = requests.request( 9 | "GET", 10 | "https://api.github.com/repos/tobi-wan-kenobi/bumblebee-status/releases/latest", 11 | ) 12 | 13 | if rv.status_code != 200: 14 | sys.exit(1) 15 | 16 | release = json.loads(rv.text) 17 | 18 | tar = requests.get(f"https://github.com/tobi-wan-kenobi/bumblebee-status/archive/{release['name']}.tar.gz") 19 | checksum = hashlib.sha512(tar.content).hexdigest() 20 | 21 | template = "" 22 | with open("./PKGBUILD.template") as f: 23 | template = f.read() 24 | 25 | template = template.replace("", release["name"].lstrip("v")) 26 | template = template.replace("", checksum) 27 | 28 | print(template) 29 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ================ 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | src/bumblebee_status 8 | -------------------------------------------------------------------------------- /docs/development/general.rst: -------------------------------------------------------------------------------- 1 | General guidelines 2 | ================== 3 | 4 | Not much, right now. If you have an idea and some code, just 5 | create a PR, I will gladly review and comment (and, likely, merge) 6 | 7 | Just one minor note: ``bumblebee-status`` is mostly a one-person, 8 | spare-time project, so please be patient when answering an issue, 9 | question or PR takes a while. 10 | 11 | Also, the (small) community that has gathered around ``bumblebee-status`` 12 | is extremely friendly and helpful, so don't hesitate to create issues 13 | with questions, somebody will always come up with a useful answer. 14 | 15 | :) 16 | -------------------------------------------------------------------------------- /docs/development/index.rst: -------------------------------------------------------------------------------- 1 | Developer's Guide 2 | ============================================ 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | general 9 | module 10 | theme 11 | testing 12 | 13 | -------------------------------------------------------------------------------- /docs/development/testing.rst: -------------------------------------------------------------------------------- 1 | Testing guidelines 2 | ================== 3 | 4 | Writing unit tests 5 | ------------------ 6 | 7 | Some general hints: 8 | 9 | - Tests should run with just Python Standard Library modules installed 10 | (i.e. if there are additional requirements, the test should be skipped 11 | if those are missing) 12 | - Tests should run even if there is no network connectivity (please mock 13 | urllib calls, for example) 14 | - Tests should be stable and not require modifications every time the 15 | tested code's implementation changes slightly (been there / done that) 16 | 17 | Right now, ``bumblebee-status`` uses the ``pytest`` framework, and its 18 | unit tests are located inside the ``tests/`` subdirectory. 19 | 20 | First implication: To run the new tests, you need to have ``pytest`` 21 | installed, it is not part of the Python Standard Library. Most 22 | distributions call the package ``python-pytest`` or ``python3-pytest`` 23 | or something similar (or you just use ``pip install --use pytest``) 24 | 25 | Aside from that, you just write your tests using ``pytest`` as usual, 26 | with one big caveat: 27 | 28 | **If** you create a new directory inside ``tests/``, you need to 29 | also create a file called ``__init__.py`` inside that, otherwise, 30 | modules won't load correctly. 31 | 32 | For examples, just browse the existing code. A good, minimal sample 33 | for unit testing ``bumblebee-status`` is ``tests/core/test_event.py``. 34 | -------------------------------------------------------------------------------- /docs/docstring.tmpl: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | :param : 4 | 5 | :raises : 6 | 7 | :return: 8 | :rtype: 9 | """ 10 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/docs/favicon.ico -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobi-wan-kenobi/bumblebee-status/07064d5c13e47e4f41eb6ad4e6ea91cf1bf2dbc3/docs/logo.png -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/other/NOTES.md: -------------------------------------------------------------------------------- 1 | # Design 2 | - core: only PSL 3 | - pass if modules are missing 4 | - minimize dependencies, code 5 | - test everything in the core framework 6 | 7 | # small stuff 8 | - @parameter? (or was it @attribute?) - remove getter/setters 9 | - use __ for private 10 | 11 | ## Improvements 12 | - app launcher (list of apps, themeable) 13 | - github pages? 14 | 15 | ## TODO 16 | - themes: use colors to improve theme readability 17 | - convert some stuff to simple attributes to reduce LOCs 18 | - use widget index for bumblebee-ctl as alternative (??) 19 | - use pytest? 20 | 21 | # documentation 22 | Add info about error widget and events for error logging 23 | - which location APIs are used? 24 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | docutils 2 | sphinx 3 | sphinx-rtd-theme 4 | commonmark 5 | recommonmark 6 | -------------------------------------------------------------------------------- /docs/src/bumblebee_status.core.rst: -------------------------------------------------------------------------------- 1 | bumblebee\_status.core package 2 | ============================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | bumblebee\_status.core.config module 8 | ------------------------------------ 9 | 10 | .. automodule:: bumblebee_status.core.config 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | bumblebee\_status.core.decorators module 16 | ---------------------------------------- 17 | 18 | .. automodule:: bumblebee_status.core.decorators 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | bumblebee\_status.core.event module 24 | ----------------------------------- 25 | 26 | .. automodule:: bumblebee_status.core.event 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | bumblebee\_status.core.input module 32 | ----------------------------------- 33 | 34 | .. automodule:: bumblebee_status.core.input 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | bumblebee\_status.core.module module 40 | ------------------------------------ 41 | 42 | .. automodule:: bumblebee_status.core.module 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | bumblebee\_status.core.output module 48 | ------------------------------------ 49 | 50 | .. automodule:: bumblebee_status.core.output 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | bumblebee\_status.core.theme module 56 | ----------------------------------- 57 | 58 | .. automodule:: bumblebee_status.core.theme 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | bumblebee\_status.core.widget module 64 | ------------------------------------ 65 | 66 | .. automodule:: bumblebee_status.core.widget 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | 72 | Module contents 73 | --------------- 74 | 75 | .. automodule:: bumblebee_status.core 76 | :members: 77 | :undoc-members: 78 | :show-inheritance: 79 | -------------------------------------------------------------------------------- /docs/src/bumblebee_status.rst: -------------------------------------------------------------------------------- 1 | bumblebee\_status package 2 | ========================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | bumblebee_status.core 11 | bumblebee_status.util 12 | 13 | Submodules 14 | ---------- 15 | 16 | bumblebee\_status.discover module 17 | --------------------------------- 18 | 19 | .. automodule:: bumblebee_status.discover 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | 24 | 25 | Module contents 26 | --------------- 27 | 28 | .. automodule:: bumblebee_status 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | -------------------------------------------------------------------------------- /docs/src/bumblebee_status.util.rst: -------------------------------------------------------------------------------- 1 | bumblebee\_status.util package 2 | ============================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | bumblebee\_status.util.algorithm module 8 | --------------------------------------- 9 | 10 | .. automodule:: bumblebee_status.util.algorithm 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | bumblebee\_status.util.cli module 16 | --------------------------------- 17 | 18 | .. automodule:: bumblebee_status.util.cli 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | bumblebee\_status.util.format module 24 | ------------------------------------ 25 | 26 | .. automodule:: bumblebee_status.util.format 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | bumblebee\_status.util.graph module 32 | ----------------------------------- 33 | 34 | .. automodule:: bumblebee_status.util.graph 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | bumblebee\_status.util.location module 40 | -------------------------------------- 41 | 42 | .. automodule:: bumblebee_status.util.location 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | bumblebee\_status.util.popup module 48 | ----------------------------------- 49 | 50 | .. automodule:: bumblebee_status.util.popup 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | bumblebee\_status.util.store module 56 | ----------------------------------- 57 | 58 | .. automodule:: bumblebee_status.util.store 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | 64 | Module contents 65 | --------------- 66 | 67 | .. automodule:: bumblebee_status.util 68 | :members: 69 | :undoc-members: 70 | :show-inheritance: 71 | -------------------------------------------------------------------------------- /man/bumblebee-ctl.1: -------------------------------------------------------------------------------- 1 | .TH BUMBLEBEE-CTL "1" "June 2022" "bumblebee-status" 2 | .SH NAME 3 | bumblebee-ctl \- Send commands to bumblebee-status 4 | .SH SYNOPSIS 5 | .B bumblebee-ctl 6 | [\fB\-h\fR] [\fB\-b\fR \fIbutton\fR] [\fB\-i\fR \fIID\fR] \fB-m\fR \fImodule\fR 7 | .SH DESCRIPTION 8 | .B bumblebee-ctl 9 | can be used to send commands to bumblebee-status. 10 | .SH OPTIONS 11 | .TP 12 | \fB\-h\fR, \fB\-\-help\fR 13 | show this help message and exit 14 | .TP 15 | \fB\-b\fR, \fB\-\-button\fR