FirmwareUpdateCheckerJob.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from UM.Application import Application
  4. from UM.Message import Message
  5. from UM.Logger import Logger
  6. from UM.Job import Job
  7. from UM.Version import Version
  8. import urllib.request
  9. from urllib.error import URLError
  10. from typing import Dict, Optional
  11. import ssl
  12. import certifi
  13. from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine
  14. from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
  15. from UM.i18n import i18nCatalog
  16. i18n_catalog = i18nCatalog("cura")
  17. ## This job checks if there is an update available on the provided URL.
  18. class FirmwareUpdateCheckerJob(Job):
  19. STRING_ZERO_VERSION = "0.0.0"
  20. STRING_EPSILON_VERSION = "0.0.1"
  21. ZERO_VERSION = Version(STRING_ZERO_VERSION)
  22. EPSILON_VERSION = Version(STRING_EPSILON_VERSION)
  23. def __init__(self, silent, machine_name, metadata, callback) -> None:
  24. super().__init__()
  25. self.silent = silent
  26. self._callback = callback
  27. self._machine_name = machine_name
  28. self._metadata = metadata
  29. self._lookups = FirmwareUpdateCheckerLookup(self._machine_name, self._metadata)
  30. self._headers = {} # type:Dict[str, str] # Don't set headers yet.
  31. def getUrlResponse(self, url: str) -> str:
  32. result = self.STRING_ZERO_VERSION
  33. try:
  34. # CURA-6698 Create an SSL context and use certifi CA certificates for verification.
  35. context = ssl.SSLContext(protocol = ssl.PROTOCOL_TLSv1_2)
  36. context.load_verify_locations(cafile = certifi.where())
  37. request = urllib.request.Request(url, headers = self._headers)
  38. response = urllib.request.urlopen(request, context = context)
  39. result = response.read().decode("utf-8")
  40. except URLError:
  41. Logger.log("w", "Could not reach '{0}', if this URL is old, consider removal.".format(url))
  42. return result
  43. def parseVersionResponse(self, response: str) -> Version:
  44. raw_str = response.split("\n", 1)[0].rstrip()
  45. return Version(raw_str)
  46. def getCurrentVersion(self) -> Version:
  47. max_version = self.ZERO_VERSION
  48. if self._lookups is None:
  49. return max_version
  50. machine_urls = self._lookups.getCheckUrls()
  51. if machine_urls is not None:
  52. for url in machine_urls:
  53. version = self.parseVersionResponse(self.getUrlResponse(url))
  54. if version > max_version:
  55. max_version = version
  56. if max_version < self.EPSILON_VERSION:
  57. Logger.log("w", "MachineID {0} not handled!".format(self._lookups.getMachineName()))
  58. return max_version
  59. def run(self):
  60. try:
  61. # Initialize a Preference that stores the last version checked for this printer.
  62. Application.getInstance().getPreferences().addPreference(
  63. getSettingsKeyForMachine(self._lookups.getMachineId()), "")
  64. # Get headers
  65. application_name = Application.getInstance().getApplicationName()
  66. application_version = Application.getInstance().getVersion()
  67. self._headers = {"User-Agent": "%s - %s" % (application_name, application_version)}
  68. # If it is not None, then we compare between the checked_version and the current_version
  69. machine_id = self._lookups.getMachineId()
  70. if machine_id is not None:
  71. Logger.log("i", "You have a(n) {0} in the printer list. Do firmware-check.".format(self._machine_name))
  72. current_version = self.getCurrentVersion()
  73. # This case indicates that was an error checking the version.
  74. # It happens for instance when not connected to internet.
  75. if current_version == self.ZERO_VERSION:
  76. return
  77. # If it is the first time the version is checked, the checked_version is ""
  78. setting_key_str = getSettingsKeyForMachine(machine_id)
  79. checked_version = Version(Application.getInstance().getPreferences().getValue(setting_key_str))
  80. # If the checked_version is "", it's because is the first time we check firmware and in this case
  81. # we will not show the notification, but we will store it for the next time
  82. Application.getInstance().getPreferences().setValue(setting_key_str, current_version)
  83. Logger.log("i", "Reading firmware version of %s: checked = %s - latest = %s",
  84. self._machine_name, checked_version, current_version)
  85. # The first time we want to store the current version, the notification will not be shown,
  86. # because the new version of Cura will be release before the firmware and we don't want to
  87. # notify the user when no new firmware version is available.
  88. if (checked_version != "") and (checked_version != current_version):
  89. Logger.log("i", "Showing firmware update message for new version: {version}".format(version = current_version))
  90. message = FirmwareUpdateCheckerMessage(machine_id, self._machine_name,
  91. self._lookups.getRedirectUserUrl())
  92. message.actionTriggered.connect(self._callback)
  93. message.show()
  94. else:
  95. Logger.log("i", "No machine with name {0} in list of firmware to check.".format(self._machine_name))
  96. except Exception as e:
  97. Logger.logException("w", "Failed to check for new version: %s", e)
  98. if not self.silent:
  99. Message(i18n_catalog.i18nc("@info", "Could not access update information.")).show()
  100. return