check_updates.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. # Copyright (c) 2015 Ali Mousavi
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. # SOFTWARE.
  20. import os
  21. from subprocess import CalledProcessError, Popen
  22. from libqtile.log_utils import logger
  23. from libqtile.widget import base
  24. import subprocess
  25. class CheckUpdates(base.ThreadPoolText):
  26. """Shows number of pending updates in different unix systems"""
  27. orientations = base.ORIENTATION_HORIZONTAL
  28. defaults = [
  29. ("distro", "Arch", "Name of your distribution"),
  30. ("custom_command", None, "Custom shell command for checking updates (counts the lines of the output)"),
  31. ("update_interval", 60, "Update interval in seconds."),
  32. ('execute', None, 'Command to execute on click'),
  33. ("display_format", "Updates: {updates}", "Display format if updates available"),
  34. ("colour_no_updates", "ffffff", "Colour when there's no updates."),
  35. ("colour_have_updates", "ffffff", "Colour when there are updates."),
  36. ("restart_indicator", "", "Indicator to represent reboot is required. (Ubuntu only)"),
  37. ("no_update_string", "", "String to display if no updates available")
  38. ]
  39. def __init__(self, **config):
  40. base.ThreadPoolText.__init__(self, "", **config)
  41. self.add_defaults(CheckUpdates.defaults)
  42. # self.add_callbacks({"Button1": self._check_updates})
  43. self.add_callbacks({"Button1": self._show_notification})
  44. # format: "Distro": ("cmd", "number of lines to subtract from output")
  45. self.cmd_dict = {"Arch": ("pacman -Qu", 0),
  46. "Arch_checkupdates": ("checkupdates", 0),
  47. "Arch_Sup": ("pacman -Sup", 1),
  48. "Arch_yay": ("yay -Qu", 0),
  49. "Debian": ("apt-show-versions -u -b", 0),
  50. "Ubuntu": ("aptitude search ~U", 0),
  51. "Fedora": ("dnf list updates", 3),
  52. "FreeBSD": ("pkg_version -I -l '<'", 0),
  53. "Mandriva": ("urpmq --auto-select", 0)
  54. }
  55. # Check if distro name is valid.
  56. try:
  57. self.cmd = self.cmd_dict[self.distro][0].split()
  58. self.subtr = self.cmd_dict[self.distro][1]
  59. except KeyError:
  60. distros = sorted(self.cmd_dict.keys())
  61. logger.error(self.distro + ' is not a valid distro name. ' +
  62. 'Use one of the list: ' + str(distros) + '.')
  63. self.cmd = None
  64. if self.execute:
  65. self.add_callbacks({'Button1': self.do_execute})
  66. def _check_updates(self):
  67. # type: () -> str
  68. try:
  69. if self.custom_command is None:
  70. updates = self.call_process(self.cmd)
  71. else:
  72. updates = self.call_process(self.custom_command, shell=True)
  73. self.subtr = 0
  74. except CalledProcessError:
  75. updates = ""
  76. num_updates = len(updates.splitlines()) - self.subtr
  77. if num_updates == 0:
  78. self.layout.colour = self.colour_no_updates
  79. return self.no_update_string
  80. num_updates = str(num_updates)
  81. if self.restart_indicator and os.path.exists('/var/run/reboot-required'):
  82. num_updates += self.restart_indicator
  83. self.layout.colour = self.colour_have_updates
  84. return self.display_format.format(**{"updates": num_updates})
  85. def poll(self):
  86. # type: () -> str
  87. if not self.cmd:
  88. return "N/A"
  89. return self._check_updates()
  90. def do_execute(self):
  91. self._process = Popen(self.execute, shell=True)
  92. self.timeout_add(1, self._refresh_count)
  93. def _refresh_count(self):
  94. if self._process.poll() is None:
  95. self.timeout_add(1, self._refresh_count)
  96. else:
  97. self.timer_setup()
  98. def _show_notification(self):
  99. self._check_updates()
  100. subprocess.call(["notify-send", "-i", "software-update-available", "Updates: WORKS!!!"])