diff --git a/plugins/UM3NetworkPrinting/src/CloudFlowMessage.py b/plugins/UM3NetworkPrinting/src/CloudFlowMessage.py new file mode 100644 index 0000000000..ab299afad0 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/CloudFlowMessage.py @@ -0,0 +1,38 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import os + +from PyQt5.QtCore import QUrl +from PyQt5.QtGui import QDesktopServices + +from UM import i18nCatalog +from UM.Message import Message +from cura.CuraApplication import CuraApplication + + +I18N_CATALOG = i18nCatalog("cura") + + +class CloudFlowMessage(Message): + + def __init__(self, address: str) -> None: + super().__init__( + text=I18N_CATALOG.i18nc("@info:status", + "Send and monitor print jobs from anywhere using your Ultimaker account."), + lifetime=0, + dismissable=True, + option_state=False, + image_source=QUrl.fromLocalFile(os.path.join( + CuraApplication.getInstance().getPluginRegistry().getPluginPath("UM3NetworkPrinting"), + "resources", "svg", "cloud-flow-start.svg" + )), + image_caption=I18N_CATALOG.i18nc("@info:status Ultimaker Cloud should not be translated.", + "Connect to Ultimaker Cloud"), + ) + self._address = address + self.addAction("", I18N_CATALOG.i18nc("@action", "Get started"), "", "") + self.actionTriggered.connect(self._onCloudFlowStarted) + + def _onCloudFlowStarted(self, messageId: str, actionId: str) -> None: + QDesktopServices.openUrl(QUrl("http://{}/cloud_connect".format(self._address))) + self.hide() diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/PrinterSystemStatus.py b/plugins/UM3NetworkPrinting/src/Models/Http/PrinterSystemStatus.py new file mode 100644 index 0000000000..ed85ed1799 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Models/Http/PrinterSystemStatus.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from ..BaseModel import BaseModel + + +## Class representing the system status of a printer. +class PrinterSystemStatus(BaseModel): + + def __init__(self, guid: str, firmware: str, hostname: str, name: str, platform: str, variant: str, **kwargs + ) -> None: + self.guid = guid + self.firmware = firmware + self.hostname = hostname + self.name = name + self.platform = platform + self.variant = variant + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Network/NetworkOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Network/NetworkOutputDeviceManager.py index 72331fdb8d..ea62f2b205 100644 --- a/plugins/UM3NetworkPrinting/src/Network/NetworkOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Network/NetworkOutputDeviceManager.py @@ -3,7 +3,6 @@ from typing import Dict, Optional, Callable from UM import i18nCatalog -from UM.Logger import Logger from UM.Signal import Signal from UM.Version import Version @@ -14,9 +13,13 @@ from cura.Settings.GlobalStack import GlobalStack from .ZeroConfClient import ZeroConfClient from .ClusterApiClient import ClusterApiClient from .NetworkOutputDevice import NetworkOutputDevice +from ..CloudFlowMessage import CloudFlowMessage from ..Models.Http.PrinterSystemStatus import PrinterSystemStatus +I18N_CATALOG = i18nCatalog("cura") + + ## The NetworkOutputDeviceManager is responsible for discovering and managing local networked clusters. class NetworkOutputDeviceManager: @@ -65,7 +68,7 @@ class NetworkOutputDeviceManager: self._manual_instances[address] = callback new_manual_devices = ",".join(self._manual_instances.keys()) CuraApplication.getInstance().getPreferences().setValue(self.MANUAL_DEVICES_PREFERENCE_KEY, new_manual_devices) - api_client = ClusterApiClient(address, self._onApiError) + api_client = ClusterApiClient(address, lambda error: print(error)) api_client.getSystem(lambda status: self._onCheckManualDeviceResponse(address, status)) ## Remove a manually added networked printer. @@ -103,13 +106,6 @@ class NetworkOutputDeviceManager: # Remove device if it is not meant for the active machine. CuraApplication.getInstance().getOutputDeviceManager().removeOutputDevice(device.key) - ## Add a device to the current active machine. - @staticmethod - def _connectToOutputDevice(device: PrinterOutputDevice, active_machine: GlobalStack) -> None: - device.connect() - active_machine.addConfiguredConnectionType(device.connectionType.value) - CuraApplication.getInstance().getOutputDeviceManager().addOutputDevice(device) - ## Callback for when a manual device check request was responded to. def _onCheckManualDeviceResponse(self, address: str, status: PrinterSystemStatus) -> None: callback = self._manual_instances.get(address, None) @@ -191,6 +187,7 @@ class NetworkOutputDeviceManager: active_machine.setMetaDataEntry(self.META_NETWORK_KEY, device.key) active_machine.setMetaDataEntry("group_name", device.name) self._connectToOutputDevice(device, active_machine) + CloudFlowMessage(device.ipAddress).show() # Nudge the user to start using Ultimaker Cloud. ## Load the user-configured manual devices from Cura preferences. def _getStoredManualInstances(self) -> Dict[str, Optional[Callable]]: @@ -199,8 +196,9 @@ class NetworkOutputDeviceManager: manual_instances = preferences.getValue(self.MANUAL_DEVICES_PREFERENCE_KEY).split(",") return {address: None for address in manual_instances} - ## Handles an API error received from the cloud. - # \param errors: The errors received + ## Add a device to the current active machine. @staticmethod - def _onApiError(errors) -> None: - Logger.log("w", str(errors)) + def _connectToOutputDevice(device: PrinterOutputDevice, active_machine: GlobalStack) -> None: + device.connect() + active_machine.addConfiguredConnectionType(device.connectionType.value) + CuraApplication.getInstance().getOutputDeviceManager().addOutputDevice(device) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 0b108e0a1b..416c60006b 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -53,142 +53,3 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): ## Remove a manually connected networked printer. def removeManualDevice(self, key: str, address: Optional[str] = None) -> None: self._network_output_device_manager.removeManualDevice(key, address) - - # ## Check if the prerequsites are in place to start the cloud flow - # def checkCloudFlowIsPossible(self, cluster: Optional[CloudOutputDevice]) -> None: - # Logger.log("d", "Checking if cloud connection is possible...") - # - # # Pre-Check: Skip if active machine already has been cloud connected or you said don't ask again - # active_machine = self._application.getMachineManager().activeMachine # type: Optional[GlobalStack] - # if active_machine: - # # Check 1A: Printer isn't already configured for cloud - # if ConnectionType.CloudConnection.value in active_machine.configuredConnectionTypes: - # Logger.log("d", "Active machine was already configured for cloud.") - # return - # - # # Check 1B: Printer isn't already configured for cloud - # if active_machine.getMetaDataEntry("cloud_flow_complete", False): - # Logger.log("d", "Active machine was already configured for cloud.") - # return - # - # # Check 2: User did not already say "Don't ask me again" - # if active_machine.getMetaDataEntry("do_not_show_cloud_message", False): - # Logger.log("d", "Active machine shouldn't ask about cloud anymore.") - # return - # - # # Check 3: User is logged in with an Ultimaker account - # if not self._account.isLoggedIn: - # Logger.log("d", "Cloud Flow not possible: User not logged in!") - # return - # - # # Check 4: Machine is configured for network connectivity - # if not self._application.getMachineManager().activeMachineHasNetworkConnection: - # Logger.log("d", "Cloud Flow not possible: Machine is not connected!") - # return - # - # # Check 5: Machine has correct firmware version - # firmware_version = self._application.getMachineManager().activeMachineFirmwareVersion # type: str - # if not Version(firmware_version) > self._min_cloud_version: - # Logger.log("d", "Cloud Flow not possible: Machine firmware (%s) is too low! (Requires version %s)", - # firmware_version, - # self._min_cloud_version) - # return - # - # Logger.log("d", "Cloud flow is possible!") - # self.cloudFlowIsPossible.emit() - - # def _onCloudFlowPossible(self) -> None: - # # Cloud flow is possible, so show the message - # if not self._start_cloud_flow_message: - # self._createCloudFlowStartMessage() - # if self._start_cloud_flow_message and not self._start_cloud_flow_message.visible: - # self._start_cloud_flow_message.show() - - # def _onCloudPrintingConfigured(self, device) -> None: - # # Hide the cloud flow start message if it was hanging around already - # # For example: if the user already had the browser openen and made the association themselves - # if self._start_cloud_flow_message and self._start_cloud_flow_message.visible: - # self._start_cloud_flow_message.hide() - # - # # Cloud flow is complete, so show the message - # if not self._cloud_flow_complete_message: - # self._createCloudFlowCompleteMessage() - # if self._cloud_flow_complete_message and not self._cloud_flow_complete_message.visible: - # self._cloud_flow_complete_message.show() - # - # # Set the machine's cloud flow as complete so we don't ask the user again and again for cloud connected printers - # active_machine = self._application.getMachineManager().activeMachine - # if active_machine: - # - # # The active machine _might_ not be the machine that was in the added cloud cluster and - # # then this will hide the cloud message for the wrong machine. So we only set it if the - # # host names match between the active machine and the newly added cluster - # saved_host_name = active_machine.getMetaDataEntry("um_network_key", "").split('.')[0] - # added_host_name = device.toDict()["host_name"] - # - # if added_host_name == saved_host_name: - # active_machine.setMetaDataEntry("do_not_show_cloud_message", True) - # - # return - - # def _onDontAskMeAgain(self, checked: bool) -> None: - # active_machine = self._application.getMachineManager().activeMachine # type: Optional[GlobalStack] - # if active_machine: - # active_machine.setMetaDataEntry("do_not_show_cloud_message", checked) - # if checked: - # Logger.log("d", "Will not ask the user again to cloud connect for current printer.") - # return - - # def _onCloudFlowStarted(self, messageId: str, actionId: str) -> None: - # address = self._application.getMachineManager().activeMachineAddress # type: str - # if address: - # QDesktopServices.openUrl(QUrl("http://" + address + "/cloud_connect")) - # if self._start_cloud_flow_message: - # self._start_cloud_flow_message.hide() - # self._start_cloud_flow_message = None - # return - - # def _onReviewCloudConnection(self, messageId: str, actionId: str) -> None: - # address = self._application.getMachineManager().activeMachineAddress # type: str - # if address: - # QDesktopServices.openUrl(QUrl("http://" + address + "/settings")) - # return - - # def _onMachineSwitched(self) -> None: - # # Hide any left over messages - # if self._start_cloud_flow_message is not None and self._start_cloud_flow_message.visible: - # self._start_cloud_flow_message.hide() - # if self._cloud_flow_complete_message is not None and self._cloud_flow_complete_message.visible: - # self._cloud_flow_complete_message.hide() - # - # # Check for cloud flow again with newly selected machine - # self.checkCloudFlowIsPossible(None) - - # def _createCloudFlowStartMessage(self): - # self._start_cloud_flow_message = Message( - # text = i18n_catalog.i18nc("@info:status", "Send and monitor print jobs from anywhere using your Ultimaker account."), - # lifetime = 0, - # image_source = QUrl.fromLocalFile(os.path.join( - # PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), - # "resources", "svg", "cloud-flow-start.svg" - # )), - # image_caption = i18n_catalog.i18nc("@info:status Ultimaker Cloud is a brand name and shouldn't be translated.", "Connect to Ultimaker Cloud"), - # option_text = i18n_catalog.i18nc("@action", "Don't ask me again for this printer."), - # option_state = False - # ) - # self._start_cloud_flow_message.addAction("", i18n_catalog.i18nc("@action", "Get started"), "", "") - # self._start_cloud_flow_message.optionToggled.connect(self._onDontAskMeAgain) - # self._start_cloud_flow_message.actionTriggered.connect(self._onCloudFlowStarted) - - # def _createCloudFlowCompleteMessage(self): - # self._cloud_flow_complete_message = Message( - # text = i18n_catalog.i18nc("@info:status", "You can now send and monitor print jobs from anywhere using your Ultimaker account."), - # lifetime = 30, - # image_source = QUrl.fromLocalFile(os.path.join( - # PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), - # "resources", "svg", "cloud-flow-completed.svg" - # )), - # image_caption = i18n_catalog.i18nc("@info:status", "Connected!") - # ) - # self._cloud_flow_complete_message.addAction("", i18n_catalog.i18nc("@action", "Review your connection"), "", "", 1) # TODO: Icon - # self._cloud_flow_complete_message.actionTriggered.connect(self._onReviewCloudConnection)