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.Application import Application
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
from UM.Qt.QtApplication import QtApplication
from UM.i18n import i18nCatalog
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.Settings.GlobalStack import GlobalStack
from .FirmwareUpdateCheckerJob import FirmwareUpdateCheckerJob
from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine
from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
i18n_catalog = i18nCatalog("cura")
@ -38,18 +35,14 @@ class FirmwareUpdateChecker(Extension):
if Application.getInstance().getPreferences().getValue("info/automatic_update_check"):
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._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.
def _onActionTriggered(self, message, action):
if action == FirmwareUpdateCheckerMessage.STR_ACTION_DOWNLOAD:
machine_id = message.getMachineId()
download_url = self._lookups.getRedirectUserFor(machine_id)
download_url = message.getDownloadUrl()
if download_url is not None:
if QDesktopServices.openUrl(QUrl(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):
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.
# If the version info is different from the current version, spawn a message to
# 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.
# This is used when checking for a new firmware version at startup.
def checkFirmwareVersion(self, container = None, silent = False):
if self._lookups is None:
self._onPluginsLoaded()
container_name = container.definition.getName()
if container_name in self._checked_printer_names:
return
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,
lookups = self._lookups,
machine_name = container_name, metadata = metadata,
callback = self._onActionTriggered)
self._check_job.start()
self._check_job.finished.connect(self._onJobFinished)

View File

@ -9,7 +9,7 @@ from UM.Version import Version
import urllib.request
from urllib.error import URLError
from typing import Dict
from typing import Dict, Optional
from .FirmwareUpdateCheckerLookup import FirmwareUpdateCheckerLookup, getSettingsKeyForMachine
from .FirmwareUpdateCheckerMessage import FirmwareUpdateCheckerMessage
@ -25,13 +25,15 @@ class FirmwareUpdateCheckerJob(Job):
ZERO_VERSION = Version(STRING_ZERO_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__()
self._container = container
self.silent = silent
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.
def getUrlResponse(self, url: str) -> str:
@ -50,10 +52,12 @@ class FirmwareUpdateCheckerJob(Job):
raw_str = response.split("\n", 1)[0].rstrip()
return Version(raw_str)
def getCurrentVersionForMachine(self, machine_id: int) -> Version:
def getCurrentVersion(self) -> 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:
for url in machine_urls:
version = self.parseVersionResponse(self.getUrlResponse(url))
@ -61,16 +65,20 @@ class FirmwareUpdateCheckerJob(Job):
max_version = 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
def run(self):
if self._lookups is None:
Logger.log("e", "Can not check for a new release. URL not set!")
return
self._lookups = FirmwareUpdateCheckerLookup(self._machine_name, self._metadata)
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_version = Application.getInstance().getVersion()
self._headers = {"User-Agent": "%s - %s" % (application_name, application_version)}
@ -79,11 +87,11 @@ class FirmwareUpdateCheckerJob(Job):
machine_name = self._container.definition.getName()
# 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:
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 ""
setting_key_str = getSettingsKeyForMachine(machine_id)
@ -99,7 +107,7 @@ class FirmwareUpdateCheckerJob(Job):
# notify the user when no new firmware version is available.
if (checked_version != "") and (checked_version != current_version):
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.show()
else:

View File

@ -1,11 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import json
from typing import Callable, Dict, List, Optional
from UM.Logger import Logger
from typing import List, Optional
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")
@ -17,44 +13,23 @@ def getSettingsKeyForMachine(machine_id: int) -> str:
class FirmwareUpdateCheckerLookup:
def __init__(self, json_path) -> 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
def __init__(self, machine_name, machine_json) -> None:
# Parse all the needed lookup-tables from the ".json" file(s) in the resources folder.
self._machine_ids = [] # type:List[int]
self._machine_per_name = {} # type:Dict[str, int]
self._parse_version_url_per_machine = {} # type:Dict[int, Callable]
self._check_urls_per_machine = {} # type:Dict[int, List[str]]
self._redirect_user_per_machine = {} # type:Dict[int, str]
try:
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))
self._machine_id = machine_json.get("id")
self._machine_name = machine_name.lower() # Lower in-case upper-case chars are added to the original json.
self._check_urls = [] # type:List[str]
for check_url in machine_json.get("check_urls"):
self._check_urls.append(check_url)
self._redirect_user = machine_json.get("update_url")
def getMachineIds(self) -> List[int]:
return self._machine_ids
def getMachineId(self) -> Optional[int]:
return self._machine_id
def getMachineByName(self, machine_name: str) -> Optional[int]:
return self._machine_per_name.get(machine_name)
def getMachineName(self) -> Optional[int]:
return self._machine_name
def getParseVersionUrlFor(self, machine_id: int) -> Optional[Callable]:
return self._parse_version_url_per_machine.get(machine_id)
def getCheckUrls(self) -> Optional[List[str]]:
return self._check_urls
def getCheckUrlsFor(self, machine_id: int) -> Optional[List[str]]:
return self._check_urls_per_machine.get(machine_id)
def getRedirectUserFor(self, machine_id: int) -> Optional[str]:
return self._redirect_user_per_machine.get(machine_id)
def getRedirectUserUrl(self) -> Optional[str]:
return self._redirect_user

View File

@ -9,7 +9,7 @@ i18n_catalog = i18nCatalog("cura")
class FirmwareUpdateCheckerMessage(Message):
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(
"@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(
@ -19,6 +19,7 @@ class FirmwareUpdateCheckerMessage(Message):
"New %s firmware available") % machine_name)
self._machine_id = machine_id
self._download_url = download_url
self.addAction(self.STR_ACTION_DOWNLOAD,
i18n_catalog.i18nc("@action:button", "How to update"),
@ -29,3 +30,6 @@ class FirmwareUpdateCheckerMessage(Message):
def getMachineId(self) -> int:
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" ],
"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"
},
"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": {

View File

@ -30,7 +30,12 @@
"first_start_actions": [ "DiscoverUM3Action" ],
"supported_actions": [ "DiscoverUM3Action" ],
"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": {