Put the firmware-update meta-data in the 'normal' printer definitions and make the code handle that.

This commit is contained in:
Remco Burema 2018-10-13 21:55:33 +02:00
parent 931143ceaa
commit 2e3abbc904
8 changed files with 73 additions and 113 deletions

View File

@ -10,15 +10,12 @@ from typing import Set
from UM.Extension import Extension from UM.Extension import Extension
from UM.Application import Application from UM.Application import Application
from UM.Logger import Logger from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
from UM.Qt.QtApplication import QtApplication
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
from .FirmwareUpdateCheckerJob import FirmwareUpdateCheckerJob from .FirmwareUpdateCheckerJob import FirmwareUpdateCheckerJob
from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine
from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
@ -38,18 +35,14 @@ class FirmwareUpdateChecker(Extension):
if Application.getInstance().getPreferences().getValue("info/automatic_update_check"): if Application.getInstance().getPreferences().getValue("info/automatic_update_check"):
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
# Partly initialize after creation, since we need our own path from the plugin-manager.
self._download_url = None
self._check_job = None self._check_job = None
self._checked_printer_names = set() # type: Set[str] self._checked_printer_names = set() # type: Set[str]
self._lookups = None
QtApplication.pluginsLoaded.connect(self._onPluginsLoaded)
## Callback for the message that is spawned when there is a new version. ## Callback for the message that is spawned when there is a new version.
def _onActionTriggered(self, message, action): def _onActionTriggered(self, message, action):
if action == FirmwareUpdateCheckerMessage.STR_ACTION_DOWNLOAD: if action == FirmwareUpdateCheckerMessage.STR_ACTION_DOWNLOAD:
machine_id = message.getMachineId() machine_id = message.getMachineId()
download_url = self._lookups.getRedirectUserFor(machine_id) download_url = message.getDownloadUrl()
if download_url is not None: if download_url is not None:
if QDesktopServices.openUrl(QUrl(download_url)): if QDesktopServices.openUrl(QUrl(download_url)):
Logger.log("i", "Redirected browser to {0} to show newly available firmware.".format(download_url)) Logger.log("i", "Redirected browser to {0} to show newly available firmware.".format(download_url))
@ -66,18 +59,6 @@ class FirmwareUpdateChecker(Extension):
def _onJobFinished(self, *args, **kwargs): def _onJobFinished(self, *args, **kwargs):
self._check_job = None self._check_job = None
def _onPluginsLoaded(self):
if self._lookups is not None:
return
self._lookups = FirmwareUpdateCheckerLookup(os.path.join(PluginRegistry.getInstance().getPluginPath(
"FirmwareUpdateChecker"), "resources/machines.json"))
# Initialize the Preference called `latest_checked_firmware` that stores the last version
# checked for each printer.
for machine_id in self._lookups.getMachineIds():
Application.getInstance().getPreferences().addPreference(getSettingsKeyForMachine(machine_id), "")
## Connect with software.ultimaker.com, load latest.version and check version info. ## Connect with software.ultimaker.com, load latest.version and check version info.
# If the version info is different from the current version, spawn a message to # If the version info is different from the current version, spawn a message to
# allow the user to download it. # allow the user to download it.
@ -85,16 +66,18 @@ class FirmwareUpdateChecker(Extension):
# \param silent type(boolean) Suppresses messages other than "new version found" messages. # \param silent type(boolean) Suppresses messages other than "new version found" messages.
# This is used when checking for a new firmware version at startup. # This is used when checking for a new firmware version at startup.
def checkFirmwareVersion(self, container = None, silent = False): def checkFirmwareVersion(self, container = None, silent = False):
if self._lookups is None:
self._onPluginsLoaded()
container_name = container.definition.getName() container_name = container.definition.getName()
if container_name in self._checked_printer_names: if container_name in self._checked_printer_names:
return return
self._checked_printer_names.add(container_name) self._checked_printer_names.add(container_name)
metadata = container.definition.getMetaData().get("firmware_update_info")
if metadata is None:
Logger.log("i", "No machine with name {0} in list of firmware to check.".format(container_name))
return
self._check_job = FirmwareUpdateCheckerJob(container = container, silent = silent, self._check_job = FirmwareUpdateCheckerJob(container = container, silent = silent,
lookups = self._lookups, machine_name = container_name, metadata = metadata,
callback = self._onActionTriggered) callback = self._onActionTriggered)
self._check_job.start() self._check_job.start()
self._check_job.finished.connect(self._onJobFinished) self._check_job.finished.connect(self._onJobFinished)

View File

@ -9,7 +9,7 @@ from UM.Version import Version
import urllib.request import urllib.request
from urllib.error import URLError from urllib.error import URLError
from typing import Dict from typing import Dict, Optional
from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine
from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
@ -25,13 +25,15 @@ class FirmwareUpdateCheckerJob(Job):
ZERO_VERSION = Version(STRING_ZERO_VERSION) ZERO_VERSION = Version(STRING_ZERO_VERSION)
EPSILON_VERSION = Version(STRING_EPSILON_VERSION) EPSILON_VERSION = Version(STRING_EPSILON_VERSION)
def __init__(self, container, silent, lookups: FirmwareUpdateCheckerLookup, callback) -> None: def __init__(self, container, silent, machine_name, metadata, callback) -> None:
super().__init__() super().__init__()
self._container = container self._container = container
self.silent = silent self.silent = silent
self._callback = callback self._callback = callback
self._lookups = lookups self._machine_name = machine_name
self._metadata = metadata
self._lookups = None # type:Optional[FirmwareUpdateCheckerLookup]
self._headers = {} # type:Dict[str, str] # Don't set headers yet. self._headers = {} # type:Dict[str, str] # Don't set headers yet.
def getUrlResponse(self, url: str) -> str: def getUrlResponse(self, url: str) -> str:
@ -50,10 +52,12 @@ class FirmwareUpdateCheckerJob(Job):
raw_str = response.split("\n", 1)[0].rstrip() raw_str = response.split("\n", 1)[0].rstrip()
return Version(raw_str) return Version(raw_str)
def getCurrentVersionForMachine(self, machine_id: int) -> Version: def getCurrentVersion(self) -> Version:
max_version = self.ZERO_VERSION max_version = self.ZERO_VERSION
if self._lookups is None:
return max_version
machine_urls = self._lookups.getCheckUrlsFor(machine_id) machine_urls = self._lookups.getCheckUrls()
if machine_urls is not None: if machine_urls is not None:
for url in machine_urls: for url in machine_urls:
version = self.parseVersionResponse(self.getUrlResponse(url)) version = self.parseVersionResponse(self.getUrlResponse(url))
@ -61,16 +65,20 @@ class FirmwareUpdateCheckerJob(Job):
max_version = version max_version = version
if max_version < self.EPSILON_VERSION: if max_version < self.EPSILON_VERSION:
Logger.log("w", "MachineID {0} not handled!".format(repr(machine_id))) Logger.log("w", "MachineID {0} not handled!".format(self._lookups.getMachineName()))
return max_version return max_version
def run(self): def run(self):
if self._lookups is None: if self._lookups is None:
Logger.log("e", "Can not check for a new release. URL not set!") self._lookups = FirmwareUpdateCheckerLookup(self._machine_name, self._metadata)
return
try: try:
# Initialize a Preference that stores the last version checked for this printer.
Application.getInstance().getPreferences().addPreference(
getSettingsKeyForMachine(self._lookups.getMachineId()), "")
# Get headers
application_name = Application.getInstance().getApplicationName() application_name = Application.getInstance().getApplicationName()
application_version = Application.getInstance().getVersion() application_version = Application.getInstance().getVersion()
self._headers = {"User-Agent": "%s - %s" % (application_name, application_version)} self._headers = {"User-Agent": "%s - %s" % (application_name, application_version)}
@ -79,11 +87,11 @@ class FirmwareUpdateCheckerJob(Job):
machine_name = self._container.definition.getName() machine_name = self._container.definition.getName()
# If it is not None, then we compare between the checked_version and the current_version # If it is not None, then we compare between the checked_version and the current_version
machine_id = self._lookups.getMachineByName(machine_name.lower()) machine_id = self._lookups.getMachineId()
if machine_id is not None: if machine_id is not None:
Logger.log("i", "You have a(n) {0} in the printer list. Let's check the firmware!".format(machine_name)) Logger.log("i", "You have a(n) {0} in the printer list. Let's check the firmware!".format(machine_name))
current_version = self.getCurrentVersionForMachine(machine_id) current_version = self.getCurrentVersion()
# If it is the first time the version is checked, the checked_version is "" # If it is the first time the version is checked, the checked_version is ""
setting_key_str = getSettingsKeyForMachine(machine_id) setting_key_str = getSettingsKeyForMachine(machine_id)
@ -99,7 +107,7 @@ class FirmwareUpdateCheckerJob(Job):
# notify the user when no new firmware version is available. # notify the user when no new firmware version is available.
if (checked_version != "") and (checked_version != current_version): if (checked_version != "") and (checked_version != current_version):
Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE") Logger.log("i", "SHOWING FIRMWARE UPDATE MESSAGE")
message = FirmwareUpdateCheckerMessage(machine_id, machine_name) message = FirmwareUpdateCheckerMessage(machine_id, machine_name, self._lookups.getRedirectUserUrl())
message.actionTriggered.connect(self._callback) message.actionTriggered.connect(self._callback)
message.show() message.show()
else: else:

View File

@ -1,11 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import json from typing import List, Optional
from typing import Callable, Dict, List, Optional
from UM.Logger import Logger
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
@ -17,44 +13,23 @@ def getSettingsKeyForMachine(machine_id: int) -> str:
class FirmwareUpdateCheckerLookup: class FirmwareUpdateCheckerLookup:
def __init__(self, json_path) -> None: def __init__(self, machine_name, machine_json) -> None:
# Open the .json file with the needed lookup-lists for each machine(/model) and retrieve "raw" json.
with open(json_path, "r", encoding = "utf-8") as json_file:
machines_json = json.load(json_file).get("machines")
if machines_json is None:
Logger.log("e", "Missing or inaccessible: {0}".format(json_path))
return
# Parse all the needed lookup-tables from the ".json" file(s) in the resources folder. # Parse all the needed lookup-tables from the ".json" file(s) in the resources folder.
self._machine_ids = [] # type:List[int] self._machine_id = machine_json.get("id")
self._machine_per_name = {} # type:Dict[str, int] self._machine_name = machine_name.lower() # Lower in-case upper-case chars are added to the original json.
self._parse_version_url_per_machine = {} # type:Dict[int, Callable] self._check_urls = [] # type:List[str]
self._check_urls_per_machine = {} # type:Dict[int, List[str]] for check_url in machine_json.get("check_urls"):
self._redirect_user_per_machine = {} # type:Dict[int, str] self._check_urls.append(check_url)
try: self._redirect_user = machine_json.get("update_url")
for machine_json in machines_json:
machine_id = machine_json.get("id")
machine_name = machine_json.get("name").lower() # Lower in case upper-case char are added to the json.
self._machine_ids.append(machine_id)
self._machine_per_name[machine_name] = machine_id
self._check_urls_per_machine[machine_id] = [] # Multiple check-urls: see "_comment" in the .json file.
for check_url in machine_json.get("check_urls"):
self._check_urls_per_machine[machine_id].append(check_url)
self._redirect_user_per_machine[machine_id] = machine_json.get("update_url")
except Exception as ex:
Logger.log("e", "Couldn't parse firmware-update-check lookup-lists from file because {0}.".format(ex))
def getMachineIds(self) -> List[int]: def getMachineId(self) -> Optional[int]:
return self._machine_ids return self._machine_id
def getMachineByName(self, machine_name: str) -> Optional[int]: def getMachineName(self) -> Optional[int]:
return self._machine_per_name.get(machine_name) return self._machine_name
def getParseVersionUrlFor(self, machine_id: int) -> Optional[Callable]: def getCheckUrls(self) -> Optional[List[str]]:
return self._parse_version_url_per_machine.get(machine_id) return self._check_urls
def getCheckUrlsFor(self, machine_id: int) -> Optional[List[str]]: def getRedirectUserUrl(self) -> Optional[str]:
return self._check_urls_per_machine.get(machine_id) return self._redirect_user
def getRedirectUserFor(self, machine_id: int) -> Optional[str]:
return self._redirect_user_per_machine.get(machine_id)

View File

@ -9,7 +9,7 @@ i18n_catalog = i18nCatalog("cura")
class FirmwareUpdateCheckerMessage(Message): class FirmwareUpdateCheckerMessage(Message):
STR_ACTION_DOWNLOAD = "download" STR_ACTION_DOWNLOAD = "download"
def __init__(self, machine_id: int, machine_name: str) -> None: def __init__(self, machine_id: int, machine_name: str, download_url: str) -> None:
super().__init__(i18n_catalog.i18nc( super().__init__(i18n_catalog.i18nc(
"@info Don't translate {machine_name}, since it gets replaced by a printer name!", "@info Don't translate {machine_name}, since it gets replaced by a printer name!",
"New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format( "New features are available for your {machine_name}! It is recommended to update the firmware on your printer.").format(
@ -19,6 +19,7 @@ class FirmwareUpdateCheckerMessage(Message):
"New %s firmware available") % machine_name) "New %s firmware available") % machine_name)
self._machine_id = machine_id self._machine_id = machine_id
self._download_url = download_url
self.addAction(self.STR_ACTION_DOWNLOAD, self.addAction(self.STR_ACTION_DOWNLOAD,
i18n_catalog.i18nc("@action:button", "How to update"), i18n_catalog.i18nc("@action:button", "How to update"),
@ -29,3 +30,6 @@ class FirmwareUpdateCheckerMessage(Message):
def getMachineId(self) -> int: def getMachineId(self) -> int:
return self._machine_id return self._machine_id
def getDownloadUrl(self) -> str:
return self._download_url

View File

@ -1,33 +0,0 @@
{
"_comment": "There are multiple 'check_urls', because sometimes an URL is about to be phased out, and it's useful to have a new 'future-proof' one at the ready.",
"machines":
[
{
"id": 9066,
"name": "ultimaker 3",
"check_urls":
[
"http://software.ultimaker.com/jedi/releases/latest.version?utm_source=cura&utm_medium=software&utm_campaign=resources",
"http://software.ultimaker.com/releases/firmware/9066/stable/version.txt"
],
"update_url": "https://ultimaker.com/en/resources/20500-upgrade-firmware"
},
{
"id": 9511,
"name": "ultimaker 3 extended",
"check_urls":
[
"http://software.ultimaker.com/jedi/releases/latest.version?utm_source=cura&utm_medium=software&utm_campaign=resources",
"http://software.ultimaker.com/releases/firmware/9511/stable/version.txt"
],
"update_url": "https://ultimaker.com/en/resources/20500-upgrade-firmware"
},
{
"id": 9051,
"name": "ultimaker s5",
"check_urls": ["http://software.ultimaker.com/releases/firmware/9051/stable/version.txt"],
"update_url": "https://ultimaker.com/en/resources/20500-upgrade-firmware"
}
]
}

View File

@ -24,7 +24,16 @@
}, },
"first_start_actions": [ "DiscoverUM3Action" ], "first_start_actions": [ "DiscoverUM3Action" ],
"supported_actions": [ "DiscoverUM3Action" ], "supported_actions": [ "DiscoverUM3Action" ],
"supports_usb_connection": false "supports_usb_connection": false,
"firmware_update_info": {
"id": 9066,
"check_urls":
[
"http://software.ultimaker.com/jedi/releases/latest.version?utm_source=cura&utm_medium=software&utm_campaign=resources",
"http://software.ultimaker.com/releases/firmware/9066/stable/version.txt"
],
"update_url": "https://ultimaker.com/en/resources/20500-upgrade-firmware"
}
}, },

View File

@ -23,7 +23,16 @@
"1": "ultimaker3_extended_extruder_right" "1": "ultimaker3_extended_extruder_right"
}, },
"first_start_actions": [ "DiscoverUM3Action" ], "first_start_actions": [ "DiscoverUM3Action" ],
"supported_actions": [ "DiscoverUM3Action" ] "supported_actions": [ "DiscoverUM3Action" ],
"firmware_update_info": {
"id": 9511,
"check_urls":
[
"http://software.ultimaker.com/jedi/releases/latest.version?utm_source=cura&utm_medium=software&utm_campaign=resources",
"http://software.ultimaker.com/releases/firmware/9511/stable/version.txt"
],
"update_url": "https://ultimaker.com/en/resources/20500-upgrade-firmware"
}
}, },
"overrides": { "overrides": {

View File

@ -30,7 +30,12 @@
"first_start_actions": [ "DiscoverUM3Action" ], "first_start_actions": [ "DiscoverUM3Action" ],
"supported_actions": [ "DiscoverUM3Action" ], "supported_actions": [ "DiscoverUM3Action" ],
"supports_usb_connection": false, "supports_usb_connection": false,
"weight": -1 "weight": -1,
"firmware_update_info": {
"id": 9051,
"check_urls": ["http://software.ultimaker.com/releases/firmware/9051/stable/version.txt"],
"update_url": "https://ultimaker.com/en/resources/20500-upgrade-firmware"
}
}, },
"overrides": { "overrides": {