def apiLoad(): logger = logging.getLogger(__name__) if not settings.get("api", "enabled"): abort(401) if not "apikey" in request.values.keys(): abort(401) if request.values["apikey"] != settings.get("api", "key"): abort(403) if not "file" in request.files.keys(): abort(400) # Perform an upload file = request.files["file"] if not gcodefiles.isGcodeFileName(file.filename): abort(400) destination = FileDestinations.LOCAL filename, done = gcodeManager.addFile(file, destination) if filename is None: logger.warn("Upload via API failed") abort(500) # Immediately perform a file select and possibly print too printAfterSelect = False if "print" in request.values.keys() and request.values["print"] in valid_boolean_trues: printAfterSelect = True filepath = gcodeManager.getAbsolutePath(filename) if filepath is not None: printer.selectFile(filepath, False, printAfterSelect) return jsonify(SUCCESS)
def init_connectivity_checker(settings, event_manager): from octoprint.events import Events from octoprint.util import ConnectivityChecker # start regular check if we are connected to the internet connectivityEnabled = settings.getBoolean( ["server", "onlineCheck", "enabled"]) connectivityInterval = settings.getInt( ["server", "onlineCheck", "interval"]) connectivityHost = settings.get(["server", "onlineCheck", "host"]) connectivityPort = settings.getInt(["server", "onlineCheck", "port"]) connectivityName = settings.get(["server", "onlineCheck", "name"]) def on_connectivity_change(old_value, new_value, connection_working=None, resolution_working=None): event_manager.fire(Events.CONNECTIVITY_CHANGED, payload=dict(old=old_value, new=new_value, connection=connection_working, resolution=resolution_working)) connectivityChecker = ConnectivityChecker(connectivityInterval, connectivityHost, port=connectivityPort, name=connectivityName, enabled=connectivityEnabled, on_change=on_connectivity_change) connectivityChecker.check_immediately() connectivityChecker.log_full_report() return connectivityChecker
def getConnectionOptions(): """ Retrieves the available ports, baudrates, prefered port and baudrate for connecting to the printer. """ return { "ports": comm.serialList(), "baudrates": comm.baudrateList(), "portPreference": settings.get("serial", "port"), "baudratePreference": settings.get("serial", "baudrate"), "autoconnect": settings.get("serial", "autoconnect") }
def apiPrinterState(): if not settings.get("api", "enabled"): abort(401) if not "apikey" in request.values.keys(): abort(401) if request.values["apikey"] != settings.get("api", "key"): abort(403) currentData = printer.getCurrentData() currentData.update({ "temperatures": printer.getCurrentTemperatures() }) return jsonify(currentData)
def _initSubscriptions(self, triggerType): """ Subscribes all events as defined in "events > $triggerType > subscriptions" in the settings with their respective commands. """ if triggerType in settings.disabled_events: return if triggerType not in settings.enabled_events: return eventsToSubscribe = [] for subscription in settings.get("events", triggerType + "_subscriptions"): #XXX: I have no idea how this works yet if not "event" in subscription.keys() or not "command" in subscription.keys(): self._logger.info( "Invalid %s, missing either event or command: %r" % (triggerType, subscription)) continue event = subscription["event"] command = subscription["command"] if not event in self._subscriptions.keys(): self._subscriptions[event] = [] self._subscriptions[event].append(command) if not event in eventsToSubscribe: eventsToSubscribe.append(event) self.subscribe(eventsToSubscribe)
def _initLogging(self, debug): config = { "version": 1, "formatters": { "simple": { "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" } }, "handlers": { "console": { "class": "logging.StreamHandler", "level": "DEBUG", "formatter": "simple", "stream": "ext://sys.stdout" }, "file": { "class": "logging.handlers.TimedRotatingFileHandler", "level": "DEBUG", "formatter": "simple", "when": "D", "backupCount": "1", "filename": os.path.join(settings.getpath("logs"), "octoprint.log") }, "serialFile": { "class": "logging.handlers.RotatingFileHandler", "level": "DEBUG", "formatter": "simple", # let's limit the serial log to 2MB in size "maxBytes": 2 * 1024 * 1024, "filename": os.path.join(settings.getpath("logs"), "serial.log") } }, "loggers": { #"octoprint.timelapse": { # "level": "DEBUG" #}, #"octoprint.events": { # "level": "DEBUG" #}, "SERIAL": { "level": "CRITICAL", "handlers": ["serialFile"], "propagate": False } }, "root": { "level": "INFO", "handlers": ["console", "file"] } } if debug: config["root"]["level"] = "DEBUG" logging.config.dictConfig(config) if settings.get("serial", "log"): # enable debug logging to serial.log logging.getLogger("SERIAL").setLevel(logging.DEBUG) logging.getLogger("SERIAL").debug("Enabling serial logging")
def addFile(self, file, destination, uploadCallback=None): """ Adds the given file for the given destination to the systems. Takes care of slicing if enabled and necessary. If the file's processing won't be finished directly with the return from this method but happen asynchronously in the background (e.g. due to slicing), returns a tuple containing the just added file's filename and False. Otherwise returns a tuple (filename, True). """ if not file or not destination: return None, True curaEnabled = settings.get("cura", "enabled") filename = file.filename absolutePath = self.getAbsolutePath(filename, mustExist=False) gcode = isGcodeFileName(filename) if absolutePath is None or (not curaEnabled and not gcode): return None, True file.save(absolutePath) if gcode: return self.processGcode(absolutePath, destination, uploadCallback), True else: if curaEnabled and isSTLFileName(filename): self.processStl(absolutePath, destination, uploadCallback) return filename, False
def processStl(self, absolutePath, destination, uploadCallback=None): from octoprint.slicers.cura import CuraFactory cura = CuraFactory.create_slicer() gcodePath = genGcodeFileName(absolutePath) config = settings.get("cura", "config") slicingStart = time.time() def stlProcessed(stlPath, gcodePath, error=None): if error: eventManager().fire("SlicingFailed", {"stl": self._getBasicFilename( stlPath), "gcode": self._getBasicFilename(gcodePath), "reason": error}) if os.path.exists(stlPath): os.remove(stlPath) else: slicingStop = time.time() eventManager().fire("SlicingDone", {"stl": self._getBasicFilename( stlPath), "gcode": self._getBasicFilename(gcodePath), "time": "%.2f" % (slicingStop - slicingStart)}) self.processGcode(gcodePath, destination, uploadCallback) eventManager().fire("SlicingStarted", {"stl": self._getBasicFilename( absolutePath), "gcode": self._getBasicFilename(gcodePath)}) cura.process_file( config, gcodePath, absolutePath, stlProcessed, [absolutePath, gcodePath])
def jog(): if not printer.isOperational() or printer.isPrinting(): # do not jog when a print job is running or we don't have a connection return jsonify(SUCCESS) (movementSpeedX, movementSpeedY, movementSpeedZ, movementSpeedE) = ( settings.get('movement_speed', s) for s in 'xyze') if "x" in request.values.keys(): # jog x x = request.values["x"] printer.commands(["G91", "G1 X%s F%d" % (x, movementSpeedX), "G90"]) if "y" in request.values.keys(): # jog y y = request.values["y"] printer.commands(["G91", "G1 Y%s F%d" % (y, movementSpeedY), "G90"]) if "z" in request.values.keys(): # jog z z = request.values["z"] printer.commands(["G91", "G1 Z%s F%d" % (z, movementSpeedZ), "G90"]) if "homeXY" in request.values.keys(): # home x/y printer.command("G28 X0 Y0") if "homeZ" in request.values.keys(): # home z printer.command("G28 Z0") if "extrude" in request.values.keys(): # extrude/retract length = request.values["extrude"] printer.commands( ["G91", "G1 E%s F%d" % (length, movementSpeedE), "G90"]) return jsonify(SUCCESS)
def login(): if octoprint.server.userManager is not None and "user" in request.values.keys() and "pass" in request.values.keys(): username = request.values["user"] password = request.values["pass"] if "remember" in request.values.keys() and request.values["remember"] == "true": remember = True else: remember = False user = octoprint.server.userManager.findUser(username) if user is not None: if user.check_password(octoprint.users.UserManager.createPasswordHash(password)): login_user(user, remember=remember) identity_changed.send( current_app._get_current_object(), identity=Identity(user.get_id())) return jsonify(user.asDict()) return make_response(("User unknown or password incorrect", 401, [])) elif "passive" in request.values.keys(): user = current_user if user is not None and not user.is_anonymous(): identity_changed.send( current_app._get_current_object(), identity=Identity(user.get_id())) return jsonify(user.asDict()) elif (s.get("access_control", "autologin_local") and s.get("access_control", "autologin_as") is not None and s.get("access_control", "local_networks") is not None): autologinAs = s.get("access_control", "autologin_as") localNetworks = netaddr.IPSet([]) for ip in s.get("access_control", "local_networks"): localNetworks.add(ip) try: remoteAddr = util.getRemoteAddress(request) if netaddr.IPAddress(remoteAddr) in localNetworks: user = octoprint.server.userManager.findUser(autologinAs) if user is not None: login_user(user) identity_changed.send( current_app._get_current_object(), identity=Identity(user.get_id())) return jsonify(user.asDict()) except: logger = logging.getLogger(__name__) logger.exception( "Could not autologin user %s for networks %r" % (autologinAs, localNetworks)) return jsonify(SUCCESS)
def index(): branch = None commit = None try: branch, commit = util.getGitInfo() except: pass return render_template( "index.jinja2", webcamStream=settings.get("webcam", "stream"), enableTimelapse=(settings.get("webcam", "snapshot") is not None and settings.get( "webcam", "ffmpeg") is not None), enableGCodeVisualizer=settings.get("features", "gcode_visualizer"), enableTemperatureGraph=settings.get("features", "temperature_graph"), #enableSystemMenu=settings.get(["system"]) is not None and settings.get( #["system", "actions"]) is not None and len(settings.get(["system", "actions"])) > 0, #XXX: restore this enableSystemMenu = False, enableAccessControl=userManager is not None, enableSdSupport=settings.get("features", "sd_support"), firstRun=settings.get("server", "first_run") and ( userManager is None or not userManager.hasBeenCustomized()), debug=debug, gitBranch=branch, gitCommit=commit )
def __call__(self, environ, start_response): script_name = environ.get('HTTP_X_SCRIPT_NAME', '') if not script_name: script_name = settings.get("server", "base_url") if script_name: environ['SCRIPT_NAME'] = script_name path_info = environ['PATH_INFO'] if path_info.startswith(script_name): environ['PATH_INFO'] = path_info[len(script_name):] scheme = environ.get('HTTP_X_SCHEME', '') if not scheme: scheme = settings.get("server", "scheme") if scheme: environ['wsgi.url_scheme'] = scheme return self.app(environ, start_response)
def __init__(self): UserManager.__init__(self) userfile = settings.get("access_control", "userfile") if not userfile: userfile = os.path.join(settings.settings_dir, "users.yaml") self._userfile = userfile self._users = {} self._dirty = False self._customized = None self._load()
def sdCommand(): if not settings.get("feature", "sd_support") or not printer.isOperational() or printer.isPrinting(): return jsonify(SUCCESS) if "command" in request.values.keys(): command = request.values["command"] if command == "init": printer.initSdCard() elif command == "refresh": printer.refreshSdFiles() elif command == "release": printer.releaseSdCard() return jsonify(SUCCESS)
def boxId(self): if not self._boxId: import os boxIdFile = "%s/box-id" % os.path.dirname( self._settings._configfile) if os.path.exists(boxIdFile): with open(boxIdFile, 'r') as f: self._boxId = f.read() if not self._boxId: self._boxId = settings.get(['setup', 'machineId']) if not self._boxId: raise RuntimeError('BoxId not present, please login') return self._boxId
def _getStateFlags(self): if not settings.get("features", "sd_support") or self._comm is None: sdReady = False else: sdReady = self._comm.isSdReady() return { "operational": self.isOperational(), "printing": self.isPrinting(), "closedOrError": self.isClosedOrError(), "error": self.isError(), "paused": self.isPaused(), "ready": self.isReady(), "sdReady": sdReady }
def init_connectivity_checker(settings, event_manager): from octoprint.events import Events from octoprint.util import ConnectivityChecker # start regular check if we are connected to the internet connectivityEnabled = settings.getBoolean(["server", "onlineCheck", "enabled"]) connectivityInterval = settings.getInt(["server", "onlineCheck", "interval"]) connectivityHost = settings.get(["server", "onlineCheck", "host"]) connectivityPort = settings.getInt(["server", "onlineCheck", "port"]) def on_connectivity_change(old_value, new_value): event_manager.fire(Events.CONNECTIVITY_CHANGED, payload=dict(old=old_value, new=new_value)) connectivityChecker = ConnectivityChecker(connectivityInterval, connectivityHost, port=connectivityPort, enabled=connectivityEnabled, on_change=on_connectivity_change) return connectivityChecker
def __init__(self): self._logger = logging.getLogger(__name__) self._imageNumber = None self._inTimelapse = False self._gcodeFile = None self._captureDir = settings.getpath("timelapse_tmp") self._movieDir = settings.getpath("timelapse") self._snapshotUrl = settings.get("webcam", "snapshot") self._renderThread = None self._captureMutex = threading.Lock() # subscribe events eventManager().subscribe("PrintStarted", self.onPrintStarted) eventManager().subscribe("PrintFailed", self.onPrintDone) eventManager().subscribe("PrintDone", self.onPrintDone) eventManager().subscribe("PrintResumed", self.onPrintResumed) for (event, callback) in self.eventSubscriptions(): eventManager().subscribe(event, callback)
def performSystemAction(): logger = logging.getLogger(__name__) if request.values.has_key("action"): action = request.values["action"] availableActions = s.get("system", "actions") for availableAction in availableActions: if availableAction["action"] == action: logger.info("Performing command: %s" % availableAction["command"]) try: subprocess.check_output( availableAction["command"], shell=True) except subprocess.CalledProcessError as e: logger.warn( "Command failed with return code %i: %s" % (e.returncode, e.message)) return make_response(("Command failed with return code %i: %s" % (e.returncode, e.message), 500, [])) except Exception as ex: logger.exception("Command failed") return make_response(("Command failed: %r" % ex, 500, [])) return jsonify(SUCCESS)
def init_connectivity_checker(settings, event_manager): from octoprint.events import Events from octoprint.util import ConnectivityChecker # start regular check if we are connected to the internet connectivityEnabled = settings.getBoolean(["server", "onlineCheck", "enabled"]) connectivityInterval = settings.getInt(["server", "onlineCheck", "interval"]) connectivityHost = settings.get(["server", "onlineCheck", "host"]) connectivityPort = settings.getInt(["server", "onlineCheck", "port"]) def on_connectivity_change(old_value, new_value): event_manager.fire(Events.CONNECTIVITY_CHANGED, payload=dict(old=old_value, new=new_value)) connectivityChecker = ConnectivityChecker(connectivityInterval, connectivityHost, port=connectivityPort, enabled=connectivityEnabled, on_change=on_connectivity_change) return connectivityChecker
def firstRunSetup(): if not s.get("server", "first_run"): abort(403) if "ac" in request.values.keys() and request.values["ac"] in valid_boolean_trues and \ "user" in request.values.keys() and "pass1" in request.values.keys() and \ "pass2" in request.values.keys() and request.values["pass1"] == request.values["pass2"]: # configure access control s.set("access_control", "enabled", True) octoprint.server.userManager.addUser( request.values["user"], request.values["pass1"], True, ["user", "admin"]) s.set("server", "first_run", False) elif "ac" in request.values.keys() and not request.values["ac"] in valid_boolean_trues: # disable access control s.set("access_control", "enabled", False) s.set("server", "first_run", False) loginManager.anonymous_user = octoprint.users.DummyUser principals.identity_loaders.appendleft( octoprint.users.dummy_identity_loader) s.save() return jsonify(SUCCESS)
def __init__(self): super(HackerspaceUserManager, self).__init__() self.group = settings.get("access_control", "group")
def get_plugin_blacklist(settings, connectivity_checker=None): import requests import os import time import yaml from octoprint.util import bom_aware_open from octoprint.util.version import is_octoprint_compatible logger = log.getLogger(__name__ + ".startup") if connectivity_checker is not None and not connectivity_checker.online: logger.info("We don't appear to be online, not fetching plugin blacklist") return [] def format_blacklist(entries): format_entry = lambda x: "{} ({})".format(x[0], x[1]) if isinstance(x, (list, tuple)) and len(x) == 2 \ else "{} (any)".format(x) return ", ".join(map(format_entry, entries)) def process_blacklist(entries): result = [] if not isinstance(entries, list): return result for entry in entries: if not "plugin" in entry: continue if "octoversions" in entry and not is_octoprint_compatible(*entry["octoversions"]): continue if "version" in entry: logger.debug("Blacklisted plugin: {}, version: {}".format(entry["plugin"], entry["version"])) result.append((entry["plugin"], entry["version"])) elif "versions" in entry: logger.debug("Blacklisted plugin: {}, versions: {}".format(entry["plugin"], ", ".join(entry["versions"]))) for version in entry["versions"]: result.append((entry["plugin"], version)) else: logger.debug("Blacklisted plugin: {}".format(entry["plugin"])) result.append(entry["plugin"]) return result def fetch_blacklist_from_cache(path, ttl): if not os.path.isfile(path): return None if os.stat(path).st_mtime + ttl < time.time(): return None with bom_aware_open(path, encoding="utf-8", mode="r") as f: result = yaml.safe_load(f) if isinstance(result, list): return result def fetch_blacklist_from_url(url, timeout=3, cache=None): result = [] try: r = requests.get(url, timeout=timeout) result = process_blacklist(r.json()) if cache is not None: try: with bom_aware_open(cache, encoding="utf-8", mode="w") as f: yaml.safe_dump(result, f) except: logger.info("Fetched plugin blacklist but couldn't write it to its cache file.") except: logger.info("Unable to fetch plugin blacklist from {}, proceeding without it.".format(url)) return result try: # first attempt to fetch from cache cache_path = os.path.join(settings.getBaseFolder("data"), "plugin_blacklist.yaml") ttl = settings.getInt(["server", "pluginBlacklist", "ttl"]) blacklist = fetch_blacklist_from_cache(cache_path, ttl) if blacklist is None: # no result from the cache, let's fetch it fresh url = settings.get(["server", "pluginBlacklist", "url"]) timeout = settings.getFloat(["server", "pluginBlacklist", "timeout"]) blacklist = fetch_blacklist_from_url(url, timeout=timeout, cache=cache_path) if blacklist is None: # still now result, so no blacklist blacklist = [] if blacklist: logger.info("Blacklist processing done, " "adding {} blacklisted plugin versions: {}".format(len(blacklist), format_blacklist(blacklist))) else: logger.info("Blacklist processing done") return blacklist except: logger.exception("Something went wrong while processing the plugin blacklist. Proceeding without it.")
def init_pluginsystem(settings, safe_mode=False, ignore_blacklist=True, connectivity_checker=None): """Initializes the plugin manager based on the settings.""" import os logger = log.getLogger(__name__ + ".startup") plugin_folders = [(os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "plugins")), True), settings.getBaseFolder("plugins")] plugin_entry_points = ["octoprint.plugin"] plugin_disabled_list = settings.get(["plugins", "_disabled"]) plugin_blacklist = [] if not ignore_blacklist and settings.getBoolean(["server", "pluginBlacklist", "enabled"]): plugin_blacklist = get_plugin_blacklist(settings, connectivity_checker=connectivity_checker) plugin_validators = [] if safe_mode: def validator(phase, plugin_info): if phase in ("before_import", "before_load", "before_enable"): setattr(plugin_info, "safe_mode_victim", not plugin_info.bundled) if not plugin_info.bundled: return False return True plugin_validators.append(validator) from octoprint.plugin import plugin_manager pm = plugin_manager(init=True, plugin_folders=plugin_folders, plugin_entry_points=plugin_entry_points, plugin_disabled_list=plugin_disabled_list, plugin_blacklist=plugin_blacklist, plugin_validators=plugin_validators) settings_overlays = dict() disabled_from_overlays = dict() def handle_plugin_loaded(name, plugin): if plugin.instance and hasattr(plugin.instance, "__plugin_settings_overlay__"): plugin.needs_restart = True # plugin has a settings overlay, inject it overlay_definition = getattr(plugin.instance, "__plugin_settings_overlay__") if isinstance(overlay_definition, (tuple, list)): overlay_definition, order = overlay_definition else: order = None overlay = settings.load_overlay(overlay_definition) if "plugins" in overlay and "_disabled" in overlay["plugins"]: disabled_plugins = overlay["plugins"]["_disabled"] del overlay["plugins"]["_disabled"] disabled_from_overlays[name] = (disabled_plugins, order) settings_overlays[name] = overlay logger.debug("Found settings overlay on plugin {}".format(name)) def handle_plugins_loaded(startup=False, initialize_implementations=True, force_reload=None): if not startup: return sorted_disabled_from_overlays = sorted([(key, value[0], value[1]) for key, value in disabled_from_overlays.items()], key=lambda x: (x[2] is None, x[2], x[0])) disabled_list = pm.plugin_disabled_list already_processed = [] for name, addons, _ in sorted_disabled_from_overlays: if not name in disabled_list and not name.endswith("disabled"): for addon in addons: if addon in disabled_list: continue if addon in already_processed: logger.info("Plugin {} wants to disable plugin {}, but that was already processed".format(name, addon)) if not addon in already_processed and not addon in disabled_list: disabled_list.append(addon) logger.info("Disabling plugin {} as defined by plugin {}".format(addon, name)) already_processed.append(name) def handle_plugin_enabled(name, plugin): if name in settings_overlays: settings.add_overlay(settings_overlays[name]) logger.info("Added settings overlay from plugin {}".format(name)) pm.on_plugin_loaded = handle_plugin_loaded pm.on_plugins_loaded = handle_plugins_loaded pm.on_plugin_enabled = handle_plugin_enabled pm.reload_plugins(startup=True, initialize_implementations=False) return pm
def write(self, data): if self.readList is None: return data = data.strip() # strip checksum if "*" in data: data = data[:data.rfind("*")] self.currentLine += 1 elif settings.get("virtual_printer", "force_checksum"): self.readList.append("Error: Missing checksum") return # track N = N + 1 if data.startswith("N") and "M110" in data: linenumber = int(re.search("N([0-9]+)", data).group(1)) self.lastN = linenumber self.currentLine = linenumber return elif data.startswith("N"): linenumber = int(re.search("N([0-9]+)", data).group(1)) expected = self.lastN + 1 if linenumber != expected: self.readList.append( "Error: expected line %d got %d" % (expected, linenumber)) self.readList.append("Resend:%d" % expected) if settings.get("virtual_printer", "ok_after_resend"): self.readList.append("ok") return elif self.currentLine == 100: # simulate a resend at line 100 of the last 5 lines self.lastN = 94 self.readList.append( "Error: Line Number is not Last Line Number\n") self.readList.append("rs %d\n" % (self.currentLine - 5)) if settings.get("virtual_printer", "ok_after_resend"): self.readList.append("ok") return else: self.lastN = linenumber data = data.split(None, 1)[1].strip() data += "\n" # shortcut for writing to SD if self._writingToSd and not self._selectedSdFile is None and not "M29" in data: with open(self._selectedSdFile, "a") as f: f.write(data) self._sendOk() return # print "Send: %s" % (data.rstrip()) if 'M104' in data or 'M109' in data: try: self.targetTemp = float(re.search('S([0-9]+)', data).group(1)) except: pass if 'M140' in data or 'M190' in data: try: self.bedTargetTemp = float( re.search('S([0-9]+)', data).group(1)) except: pass if 'M105' in data: # send simulated temperature data self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f @:64\n" % (self.temp, self.targetTemp, self.bedTemp, self.bedTargetTemp)) elif 'M20' in data: if self._sdCardReady: self._listSd() elif 'M21' in data: self._sdCardReady = True self.readList.append("SD card ok") elif 'M22' in data: self._sdCardReady = False elif 'M23' in data: if self._sdCardReady: filename = data.split(None, 1)[1].strip() self._selectSdFile(filename) elif 'M24' in data: if self._sdCardReady: self._startSdPrint() elif 'M25' in data: if self._sdCardReady: self._pauseSdPrint() elif 'M26' in data: if self._sdCardReady: pos = int(re.search("S([0-9]+)", data).group(1)) self._setSdPos(pos) elif 'M27' in data: if self._sdCardReady: self._reportSdStatus() elif 'M28' in data: if self._sdCardReady: filename = data.split(None, 1)[1].strip() self._writeSdFile(filename) elif 'M29' in data: if self._sdCardReady: self._finishSdFile() elif 'M30' in data: if self._sdCardReady: filename = data.split(None, 1)[1].strip() self._deleteSdFile(filename) elif "M114" in data: # send dummy position report self.readList.append("ok C: X:10.00 Y:3.20 Z:5.20 E:1.24") elif "M117" in data: # we'll just use this to echo a message, to allow playing around # with pause triggers self.readList.append( "ok %s" % re.search("M117\s+(.*)", data).group(1)) elif "M999" in data: # mirror Marlin behaviour self.readList.append("Resend: 1") elif len(data.strip()) > 0: self._sendOk()
def _sendOk(self): if settings.get("virtual_printer", "ok_with_line_number"): self.readList.append("ok %d" % self.lastN) else: self.readList.append("ok")
def init_pluginsystem(settings, safe_mode=False): """Initializes the plugin manager based on the settings.""" import os logger = log.getLogger(__name__) plugin_folders = [(os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "plugins")), True), settings.getBaseFolder("plugins")] plugin_entry_points = ["octoprint.plugin"] plugin_disabled_list = settings.get(["plugins", "_disabled"]) plugin_validators = [] if safe_mode: def validator(phase, plugin_info): if phase == "after_load": setattr(plugin_info, "safe_mode_victim", not plugin_info.bundled) setattr(plugin_info, "safe_mode_enabled", False) elif phase == "before_enable": if not plugin_info.bundled: setattr(plugin_info, "safe_mode_enabled", True) return False return True plugin_validators.append(validator) from octoprint.plugin import plugin_manager pm = plugin_manager(init=True, plugin_folders=plugin_folders, plugin_entry_points=plugin_entry_points, plugin_disabled_list=plugin_disabled_list, plugin_validators=plugin_validators) settings_overlays = dict() disabled_from_overlays = dict() def handle_plugin_loaded(name, plugin): if hasattr(plugin.instance, "__plugin_settings_overlay__"): plugin.needs_restart = True # plugin has a settings overlay, inject it overlay_definition = getattr(plugin.instance, "__plugin_settings_overlay__") if isinstance(overlay_definition, (tuple, list)): overlay_definition, order = overlay_definition else: order = None overlay = settings.load_overlay(overlay_definition) if "plugins" in overlay and "_disabled" in overlay["plugins"]: disabled_plugins = overlay["plugins"]["_disabled"] del overlay["plugins"]["_disabled"] disabled_from_overlays[name] = (disabled_plugins, order) settings_overlays[name] = overlay logger.debug("Found settings overlay on plugin {}".format(name)) def handle_plugins_loaded(startup=False, initialize_implementations=True, force_reload=None): if not startup: return sorted_disabled_from_overlays = sorted([(key, value[0], value[1]) for key, value in disabled_from_overlays.items()], key=lambda x: (x[2] is None, x[2], x[0])) disabled_list = pm.plugin_disabled_list already_processed = [] for name, addons, _ in sorted_disabled_from_overlays: if not name in disabled_list and not name.endswith("disabled"): for addon in addons: if addon in disabled_list: continue if addon in already_processed: logger.info("Plugin {} wants to disable plugin {}, but that was already processed".format(name, addon)) if not addon in already_processed and not addon in disabled_list: disabled_list.append(addon) logger.info("Disabling plugin {} as defined by plugin {} through settings overlay".format(addon, name)) already_processed.append(name) def handle_plugin_enabled(name, plugin): if name in settings_overlays: settings.add_overlay(settings_overlays[name]) logger.info("Added settings overlay from plugin {}".format(name)) pm.on_plugin_loaded = handle_plugin_loaded pm.on_plugins_loaded = handle_plugins_loaded pm.on_plugin_enabled = handle_plugin_enabled pm.reload_plugins(startup=True, initialize_implementations=False) return pm
def _createMovie(self): ffmpeg = settings.get("webcam", "ffmpeg") bitrate = settingsng_.get("webcam", "bitrate") if ffmpeg is None or bitrate is None: self._logger.warn( "Cannot create movie, path to ffmpeg or desired bitrate is unset") return input = os.path.join(self._captureDir, "tmp_%05d.jpg") output = os.path.join(self._movieDir, "%s_%s.mpg" % ( os.path.splitext(self._gcodeFile)[0], time.strftime("%Y%m%d%H%M%S"))) # prepare ffmpeg command command = [ ffmpeg, '-i', input, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', '-b:v', bitrate, '-f', 'vob'] filters = [] # flip video if configured if settings.get("webcam", "flipH"): filters.append('hflip') if settings.get("webcam", "flipV"): filters.append('vflip') # add watermark if configured watermarkFilter = None if settings.get("webcam", "watermark"): watermark = os.path.join( os.path.dirname(__file__), "static", "img", "watermark.png") if sys.platform == "win32": # Because ffmpeg hiccups on windows' drive letters and backslashes we have to give the watermark # path a special treatment. Yeah, I couldn't believe it # either... watermark = watermark.replace("\\", "/").replace(":", "\\\\:") watermarkFilter = "movie=%s [wm]; [%%(inputName)s][wm] overlay=10:main_h-overlay_h-10" % watermark filterstring = None if len(filters) > 0: if watermarkFilter is not None: filterstring = "[in] %s [postprocessed]; %s [out]" % ( ",".join(filters), watermarkFilter % {"inputName": "postprocessed"}) else: filterstring = "[in] %s [out]" % ",".join(filters) elif watermarkFilter is not None: filterstring = watermarkFilter % {"inputName": "in"} + " [out]" if filterstring is not None: self._logger.debug("Applying videofilter chain: %s" % filterstring) command.extend(["-vf", filterstring]) # finalize command with output file self._logger.debug("Rendering movie to %s" % output) command.append(output) try: subprocess.check_call(command) eventManager().fire("MovieDone", output) except subprocess.CalledProcessError as (e): self._logger.warn( "Could not render movie, got return code %r" % e.returncode)
def uploadGcodeFile(target): if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]: return make_response("Invalid target: %s" % target, 400) if "gcode_file" in request.files.keys(): file = request.files["gcode_file"] sd = target == FileDestinations.SDCARD selectAfterUpload = "select" in request.values.keys( ) and request.values["select"] in valid_boolean_trues printAfterSelect = "print" in request.values.keys( ) and request.values["print"] in valid_boolean_trues # determine current job currentFilename = None currentSd = None currentJob = printer.getCurrentJob() if currentJob is not None and "filename" in currentJob.keys() and "sd" in currentJob.keys(): currentFilename = currentJob["filename"] currentSd = currentJob["sd"] # determine future filename of file to be uploaded, abort if it can't # be uploaded futureFilename = gcodeManager.getFutureFilename(file) if futureFilename is None or (not settings.get("cura", "enabled") and not gcodefiles.isGcodeFileName(futureFilename)): return make_response("Can not upload file %s, wrong format?" % file.filename, 400) # prohibit overwriting currently selected file while it's being printed if futureFilename == currentFilename and sd == currentSd and printer.isPrinting() or printer.isPaused(): return make_response("Trying to overwrite file that is currently being printed: %s" % currentFilename, 403) filename = None def fileProcessingFinished(filename, absFilename, destination): """ Callback for when the file processing (upload, optional slicing, addition to analysis queue) has finished. Depending on the file's destination triggers either streaming to SD card or directly calls selectOrPrint. """ sd = destination == FileDestinations.SDCARD if sd: printer.addSdFile(filename, absFilename, selectAndOrPrint) else: selectAndOrPrint(absFilename, destination) def selectAndOrPrint(nameToSelect, destination): """ Callback for when the file is ready to be selected and optionally printed. For SD file uploads this only the case after they have finished streaming to the printer, which is why this callback is also used for the corresponding call to addSdFile. Selects the just uploaded file if either selectAfterUpload or printAfterSelect are True, or if the exact file is already selected, such reloading it. """ sd = destination == FileDestinations.SDCARD if selectAfterUpload or printAfterSelect or (currentFilename == filename and currentSd == sd): printer.selectFile(nameToSelect, sd, printAfterSelect) destination = FileDestinations.SDCARD if sd else FileDestinations.LOCAL filename, done = gcodeManager.addFile( file, destination, fileProcessingFinished) if filename is None: return make_response("Could not upload the file %s" % file.filename, 500) eventManager.fire("Upload", filename) return jsonify(files=gcodeManager.getAllFileData(), filename=filename, done=done) else: return make_response("No gcode_file included", 400)
def _load(self, gcodeFile): filePos = 0 pos = [0.0, 0.0, 0.0] posOffset = [0.0, 0.0, 0.0] currentE = 0.0 totalExtrusion = 0.0 maxExtrusion = 0.0 currentExtruder = 0 totalMoveTimeMinute = 0.0 absoluteE = True scale = 1.0 posAbs = True feedRateXY = settings.get("movement_speed", "x") for line in gcodeFile: if self._abort: raise AnalysisAborted() filePos += 1 try: if self.progressCallback is not None and (filePos % 1000 == 0): if isinstance(gcodeFile, (file)): self.progressCallback( float(gcodeFile.tell()) / float(self._fileSize)) elif isinstance(gcodeFile, (list)): self.progressCallback( float(filePos) / float(len(gcodeFile))) except: pass if ';' in line: comment = line[line.find(';') + 1:].strip() if comment.startswith("filament_diameter"): self._filamentDiameter = float( comment.split("=", 1)[1].strip()) elif comment.startswith("CURA_PROFILE_STRING"): curaOptions = self._parseCuraProfileString(comment) if "filament_diameter" in curaOptions: try: self._filamentDiameter = float( curaOptions["filament_diameter"]) except: self._filamentDiameter = 0.0 line = line[0:line.find(';')] T = getCodeInt(line, 'T') if T is not None: if currentExtruder > 0: posOffset[ 0] -= getPreference('extruder_offset_x%d' % (currentExtruder), 0.0) posOffset[ 1] -= getPreference('extruder_offset_y%d' % (currentExtruder), 0.0) currentExtruder = T if currentExtruder > 0: posOffset[ 0] += getPreference('extruder_offset_x%d' % (currentExtruder), 0.0) posOffset[ 1] += getPreference('extruder_offset_y%d' % (currentExtruder), 0.0) G = getCodeInt(line, 'G') if G is not None: if G == 0 or G == 1: # Move x = getCodeFloat(line, 'X') y = getCodeFloat(line, 'Y') z = getCodeFloat(line, 'Z') e = getCodeFloat(line, 'E') f = getCodeFloat(line, 'F') oldPos = pos pos = pos[:] if posAbs: if x is not None: pos[0] = x * scale + posOffset[0] if y is not None: pos[1] = y * scale + posOffset[1] if z is not None: pos[2] = z * scale + posOffset[2] else: if x is not None: pos[0] += x * scale if y is not None: pos[1] += y * scale if z is not None: pos[2] += z * scale if f is not None: feedRateXY = f if x is not None or y is not None or z is not None: diffX = oldPos[0] - pos[0] diffY = oldPos[1] - pos[1] totalMoveTimeMinute += math.sqrt( diffX * diffX + diffY * diffY) / feedRateXY moveType = 'move' if e is not None: if absoluteE: e -= currentE if e > 0.0: moveType = 'extrude' if e < 0.0: moveType = 'retract' totalExtrusion += e currentE += e if totalExtrusion > maxExtrusion: maxExtrusion = totalExtrusion else: e = 0.0 if moveType == 'move' and oldPos[2] != pos[2]: if oldPos[2] > pos[2] and abs(oldPos[2] - pos[2]) > 5.0 and pos[2] < 1.0: oldPos[2] = 0.0 elif G == 4: # Delay S = getCodeFloat(line, 'S') if S is not None: totalMoveTimeMinute += S / 60.0 P = getCodeFloat(line, 'P') if P is not None: totalMoveTimeMinute += P / 60.0 / 1000.0 elif G == 20: # Units are inches scale = 25.4 elif G == 21: # Units are mm scale = 1.0 elif G == 28: # Home x = getCodeFloat(line, 'X') y = getCodeFloat(line, 'Y') z = getCodeFloat(line, 'Z') center = [0.0, 0.0, 0.0] if x is None and y is None and z is None: pos = center else: pos = pos[:] if x is not None: pos[0] = center[0] if y is not None: pos[1] = center[1] if z is not None: pos[2] = center[2] elif G == 90: # Absolute position posAbs = True elif G == 91: # Relative position posAbs = False elif G == 92: x = getCodeFloat(line, 'X') y = getCodeFloat(line, 'Y') z = getCodeFloat(line, 'Z') e = getCodeFloat(line, 'E') if e is not None: currentE = e if x is not None: posOffset[0] = pos[0] - x if y is not None: posOffset[1] = pos[1] - y if z is not None: posOffset[2] = pos[2] - z else: M = getCodeInt(line, 'M') if M is not None: if M == 82: # Absolute E absoluteE = True elif M == 83: # Relative E absoluteE = False if self.progressCallback is not None: self.progressCallback(100.0) self.extrusionAmount = maxExtrusion self.totalMoveTimeMinute = totalMoveTimeMinute
def get_plugin_blacklist(settings, connectivity_checker=None): import requests import os import time import yaml from octoprint.util import bom_aware_open from octoprint.util.version import is_octoprint_compatible logger = log.getLogger(__name__ + ".startup") if connectivity_checker is not None and not connectivity_checker.online: logger.info("We don't appear to be online, not fetching plugin blacklist") return [] def format_blacklist(entries): format_entry = lambda x: "{} ({})".format(x[0], x[1]) if isinstance(x, (list, tuple)) and len(x) == 2 \ else "{} (any)".format(x) return ", ".join(map(format_entry, entries)) def process_blacklist(entries): result = [] if not isinstance(entries, list): return result for entry in entries: if not "plugin" in entry: continue if "octoversions" in entry and not is_octoprint_compatible(*entry["octoversions"]): continue if "version" in entry: logger.debug("Blacklisted plugin: {}, version: {}".format(entry["plugin"], entry["version"])) result.append((entry["plugin"], entry["version"])) elif "versions" in entry: logger.debug("Blacklisted plugin: {}, versions: {}".format(entry["plugin"], ", ".join(entry["versions"]))) for version in entry["versions"]: result.append((entry["plugin"], version)) else: logger.debug("Blacklisted plugin: {}".format(entry["plugin"])) result.append(entry["plugin"]) return result def fetch_blacklist_from_cache(path, ttl): if not os.path.isfile(path): return None if os.stat(path).st_mtime + ttl < time.time(): return None with bom_aware_open(path, encoding="utf-8", mode="r") as f: result = yaml.safe_load(f) if isinstance(result, list): return result def fetch_blacklist_from_url(url, timeout=3, cache=None): result = [] try: r = requests.get(url, timeout=timeout) result = process_blacklist(r.json()) if cache is not None: try: with bom_aware_open(cache, encoding="utf-8", mode="w") as f: yaml.safe_dump(result, f) except: logger.info("Fetched plugin blacklist but couldn't write it to its cache file.") except: logger.info("Unable to fetch plugin blacklist from {}, proceeding without it.".format(url)) return result try: # first attempt to fetch from cache cache_path = os.path.join(settings.getBaseFolder("data"), "plugin_blacklist.yaml") ttl = settings.getInt(["server", "pluginBlacklist", "ttl"]) blacklist = fetch_blacklist_from_cache(cache_path, ttl) if blacklist is None: # no result from the cache, let's fetch it fresh url = settings.get(["server", "pluginBlacklist", "url"]) timeout = settings.getFloat(["server", "pluginBlacklist", "timeout"]) blacklist = fetch_blacklist_from_url(url, timeout=timeout, cache=cache_path) if blacklist is None: # still now result, so no blacklist blacklist = [] if blacklist: logger.info("Blacklist processing done, " "adding {} blacklisted plugin versions: {}".format(len(blacklist), format_blacklist(blacklist))) else: logger.info("Blacklist processing done") return blacklist except: logger.exception("Something went wrong while processing the plugin blacklist. Proceeding without it.")
def decorated_view(*args, **kwargs): if settings.get("server", "first_run") and (userManager is None or not userManager.hasBeenCustomized()): return make_response("OctoPrint isn't setup yet", 403) return login_required(func)(*args, **kwargs)
def run(self): if not self._allowRoot: self._checkForRoot() global gcodeManager global userManager global eventManager global loginManager global debug from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from tornado.web import Application, FallbackHandler debug = self._debug # then initialize logging self._initLogging(self._debug) logger = logging.getLogger(__name__) eventManager = events.eventManager() gcodeManager = gcodefiles.GcodeManager() self.printer = Printer(gcodeManager) # configure timelapse octoprint.timelapse.configureTimelapse() # setup system and gcode command triggers events.SystemCommandTrigger(self.printer) events.GcodeCommandTrigger(self.printer) if self._debug: events.DebugEventListener() if settings.get("access_control", "enabled"): userManagerName = settings.get("access_control", "user_manager") try: clazz = util.getClass(userManagerName) userManager = clazz() except AttributeError as e: logger.exception( "Could not instantiate user manager %s, will run with accessControl disabled!" % userManagerName) app.wsgi_app = ReverseProxied(app.wsgi_app) app.secret_key = settings.get('server', 'secret_key') if not app.secret_key: raise NoSecretKeyError() loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user if userManager is None: loginManager.anonymous_user = users.DummyUser principals.identity_loaders.appendleft(users.dummy_identity_loader) loginManager.init_app(app) if self._host is None: self._host = settings.get("server", "host") if self._port is None: self._port = settings.get("server", "port") logger.info("Listening on http://%s:%d" % (self._host, self._port)) app.debug = self._debug from octoprint.server.ajax import ajax from octoprint.server.api import api app.register_blueprint(ajax, url_prefix="/ajax") app.register_blueprint(api, url_prefix="/api") self._router = SockJSRouter(self._createSocketConnection, "/sockjs") self._tornado_app = Application(self._router.urls + [ (r"/downloads/timelapse/([^/]*\.mpg)", LargeResponseHandler, { "path": settings.getpath("timelapse"), "as_attachment": True}), (r"/downloads/gcode/([^/]*\.(gco|gcode))", LargeResponseHandler, { "path": settings.getpath("uploads"), "as_attachment": True}), (r".*", FallbackHandler, {"fallback": WSGIContainer(app.wsgi_app)}) ]) self._server = HTTPServer(self._tornado_app) self._server.listen(self._port, address=self._host) eventManager.fire("Startup") if settings.get("serial", "autoconnect"): port, baudrate = (settings.get('serial', e) for e in ('port', 'baudrate')) connectionOptions = getConnectionOptions() if port in connectionOptions["ports"]: self.printer.connect(port, baudrate) try: IOLoop.instance().start() except: logger.fatal( "Now that is embarrassing... Something really really went" "wrong here. Please report this including the stacktrace below" "in OctoPrint's bugtracker. Thanks!") logger.exception("Stacktrace follows:")