Exemple #1
0
	def _processTemperatureQuery(self):
		includeTarget = not settings().getBoolean(["devel", "virtualPrinter", "repetierStyleTargetTemperature"])

		# send simulated temperature data
		if settings().getInt(["devel", "virtualPrinter", "numExtruders"]) > 1:
			allTemps = []
			for i in range(len(self.temp)):
				allTemps.append((i, self.temp[i], self.targetTemp[i]))
			allTempsString = " ".join(map(lambda x: "T%d:%.2f /%.2f" % x if includeTarget else "T%d:%.2f" % (x[0], x[1]), allTemps))

			if settings().getBoolean(["devel", "virtualPrinter", "hasBed"]):
				if includeTarget:
					allTempsString = "B:%.2f /%.2f %s" % (self.bedTemp, self.bedTargetTemp, allTempsString)
				else:
					allTempsString = "B:%.2f %s" % (self.bedTemp, allTempsString)

			if settings().getBoolean(["devel", "virtualPrinter", "includeCurrentToolInTemps"]):
				if includeTarget:
					self.readList.append("ok T:%.2f /%.2f %s @:64\n" % (self.temp[self.currentExtruder], self.targetTemp[self.currentExtruder] + 1, allTempsString))
				else:
					self.readList.append("ok T:%.2f %s @:64\n" % (self.temp[self.currentExtruder], allTempsString))
			else:
				self.readList.append("ok %s @:64\n" % allTempsString)
		else:
			if includeTarget:
				self.readList.append("ok T:%.2f /%.2f B:%.2f /%.2f @:64\n" % (self.temp[0], self.targetTemp[0], self.bedTemp, self.bedTargetTemp))
			else:
				self.readList.append("ok T:%.2f B:%.2f @:64\n" % (self.temp[0], self.bedTemp))
Exemple #2
0
	def __init__(self):
		self.readList = ['start\n', 'Marlin: Virtual Marlin!\n', '\x80\n', 'SD init fail\n'] # no sd card as default startup scenario
		self.currentExtruder = 0
		self.temp = [0.0] * settings().getInt(["devel", "virtualPrinter", "numExtruders"])
		self.targetTemp = [0.0] * settings().getInt(["devel", "virtualPrinter", "numExtruders"])
		self.lastTempAt = time.time()
		self.bedTemp = 1.0
		self.bedTargetTemp = 1.0

		self._virtualSd = settings().getBaseFolder("virtualSd")
		self._sdCardReady = False
		self._sdPrinter = None
		self._sdPrintingSemaphore = threading.Event()
		self._selectedSdFile = None
		self._selectedSdFileSize = None
		self._selectedSdFilePos = None
		self._writingToSd = False
		self._newSdFilePos = None
		self._heatupThread = None

		self.currentLine = 0
		self.lastN = 0

		waitThread = threading.Thread(target=self._sendWaitAfterTimeout)
		waitThread.start()
Exemple #3
0
def main():
    from optparse import OptionParser

    defaultHost = settings().get("server", "host")
    defaultPort = settings().get("server", "port")

    parser = OptionParser(usage="usage: %prog [options]")
    parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Enable debug mode")
    parser.add_option(
        "--host",
        action="store",
        type="string",
        default=defaultHost,
        dest="host",
        help="Specify the host on which to bind the server, defaults to %s if not set" % (defaultHost),
    )
    parser.add_option(
        "--port",
        action="store",
        type="int",
        default=defaultPort,
        dest="port",
        help="Specify the port on which to bind the server, defaults to %s if not set" % (defaultPort),
    )
    (options, args) = parser.parse_args()

    run(host=options.host, port=options.port, debug=options.debug)
Exemple #4
0
def uploadFirmwareFile(target):
	print("lkj uploadFirmwareFile target:%s" % str(target))
	target = FileDestinations.LOCAL
	if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]:
		return make_response("Unknown target: %s" % target, 404)
	
	print("lkj post target:%s" % str(target))
	print("lkj post request.values:%s" % str(request.values))
	print("lkj post request.files:%s" % str(request.files))
	
	input_name = "file"
	input_upload_name = input_name + "." + settings().get(["server", "uploads", "nameSuffix"])
	input_upload_path = input_name + "." + settings().get(["server", "uploads", "pathSuffix"])
	if input_upload_name in request.values and input_upload_path in request.values:
		print("lkj here 1")
		import shutil
		upload = util.Object()
		upload.filename = request.values[input_upload_name]
		upload.save = lambda new_path: shutil.move(request.values[input_upload_path], new_path)
	elif input_name in request.files:
		print("lkj here 2")
		upload = request.files[input_name]
	else:
		return make_response("No file included", 400)
	print("lkj post 2")
	# determine future filename of file to be uploaded, abort if it can't be uploaded
	try:
		futureFilename = fileManager.sanitize_name(FileDestinations.LOCAL, upload.filename)
	except:
		futureFilename = None
		
	print("lkj futureFilename:%s" % futureFilename)
	
	try :
		added_file = fileManager.add_firmware_file(FileDestinations.LOCAL, upload.filename, upload, allow_overwrite=True)
	except :
		added_file = None
	if added_file is None:
		print("lkj post added_file is None")
		return make_response("Could not upload the file %s" % upload.filename, 500)
	print("lkj added_file: %s" % added_file)
	
	files = {}
	done = True
	files.update({
		FileDestinations.LOCAL: {
			"name": added_file,
			"origin": FileDestinations.LOCAL		
		}
	})
	r = make_response(jsonify(files=files, done=done), 201)
	#lkj 
	from octoprint.server import printer
	if printer.isOperational():
		#cmd = ("M205", param=added_file)
		#printer.command()
		pass
			
	#r.headers["Location"] = added_file
	return r
Exemple #5
0
	def _parseHotendCommand(self, line):
		tool = 0
		toolMatch = re.search('T([0-9]+)', line)
		if toolMatch:
			try:
				tool = int(toolMatch.group(1))
			except:
				pass

		if tool >= settings().getInt(["devel", "virtualPrinter", "numExtruders"]):
			self._sendOk()
			return

		try:
			self.targetTemp[tool] = float(re.search('S([0-9]+)', line).group(1))
		except:
			pass

		if "M109" in line:
			self._heatupThread = threading.Thread(target=self._waitForHeatup, args=["tool%d" % tool])
			self._heatupThread.start()
			return

		self._sendOk()

		if settings().getBoolean(["devel", "virtualPrinter", "repetierStyleTargetTemperature"]):
			self.readList.append("TargetExtr%d:%d" % (tool, self.targetTemp[tool]))
Exemple #6
0
	def _initSubscriptions(self):
		"""
		Subscribes all events as defined in "events > $triggerType > subscriptions" in the settings with their
		respective commands.
		"""
		if not settings().get(["events"]):
			return

		if not settings().getBoolean(["events", "enabled"]):
			return

		eventsToSubscribe = []
		for subscription in settings().get(["events", "subscriptions"]):
			if not "event" in subscription.keys() or not "command" in subscription.keys() \
					or not "type" in subscription.keys() or not subscription["type"] in ["system", "gcode"]:
				self._logger.info("Invalid command trigger, missing either event, type or command or type is invalid: %r" % subscription)
				continue

			if "enabled" in subscription.keys() and not subscription["enabled"]:
				self._logger.info("Disabled command trigger: %r" % subscription)
				continue

			event = subscription["event"]
			command = subscription["command"]
			commandType = subscription["type"]

			if not event in self._subscriptions.keys():
				self._subscriptions[event] = []
			self._subscriptions[event].append((command, commandType))

			if not event in eventsToSubscribe:
				eventsToSubscribe.append(event)

		self.subscribe(eventsToSubscribe)
    def connect(self, port=None, baudrate=None):
        if port == None:
            port = settings().get(["serial", "port"])
        if baudrate == None:
            settings_baudrate = settings().getInt(["serial", "baudrate"])
            if settings_baudrate is None:
                baudrate = 0
            else:
                baudrate = settings_baudrate

        self._port = port
        self._baudrate = baudrate
        self._printer_uri = self._get_or_create_printer(port, baudrate)

        self._authentise_process = helpers.run_client(self._settings) #pylint: disable=no-member

        # monitoring thread
        self._monitoring_active = True
        self.monitoring_thread = threading.Thread(target=self._monitor_loop, name="comm._monitor")
        self.monitoring_thread.daemon = True
        self.monitoring_thread.start()

        self._printer_status_timer = RepeatedTimer(
            lambda: comm_helpers.get_interval("temperature", default_value=10.0),
            self._update_printer_data,
            run_first=True
        )
        self._printer_status_timer.start()

        self._change_state(PRINTER_STATE['CONNECTING'])
Exemple #8
0
def serialList():
	baselist=[]
	if os.name=="nt":
		try:
			key=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM")
			i=0
			while(1):
				baselist+=[_winreg.EnumValue(key,i)[1]]
				i+=1
		except:
			pass
	baselist = baselist \
			   + glob.glob("/dev/ttyUSB*") \
			   + glob.glob("/dev/ttyACM*") \
			   + glob.glob("/dev/ttyAMA*") \
			   + glob.glob("/dev/tty.usb*") \
			   + glob.glob("/dev/cu.*") \
			   + glob.glob("/dev/cuaU*") \
			   + glob.glob("/dev/rfcomm*")

	additionalPorts = settings().get(["serial", "additionalPorts"])
	for additional in additionalPorts:
		baselist += glob.glob(additional)

	prev = settings().get(["serial", "port"])
	if prev in baselist:
		baselist.remove(prev)
		baselist.insert(0, prev)
	if settings().getBoolean(["devel", "virtualPrinter", "enabled"]):
		baselist.append("VIRTUAL")
	return baselist
Exemple #9
0
	def __init__(self, post_roll=0, fps=25):
		self._logger = logging.getLogger(__name__)
		self._image_number = None
		self._in_timelapse = False
		self._gcode_file = None

		self._post_roll = post_roll
		self._on_post_roll_done = None

		self._capture_dir = settings().getBaseFolder("timelapse_tmp")
		self._movie_dir = settings().getBaseFolder("timelapse")
		self._snapshot_url = settings().get(["webcam", "snapshot"])
		self._ffmpeg_threads = settings().get(["webcam", "ffmpegThreads"])

		self._fps = fps

		self._render_thread = None

		self._capture_mutex = threading.Lock()
		self._capture_queue = Queue.Queue()
		self._capture_queue_active = True

		self._capture_queue_thread = threading.Thread(target=self._capture_queue_worker)
		self._capture_queue_thread.daemon = True
		self._capture_queue_thread.start()

		# subscribe events
		eventManager().subscribe(Events.PRINT_STARTED, self.on_print_started)
		eventManager().subscribe(Events.PRINT_FAILED, self.on_print_done)
		eventManager().subscribe(Events.PRINT_DONE, self.on_print_done)
		eventManager().subscribe(Events.PRINT_RESUMED, self.on_print_resumed)
		for (event, callback) in self.event_subscriptions():
			eventManager().subscribe(event, callback)
Exemple #10
0
	def run(self):
		# Global as I can't work out a way to get it into PrinterStateConnection
		global printer
		global gcodeManager

		from tornado.wsgi import WSGIContainer
		from tornado.httpserver import HTTPServer
		from tornado.ioloop import IOLoop
		from tornado.web import Application, FallbackHandler

		# first initialize the settings singleton and make sure it uses given configfile and basedir if available
		self._initSettings(self._configfile, self._basedir)

		# then initialize logging
		self._initLogging(self._debug)

		gcodeManager = gcodefiles.GcodeManager()
		printer = Printer(gcodeManager)

		if self._host is None:
			self._host = settings().get(["server", "host"])
		if self._port is None:
			self._port = settings().getInt(["server", "port"])

		logging.getLogger(__name__).info("Listening on http://%s:%d" % (self._host, self._port))
		app.debug = self._debug

		self._router = tornadio2.TornadioRouter(PrinterStateConnection)

		self._tornado_app = Application(self._router.urls + [
			(".*", FallbackHandler, {"fallback": WSGIContainer(app)})
		])
		self._server = HTTPServer(self._tornado_app)
		self._server.listen(self._port, address=self._host)
		IOLoop.instance().start()
Exemple #11
0
	def decorated_view(*args, **kwargs):
		# if OctoPrint hasn't been set up yet, abort
		if settings().getBoolean(["server", "firstRun"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()):
			return make_response("OctoPrint isn't setup yet", 403)

		# if API is globally enabled, enabled for this request and an api key is provided that is not the current UI API key, try to use that
		apikey = getApiKey(request)
		if settings().get(["api", "enabled"]) and apiEnabled and apikey is not None and apikey != octoprint.server.UI_API_KEY:
			if apikey == settings().get(["api", "key"]):
				# master key was used
				user = ApiUser()
			else:
				# user key might have been used
				user = octoprint.server.userManager.findUser(apikey=apikey)

			if user is None:
				return make_response("Invalid API key", 401)
			if login_user(user, remember=False):
				identity_changed.send(current_app._get_current_object(), identity=Identity(user.get_id()))
				return func(*args, **kwargs)

		# call regular login_required decorator
		#TODO: remove this temporary disablement of login requirement
		#return login_required(func)(*args, **kwargs)
		return func(*args, **kwargs)
Exemple #12
0
	def __call__(self, environ, start_response):
		script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
		if not script_name:
			script_name = settings().get(["server", "baseUrl"])

		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

		host = environ.get('HTTP_X_FORWARDED_HOST', '')
		if not host:
			host = settings().get(["server", "forwardedHost"])

		if host:
			environ['HTTP_HOST'] = host

		return self.app(environ, start_response)
Exemple #13
0
	def __init__(self, postRoll=0):
		self._logger = logging.getLogger(__name__)
		self._imageNumber = None
		self._inTimelapse = False
		self._gcodeFile = None

		self._postRoll = postRoll
		self._postRollStart = None
		self._onPostRollDone = None

		self._captureDir = settings().getBaseFolder("timelapse_tmp")
		self._movieDir = settings().getBaseFolder("timelapse")
		self._snapshotUrl = settings().get(["webcam", "snapshot"])

		self._fps = 25

		self._renderThread = None
		self._captureMutex = threading.Lock()

		# subscribe events
		eventManager().subscribe(Events.PRINT_STARTED, self.onPrintStarted)
		eventManager().subscribe(Events.PRINT_FAILED, self.onPrintDone)
		eventManager().subscribe(Events.PRINT_DONE, self.onPrintDone)
		eventManager().subscribe(Events.PRINT_RESUMED, self.onPrintResumed)
		for (event, callback) in self.eventSubscriptions():
			eventManager().subscribe(event, callback)
Exemple #14
0
def delete_old_unrendered_timelapses():
	basedir = settings().getBaseFolder("timelapse_tmp")
	clean_after_days = settings().getInt(["webcam", "cleanTmpAfterDays"])
	cutoff = time.time() - clean_after_days * 24 * 60 * 60

	prefixes_to_clean = []
	for filename in os.listdir(basedir):
		try:
			path = os.path.join(basedir, filename)

			prefix = _extract_prefix(filename)
			if prefix is None:
				# might be an old tmp_00000.jpg kinda frame. we can't
				# render those easily anymore, so delete that stuff
				if _old_capture_format_re.match(filename):
					os.remove(path)
				continue

			if prefix in prefixes_to_clean:
				continue

			if os.path.getmtime(path) < cutoff:
				prefixes_to_clean.append(prefix)
		except:
			logging.getLogger(__name__).exception("Error while processing file {} during cleanup".format(filename))

	for prefix in prefixes_to_clean:
		delete_unrendered_timelapse(prefix)
Exemple #15
0
	def start_print(self, pos=None):
		"""
		 Starts the currently loaded print job.
		 Only starts if the printer is connected and operational, not currently printing and a printjob is loaded
		"""
		if self._comm is None or not self._comm.isOperational() or self._comm.isPrinting():
			return
		if self._selectedFile is None:
			return

		# we are happy if the average of the estimates stays within 60s of the prior one
		threshold = settings().getFloat(["estimation", "printTime", "stableThreshold"])
		rolling_window = None
		countdown = None

		if self._selectedFile["sd"]:
			# we are interesting in a rolling window of roughly the last 15s, so the number of entries has to be derived
			# by that divided by the sd status polling interval
			rolling_window = 15 / settings().get(["serial", "timeout", "sdStatus"])

			# we are happy when one rolling window has been stable
			countdown = rolling_window
		self._timeEstimationData = TimeEstimationHelper(rolling_window=rolling_window,
		                                                threshold=threshold,
		                                                countdown=countdown)

		self._fileManager.delete_recovery_data()

		self._lastProgressReport = None
		self._setProgressData(completion=0)
		self._setCurrentZ(None)
		self._comm.startPrint(pos=pos)
Exemple #16
0
	def _createMovie(self, success=True):
		ffmpeg = settings().get(["webcam", "ffmpeg"])
		bitrate = settings().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")
		if success:
			output = os.path.join(self._movieDir, "%s_%s.mpg" % (os.path.splitext(self._gcodeFile)[0], time.strftime("%Y%m%d%H%M%S")))
		else:
			output = os.path.join(self._movieDir, "%s_%s-failed.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', str(self._fps), '-y', '-b:v', bitrate,
			'-f', 'vob']

		filters = []

		# flip video if configured
		if settings().getBoolean(["webcam", "flipH"]):
			filters.append('hflip')
		if settings().getBoolean(["webcam", "flipV"]):
			filters.append('vflip')

		# add watermark if configured
		watermarkFilter = None
		if settings().getBoolean(["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)
		eventManager().fire(Events.MOVIE_RENDERING, {"gcode": self._gcodeFile, "movie": output, "movie_basename": os.path.basename(output)})
		try:
			subprocess.check_call(command)
			eventManager().fire(Events.MOVIE_DONE, {"gcode": self._gcodeFile, "movie": output, "movie_basename": os.path.basename(output)})
		except subprocess.CalledProcessError as (e):
			self._logger.warn("Could not render movie, got return code %r" % e.returncode)
			eventManager().fire(Events.MOVIE_FAILED, {"gcode": self._gcodeFile, "movie": output, "movie_basename": os.path.basename(output), "returncode": e.returncode})
Exemple #17
0
def passive_login():
	if octoprint.server.userManager is not None:
		user = octoprint.server.userManager.login_user(flask.ext.login.current_user)
	else:
		user = flask.ext.login.current_user

	if user is not None and not user.is_anonymous():
		flask.g.user = user
		flask.ext.principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask.ext.principal.Identity(user.get_id()))
		return flask.jsonify(user.asDict())
	elif settings().getBoolean(["accessControl", "autologinLocal"]) \
			and settings().get(["accessControl", "autologinAs"]) is not None \
			and settings().get(["accessControl", "localNetworks"]) is not None:

		autologinAs = settings().get(["accessControl", "autologinAs"])
		localNetworks = netaddr.IPSet([])
		for ip in settings().get(["accessControl", "localNetworks"]):
			localNetworks.add(ip)

		try:
			remoteAddr = get_remote_address(flask.request)
			if netaddr.IPAddress(remoteAddr) in localNetworks:
				user = octoprint.server.userManager.findUser(autologinAs)
				if user is not None:
					flask.g.user = user
					flask.ext.login.login_user(user)
					flask.ext.principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask.ext.principal.Identity(user.get_id()))
					return flask.jsonify(user.asDict())
		except:
			logger = logging.getLogger(__name__)
			logger.exception("Could not autologin user %s for networks %r" % (autologinAs, localNetworks))

	return ("", 204)
Exemple #18
0
	def _createMovie(self):
		ffmpeg = settings().get(["webcam", "ffmpeg"])
		bitrate = settings().get(["webcam", "bitrate"])
		if ffmpeg is None or bitrate is None:
			self._logger.warn("Cannot create movie, path to ffmpeg 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']

		# add watermark if configured
		if settings().getBoolean(["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(":", "\\\\:")
			command.extend(['-vf', 'movie=%s [wm]; [in][wm] overlay=10:main_h-overlay_h-10 [out]' % watermark])

		# finalize command with output file
		command.append(output)
		subprocess.call(command)
		self._logger.debug("Rendering movie to %s" % output)
Exemple #19
0
	def run(self):
		# Global as I can't work out a way to get it into PrinterStateConnection
		global printer
		global gcodeManager
		global userManager

		from tornado.wsgi import WSGIContainer
		from tornado.httpserver import HTTPServer
		from tornado.ioloop import IOLoop
		from tornado.web import Application, FallbackHandler

		# first initialize the settings singleton and make sure it uses given configfile and basedir if available
		self._initSettings(self._configfile, self._basedir)

		# then initialize logging
		self._initLogging(self._debug)
		logger = logging.getLogger(__name__)

		gcodeManager = gcodefiles.GcodeManager()
		printer = Printer(gcodeManager)

		if settings().getBoolean(["accessControl", "enabled"]):
			userManagerName = settings().get(["accessControl", "userManager"])
			try:
				clazz = util.getClass(userManagerName)
				userManager = clazz()
			except AttributeError, e:
				logger.exception("Could not instantiate user manager %s, will run with accessControl disabled!" % userManagerName)
Exemple #20
0
	def set_default(self, identifier):
		all_identifiers = self._load_all_identifiers().keys()
		if identifier is not None and not identifier in all_identifiers:
			return

		settings().set(["printerProfile", "default"], identifier)
		settings().save()
Exemple #21
0
	def _initSubscriptions(self, triggerType):
		"""
		Subscribes all events as defined in "events > $triggerType > subscriptions" in the settings with their
		respective commands.
		"""
		if not settings().get(["events", triggerType]):
			return

		if not settings().getBoolean(["events", triggerType, "enabled"]):
			return

		eventsToSubscribe = []
		for subscription in settings().get(["events", triggerType, "subscriptions"]):
			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)
Exemple #22
0
	def run(self):
		if not self._allowRoot:
			self._checkForRoot()

		global userManager
		global eventManager
		global loginManager
		global debug
		global softwareManager
		global VERSION

		from tornado.wsgi import WSGIContainer
		from tornado.httpserver import HTTPServer
		from tornado.ioloop import IOLoop
		from tornado.web import Application, FallbackHandler

		from astroprint.printfiles.watchdogs import UploadCleanupWatchdogHandler

		debug = self._debug

		# first initialize the settings singleton and make sure it uses given configfile and basedir if available
		self._initSettings(self._configfile, self._basedir)
		s = settings()

		# then initialize logging
		self._initLogging(self._debug, self._logConf)
		logger = logging.getLogger(__name__)

		if s.getBoolean(["accessControl", "enabled"]):
			userManagerName = settings().get(["accessControl", "userManager"])
			try:
				clazz = util.getClass(userManagerName)
				userManager = clazz()
			except AttributeError, e:
				logger.exception("Could not instantiate user manager %s, will run with accessControl disabled!" % userManagerName)
Exemple #23
0
def delete_old_unrendered_timelapses():
	global _cleanup_lock

	basedir = settings().getBaseFolder("timelapse_tmp")
	clean_after_days = settings().getInt(["webcam", "cleanTmpAfterDays"])
	cutoff = time.time() - clean_after_days * 24 * 60 * 60

	prefixes_to_clean = []

	with _cleanup_lock:
		for entry in scandir(basedir):
			try:
				prefix = _extract_prefix(entry.name)
				if prefix is None:
					# might be an old tmp_00000.jpg kinda frame. we can't
					# render those easily anymore, so delete that stuff
					if _old_capture_format_re.match(entry.name):
						os.remove(entry.path)
					continue

				if prefix in prefixes_to_clean:
					continue

				# delete if both creation and modification time are older than the cutoff
				if max(entry.stat().st_ctime, entry.stat().st_mtime) < cutoff:
					prefixes_to_clean.append(prefix)
			except:
				if logging.getLogger(__name__).isEnabledFor(logging.DEBUG):
					logging.getLogger(__name__).exception("Error while processing file {} during cleanup".format(entry.name))

		for prefix in prefixes_to_clean:
			delete_unrendered_timelapse(prefix)
			logging.getLogger(__name__).info("Deleted old unrendered timelapse {}".format(prefix))
Exemple #24
0
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"]
	filename = gcodeManager.addFile(file)
	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)
Exemple #25
0
def plugin_manager(init=False, plugin_folders=None, plugin_types=None, plugin_entry_points=None, plugin_disabled_list=None):
	global _instance
	if _instance is None:
		if init:
			if plugin_folders is None:
				plugin_folders = (settings().getBaseFolder("plugins"), os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "plugins")))
			if plugin_types is None:
				plugin_types = [StartupPlugin,
				                ShutdownPlugin,
				                TemplatePlugin,
				                SettingsPlugin,
				                SimpleApiPlugin,
				                AssetPlugin,
				                BlueprintPlugin,
				                EventHandlerPlugin,
				                SlicerPlugin,
				                AppPlugin,
				                ProgressPlugin]
			if plugin_entry_points is None:
				plugin_entry_points = "octoprint.plugin"
			if plugin_disabled_list is None:
				all_plugin_settings = settings().get(["plugins"])
				plugin_disabled_list = []
				for key in all_plugin_settings:
					if "enabled" in all_plugin_settings[key] and not all_plugin_settings[key]:
						plugin_disabled_list.append(key)

			_instance = PluginManager(plugin_folders, plugin_types, plugin_entry_points, plugin_disabled_list=plugin_disabled_list)
		else:
			raise ValueError("Plugin Manager not initialized yet")
	return _instance
Exemple #26
0
def get_user_for_authorization_header(header):
	if not settings().getBoolean(["accessControl", "trustBasicAuthentication"]):
		return None

	if header is None:
		return None

	if not header.startswith("Basic "):
		# we currently only support Basic Authentication
		return None

	header = header.replace('Basic ', '', 1)
	try:
		header = base64.b64decode(header)
	except TypeError:
		return None

	name, password = header.split(':', 1)
	if not octoprint.server.userManager.enabled:
		return None

	user = octoprint.server.userManager.findUser(userid=name)
	if settings().getBoolean(["accessControl", "checkBasicAuthenticationPassword"]) \
			and not octoprint.server.userManager.checkPassword(name, password):
		# password check enabled and password don't match
		return None

	return user
Exemple #27
0
def configureTimelapse(config=None, persist=False):
	global current

	if config is None:
		config = settings().get(["webcam", "timelapse"])

	if current is not None:
		current.unload()

	type = config["type"]

	postRoll = 0
	if "postRoll" in config and config["postRoll"] >= 0:
		postRoll = config["postRoll"]

	fps = 25
	if "fps" in config and config["fps"] > 0:
		fps = config["fps"]

	if type is None or "off" == type:
		current = None
	elif "zchange" == type:
		current = ZTimelapse(post_roll=postRoll, fps=fps)
	elif "timed" == type:
		interval = 10
		if "options" in config and "interval" in config["options"] and config["options"]["interval"] > 0:
			interval = config["options"]["interval"]
		current = TimedTimelapse(post_roll=postRoll, interval=interval, fps=fps)

	notifyCallbacks(current)

	if persist:
		settings().set(["webcam", "timelapse"], config)
		settings().save()
Exemple #28
0
def cameraManager():
	global _instance
	if _instance is None:
		if platform == "linux" or platform == "linux2":
			number_of_video_device = 0 #/dev/video``0´´

			manager = settings().get(['camera', 'manager'])

			if manager == 'gstreamer':
				try:
					from astroprint.camera.v4l2.gstreamer import GStreamerManager
					_instance = GStreamerManager(number_of_video_device)

				except ImportError, ValueError:
					#another manager was selected or the gstreamer library is not present on this 
					#system, in that case we pick a mjpeg manager

					_instance = None
					s = settings()
					s.set(['camera', 'manager'], 'mjpeg')
					s.save()

			if _instance is None:
				from astroprint.camera.v4l2.mjpeg import MjpegManager
				_instance = MjpegManager(number_of_video_device)

		elif platform == "darwin":
			from astroprint.camera.mac import CameraMacManager
			_instance = CameraMacManager()
Exemple #29
0
	def _generateTemperatureOutput(self):
		includeTarget = not settings().getBoolean(["devel", "virtualPrinter", "repetierStyleTargetTemperature"])

		# send simulated temperature data
		if self.temperatureCount > 1:
			allTemps = []
			for i in range(len(self.temp)):
				allTemps.append((i, self.temp[i], self.targetTemp[i]))
			allTempsString = " ".join(map(lambda x: "T%d:%.2f /%.2f" % x if includeTarget else "T%d:%.2f" % (x[0], x[1]), allTemps))

			if settings().getBoolean(["devel", "virtualPrinter", "smoothieTemperatureReporting"]):
				allTempsString = allTempsString.replace("T0:", "T:")

			if settings().getBoolean(["devel", "virtualPrinter", "hasBed"]):
				if includeTarget:
					allTempsString = "B:%.2f /%.2f %s" % (self.bedTemp, self.bedTargetTemp, allTempsString)
				else:
					allTempsString = "B:%.2f %s" % (self.bedTemp, allTempsString)

			if settings().getBoolean(["devel", "virtualPrinter", "includeCurrentToolInTemps"]):
				if includeTarget:
					output = "T:%.2f /%.2f %s" % (self.temp[self.currentExtruder], self.targetTemp[self.currentExtruder], allTempsString)
				else:
					output = "T:%.2f %s" % (self.temp[self.currentExtruder], allTempsString)
			else:
				output = allTempsString
		else:
			if includeTarget:
				output = "T:%.2f /%.2f B:%.2f /%.2f" % (self.temp[0], self.targetTemp[0], self.bedTemp, self.bedTargetTemp)
			else:
				output = "T:%.2f B:%.2f" % (self.temp[0], self.bedTemp)

		output += " @:64\n"
		return output
Exemple #30
0
	def default_view():
		wizard = wizard_active(_templates[locale])
		enable_accesscontrol = userManager.enabled
		accesscontrol_active = enable_accesscontrol and userManager.hasBeenCustomized()
		render_kwargs.update(dict(
			webcamStream=settings().get(["webcam", "stream"]),
			enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]),
			enableAccessControl=enable_accesscontrol,
			accessControlActive=accesscontrol_active,
			enableSdSupport=settings().get(["feature", "sdSupport"]),
			gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
			gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
			wizard=wizard,
			now=now,
		))

		# no plugin took an interest, we'll use the default UI
		def make_default_ui():
			r = make_response(render_template("index.jinja2", **render_kwargs))
			if wizard:
				# if we have active wizard dialogs, set non caching headers
				r = util.flask.add_non_caching_response_headers(r)
			return r

		cached = get_cached_view("_default",
		                         make_default_ui)
		preemptively_cached = get_preemptively_cached_view("_default",
		                                                   cached,
		                                                   dict(),
		                                                   dict())
		return preemptively_cached()
Exemple #31
0
 def __init__(self):
     self.settings = settings()
     self.sslManager = SslManager()
Exemple #32
0
class Server():
    def __init__(self,
                 configfile=None,
                 basedir=None,
                 host="0.0.0.0",
                 port=5000,
                 debug=False,
                 allowRoot=False):
        self._configfile = configfile
        self._basedir = basedir
        self._host = host
        self._port = port
        self._debug = debug
        self._allowRoot = allowRoot

    def run(self):
        if not self._allowRoot:
            self._checkForRoot()

        global printer
        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

        # first initialize the settings singleton and make sure it uses given configfile and basedir if available
        self._initSettings(self._configfile, self._basedir)

        # then initialize logging
        self._initLogging(self._debug)
        logger = logging.getLogger(__name__)

        eventManager = events.eventManager()
        gcodeManager = gcodefiles.GcodeManager()
        printer = Printer(gcodeManager)

        # configure timelapse
        octoprint.timelapse.configureTimelapse()

        # setup system and gcode command triggers
        events.SystemCommandTrigger(printer)
        events.GcodeCommandTrigger(printer)
        if self._debug:
            events.DebugEventListener()

        if settings().getBoolean(["accessControl", "enabled"]):
            userManagerName = settings().get(["accessControl", "userManager"])
            try:
                clazz = util.getClass(userManagerName)
                userManager = clazz()
            except AttributeError, 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 = "k3PuVYgtxNm8DXKKTw2nWmFQQun9qceV"
        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().getInt(["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().getBaseFolder("timelapse"),
                "as_attachment": True
            }),
             (r"/downloads/gcode/([^/]*\.(gco|gcode))", LargeResponseHandler, {
                 "path": settings().getBaseFolder("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().getBoolean(["serial", "autoconnect"]):
            (port, baudrate) = settings().get(
                ["serial", "port"]), settings().getInt(["serial", "baudrate"])
            connectionOptions = getConnectionOptions()
            if port in connectionOptions["ports"]:
                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:")
Exemple #33
0
    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().getBaseFolder("logs"),
                                 "octoprint.log")
                },
                "serialFile": {
                    "class":
                    "logging.handlers.RotatingFileHandler",
                    "level":
                    "DEBUG",
                    "formatter":
                    "simple",
                    "maxBytes":
                    2 * 1024 *
                    1024,  # let's limit the serial log to 2MB in size
                    "filename":
                    os.path.join(settings().getBaseFolder("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().getBoolean(["serial", "log"]):
            # enable debug logging to serial.log
            logging.getLogger("SERIAL").setLevel(logging.DEBUG)
            logging.getLogger("SERIAL").debug("Enabling serial logging")
Exemple #34
0
def getInstalledLanguagePacks():
    translation_folder = settings().getBaseFolder("translations")
    if not os.path.exists(translation_folder):
        return jsonify(language_packs=dict(_core=[]))

    core_packs = []
    plugin_packs = defaultdict(
        lambda: dict(identifier=None, display=None, languages=[]))
    for entry in scandir(translation_folder):
        if not entry.is_dir():
            continue

        def load_meta(path, locale):
            meta = dict()

            meta_path = os.path.join(path, "meta.yaml")
            if os.path.isfile(meta_path):
                import yaml
                try:
                    with open(meta_path) as f:
                        meta = yaml.safe_load(f)
                except:
                    pass
                else:
                    import datetime
                    if "last_update" in meta and isinstance(
                            meta["last_update"], datetime.datetime):
                        meta["last_update"] = (
                            meta["last_update"] -
                            datetime.datetime(1970, 1, 1)).total_seconds()

            l = Locale.parse(locale)
            meta["locale"] = locale
            meta["locale_display"] = l.display_name
            meta["locale_english"] = l.english_name
            return meta

        if entry.name == "_plugins":
            for plugin_entry in scandir(entry.path):
                if not plugin_entry.is_dir():
                    continue

                if not plugin_entry.name in plugin_manager().plugins:
                    continue

                plugin_info = plugin_manager().plugins[plugin_entry.name]

                plugin_packs[
                    plugin_entry.name]["identifier"] = plugin_entry.name
                plugin_packs[plugin_entry.name]["display"] = plugin_info.name

                for language_entry in scandir(plugin_entry.path):
                    plugin_packs[plugin_entry.name]["languages"].append(
                        load_meta(language_entry.path, language_entry.name))
        else:
            core_packs.append(load_meta(entry.path, entry.name))

    result = dict(
        _core=dict(identifier="_core", display="Core", languages=core_packs))
    result.update(plugin_packs)
    return jsonify(language_packs=result)
Exemple #35
0
def setSettings():
    if "application/json" in request.headers["Content-Type"]:
        data = request.json
        s = settings()

        if "api" in data.keys():
            if "enabled" in data["api"].keys():
                s.set(["api", "enabled"], data["api"]["enabled"])
            if "key" in data["api"].keys():
                s.set(["api", "key"], data["api"]["key"], True)

        if "appearance" in data.keys():
            if "name" in data["appearance"].keys():
                s.set(["appearance", "name"], data["appearance"]["name"])
            if "color" in data["appearance"].keys():
                s.set(["appearance", "color"], data["appearance"]["color"])

        if "printer" in data.keys():
            if "movementSpeedX" in data["printer"].keys():
                s.setInt(["printerParameters", "movementSpeed", "x"],
                         data["printer"]["movementSpeedX"])
            if "movementSpeedY" in data["printer"].keys():
                s.setInt(["printerParameters", "movementSpeed", "y"],
                         data["printer"]["movementSpeedY"])
            if "movementSpeedZ" in data["printer"].keys():
                s.setInt(["printerParameters", "movementSpeed", "z"],
                         data["printer"]["movementSpeedZ"])
            if "movementSpeedE" in data["printer"].keys():
                s.setInt(["printerParameters", "movementSpeed", "e"],
                         data["printer"]["movementSpeedE"])

        if "webcam" in data.keys():
            if "streamUrl" in data["webcam"].keys():
                s.set(["webcam", "stream"], data["webcam"]["streamUrl"])
            if "snapshotUrl" in data["webcam"].keys():
                s.set(["webcam", "snapshot"], data["webcam"]["snapshotUrl"])
            if "ffmpegPath" in data["webcam"].keys():
                s.set(["webcam", "ffmpeg"], data["webcam"]["ffmpegPath"])
            if "bitrate" in data["webcam"].keys():
                s.set(["webcam", "bitrate"], data["webcam"]["bitrate"])
            if "watermark" in data["webcam"].keys():
                s.setBoolean(["webcam", "watermark"],
                             data["webcam"]["watermark"])
            if "flipH" in data["webcam"].keys():
                s.setBoolean(["webcam", "flipH"], data["webcam"]["flipH"])
            if "flipV" in data["webcam"].keys():
                s.setBoolean(["webcam", "flipV"], data["webcam"]["flipV"])

        if "feature" in data.keys():
            if "gcodeViewer" in data["feature"].keys():
                s.setBoolean(["feature", "gCodeVisualizer"],
                             data["feature"]["gcodeViewer"])
            if "waitForStart" in data["feature"].keys():
                s.setBoolean(["feature", "waitForStartOnConnect"],
                             data["feature"]["waitForStart"])
            if "alwaysSendChecksum" in data["feature"].keys():
                s.setBoolean(["feature", "alwaysSendChecksum"],
                             data["feature"]["alwaysSendChecksum"])
            if "sdSupport" in data["feature"].keys():
                s.setBoolean(["feature", "sdSupport"],
                             data["feature"]["sdSupport"])

        if "serial" in data.keys():
            if "autoconnect" in data["serial"].keys():
                s.setBoolean(["serial", "autoconnect"],
                             data["serial"]["autoconnect"])
            if "port" in data["serial"].keys():
                s.set(["serial", "port"], data["serial"]["port"])
            if "baudrate" in data["serial"].keys():
                s.setInt(["serial", "baudrate"], data["serial"]["baudrate"])
            if "timeoutConnection" in data["serial"].keys():
                s.setFloat(["serial", "timeout", "connection"],
                           data["serial"]["timeoutConnection"])
            if "timeoutDetection" in data["serial"].keys():
                s.setFloat(["serial", "timeout", "detection"],
                           data["serial"]["timeoutDetection"])
            if "timeoutCommunication" in data["serial"].keys():
                s.setFloat(["serial", "timeout", "communication"],
                           data["serial"]["timeoutCommunication"])

            oldLog = s.getBoolean(["serial", "log"])
            if "log" in data["serial"].keys():
                s.setBoolean(["serial", "log"], data["serial"]["log"])
            if oldLog and not s.getBoolean(["serial", "log"]):
                # disable debug logging to serial.log
                logging.getLogger("SERIAL").debug("Disabling serial logging")
                logging.getLogger("SERIAL").setLevel(logging.CRITICAL)
            elif not oldLog and s.getBoolean(["serial", "log"]):
                # enable debug logging to serial.log
                logging.getLogger("SERIAL").setLevel(logging.DEBUG)
                logging.getLogger("SERIAL").debug("Enabling serial logging")

        if "folder" in data.keys():
            if "uploads" in data["folder"].keys():
                s.setBaseFolder("uploads", data["folder"]["uploads"])
            if "timelapse" in data["folder"].keys():
                s.setBaseFolder("timelapse", data["folder"]["timelapse"])
            if "timelapseTmp" in data["folder"].keys():
                s.setBaseFolder("timelapse_tmp",
                                data["folder"]["timelapseTmp"])
            if "logs" in data["folder"].keys():
                s.setBaseFolder("logs", data["folder"]["logs"])

        if "temperature" in data.keys():
            if "profiles" in data["temperature"].keys():
                s.set(["temperature", "profiles"],
                      data["temperature"]["profiles"])

        if "system" in data.keys():
            if "actions" in data["system"].keys():
                s.set(["system", "actions"], data["system"]["actions"])
            if "events" in data["system"].keys():
                s.set(["system", "events"], data["system"]["events"])
        s.save()

    return getSettings()
Exemple #36
0
 def is_sd_ready(self):
     if not settings().getBoolean(["feature", "sdSupport"
                                   ]) or self._comm is None:
         return False
     else:
         return self._comm.isSdReady()
Exemple #37
0
    def __init__(self, fileManager, analysisQueue, printerProfileManager):
        from collections import deque

        self._logger = logging.getLogger(__name__)

        self._analysisQueue = analysisQueue
        self._fileManager = fileManager
        self._printerProfileManager = printerProfileManager

        # state
        # TODO do we really need to hold the temperature here?
        self._temp = None
        self._bedTemp = None
        self._targetTemp = None
        self._targetBedTemp = None
        self._temps = TemperatureHistory(
            cutoff=settings().getInt(["temperature", "cutoff"]) * 60)
        self._tempBacklog = []

        self._latestMessage = None
        self._messages = deque([], 300)
        self._messageBacklog = []

        self._latestLog = None
        self._log = deque([], 300)
        self._logBacklog = []

        self._state = None

        self._currentZ = None

        self._progress = None
        self._printTime = None
        self._printTimeLeft = None

        self._printAfterSelect = False

        # sd handling
        self._sdPrinting = False
        self._sdStreaming = False
        self._sdFilelistAvailable = threading.Event()
        self._streamingFinishedCallback = None

        self._selectedFile = None
        self._timeEstimationData = None

        # comm
        self._comm = None

        # callbacks
        self._callbacks = []

        # progress plugins
        self._lastProgressReport = None
        self._progressPlugins = plugin_manager().get_implementations(
            ProgressPlugin)

        self._stateMonitor = StateMonitor(
            interval=0.5,
            on_update=self._sendCurrentDataCallbacks,
            on_add_temperature=self._sendAddTemperatureCallbacks,
            on_add_log=self._sendAddLogCallbacks,
            on_add_message=self._sendAddMessageCallbacks)
        self._stateMonitor.reset(state={
            "text": self.get_state_string(),
            "flags": self._getStateFlags()
        },
                                 job_data={
                                     "file": {
                                         "name": None,
                                         "size": None,
                                         "origin": None,
                                         "date": None
                                     },
                                     "estimatedPrintTime": None,
                                     "lastPrintTime": None,
                                     "filament": {
                                         "length": None,
                                         "volume": None
                                     }
                                 },
                                 progress={
                                     "completion": None,
                                     "filepos": None,
                                     "printTime": None,
                                     "printTimeLeft": None
                                 },
                                 current_z=None)

        eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED,
                                 self._on_event_MetadataAnalysisFinished)
        eventManager().subscribe(Events.METADATA_STATISTICS_UPDATED,
                                 self._on_event_MetadataStatisticsUpdated)
Exemple #38
0
def connectionCommand():
    valid_commands = {"connect": [], "disconnect": []}

    command, data, response = get_json_command_from_request(
        request, valid_commands)
    if response is not None:
        return response

    if command == "connect":
        connection_options = get_connection_options()

        port = None
        port1 = None
        projector = None
        baudrate = None
        printerProfile = None
        if "port" in data.keys():
            port = data["port"]
            if port not in connection_options["ports"]:
                return make_response("Invalid port: %s" % port, 400)
        if "baudrate" in data.keys():
            baudrate = data["baudrate"]
            if baudrate not in connection_options["baudrates"]:
                return make_response("Invalid baudrate: %d" % baudrate, 400)
        if "printerProfile" in data.keys():
            printerProfile = data["printerProfile"]
            if not printerProfileManager.exists(printerProfile):
                return make_response(
                    "Invalid printer profile: %s" % printerProfile, 400)
        if "save" in data.keys() and data["save"]:
            port1 = data["port1"]
            projector = data["projector"]
            settings().set(["serial", "port"], port)
            settings().set(["serial", "port1"], port1)
            settings().set(["serial", "projector"], projector)
            settings().setInt(["serial", "baudrate"], baudrate)
            printerProfileManager.set_default(printerProfile)
        if "autoconnect" in data.keys():
            settings().setBoolean(["serial", "autoconnect"],
                                  data["autoconnect"])
        settings().save()
        printer.connect(port=port, baudrate=baudrate, profile=printerProfile)
    elif command == "disconnect":
        printer.disconnect()

    return NO_CONTENT
Exemple #39
0
	def _load(self, gcodeFile, printer_profile, throttle=None):
		filePos = 0
		readBytes = 0
		pos = Vector3D(0.0, 0.0, 0.0)
		posOffset = Vector3D(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
		fwretractTime = 0
		fwretractDist = 0
		fwrecoverTime = 0
		feedrate = min(printer_profile["axes"]["x"]["speed"], printer_profile["axes"]["y"]["speed"])
		if feedrate == 0:
			# some somewhat sane default if axes speeds are insane...
			feedrate = 2000
		offsets = printer_profile["extruder"]["offsets"]

		for line in gcodeFile:
			if self._abort:
				raise AnalysisAborted()
			filePos += 1
			readBytes += len(line)

			if isinstance(gcodeFile, (file)):
				percentage = float(readBytes) / float(self._fileSize)
			elif isinstance(gcodeFile, (list)):
				percentage = float(filePos) / float(len(gcodeFile))
			else:
				percentage = None

			try:
				if self.progressCallback is not None and (filePos % 1000 == 0) and percentage is not None:
					self.progressCallback(percentage)
			except:
				pass

			if ';' in line:
				comment = line[line.find(';')+1:].strip()
				if comment.startswith("filament_diameter"):
					filamentValue = comment.split("=", 1)[1].strip()
					try:
						self._filamentDiameter = float(filamentValue)
					except ValueError:
						try:
							self._filamentDiameter = float(filamentValue.split(",")[0].strip())
						except ValueError:
							self._filamentDiameter = 0.0
				elif comment.startswith("CURA_PROFILE_STRING") or comment.startswith("CURA_OCTO_PROFILE_STRING"):
					if comment.startswith("CURA_PROFILE_STRING"):
						prefix = "CURA_PROFILE_STRING:"
					else:
						prefix = "CURA_OCTO_PROFILE_STRING:"

					curaOptions = self._parseCuraProfileString(comment, prefix)
					if "filament_diameter" in curaOptions:
						try:
							self._filamentDiameter = float(curaOptions["filament_diameter"])
						except:
							self._filamentDiameter = 0.0
				line = line[0:line.find(';')]

			G = getCodeInt(line, 'G')
			M = getCodeInt(line, 'M')
			T = getCodeInt(line, 'T')

			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
					newPos = Vector3D(x if x is not None else pos.x,
					                  y if y is not None else pos.y,
					                  z if z is not None else pos.z)

					if posAbs:
						pos = newPos * scale + posOffset
					else:
						pos += newPos * scale
					if f is not None and f != 0:
						feedrate = f

					if e is not None:
						if absoluteE:
							# make sure e is relative
							e -= currentE[currentExtruder]
						# If move includes extrusion, calculate new min/max coordinates of model
						if e > 0.0:
							# extrusion -> relevant for print area & dimensions
							self._minMax.record(pos)
						totalExtrusion[currentExtruder] += e
						currentE[currentExtruder] += e
						maxExtrusion[currentExtruder] = max(maxExtrusion[currentExtruder],
						                                    totalExtrusion[currentExtruder])
					else:
						e = 0.0

					# move time in x, y, z, will be 0 if no movement happened
					moveTimeXYZ = abs((oldPos - pos).length / feedrate)

					# time needed for extruding, will be 0 if no extrusion happened
					extrudeTime = abs(e / feedrate)

					# time to add is maximum of both
					totalMoveTimeMinute += max(moveTimeXYZ, extrudeTime)

				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 == 10:   #Firmware retract
					totalMoveTimeMinute += fwretractTime
				elif G == 11:   #Firmware retract recover
					totalMoveTimeMinute += fwrecoverTime
				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 = Vector3D(0.0, 0.0, 0.0)
					if x is None and y is None and z is None:
						pos = center
					else:
						pos = Vector3D(pos)
						if x is not None:
							pos.x = center.x
						if y is not None:
							pos.y = center.y
						if z is not None:
							pos.z = center.z
				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[currentExtruder] = e
					if x is not None:
						posOffset.x = pos.x - x
					if y is not None:
						posOffset.y = pos.y - y
					if z is not None:
						posOffset.z = pos.z - z

			elif M is not None:
				if M == 82:   #Absolute E
					absoluteE = True
				elif M == 83:   #Relative E
					absoluteE = False
				elif M == 207 or M == 208: #Firmware retract settings
					s = getCodeFloat(line, 'S')
					f = getCodeFloat(line, 'F')
					if s is not None and f is not None:
						if M == 207:
							fwretractTime = s / f
							fwretractDist = s
						else:
							fwrecoverTime = (fwretractDist + s) / f

			elif T is not None:
				if T > settings().getInt(["gcodeAnalysis", "maxExtruders"]):
					self._logger.warn("GCODE tried to select tool %d, that looks wrong, ignoring for GCODE analysis" % T)
				else:
					posOffset.x -= offsets[currentExtruder][0] if currentExtruder < len(offsets) else 0
					posOffset.y -= offsets[currentExtruder][1] if currentExtruder < len(offsets) else 0

					currentExtruder = T

					posOffset.x += offsets[currentExtruder][0] if currentExtruder < len(offsets) else 0
					posOffset.y += offsets[currentExtruder][1] if currentExtruder < len(offsets) else 0

					if len(currentE) <= currentExtruder:
						for i in range(len(currentE), currentExtruder + 1):
							currentE.append(0.0)
					if len(maxExtrusion) <= currentExtruder:
						for i in range(len(maxExtrusion), currentExtruder + 1):
							maxExtrusion.append(0.0)
					if len(totalExtrusion) <= currentExtruder:
						for i in range(len(totalExtrusion), currentExtruder + 1):
							totalExtrusion.append(0.0)

			if throttle is not None:
				throttle()

		if self.progressCallback is not None:
			self.progressCallback(100.0)

		self.extrusionAmount = maxExtrusion
		self.extrusionVolume = [0] * len(maxExtrusion)
		for i in range(len(maxExtrusion)):
			radius = self._filamentDiameter / 2
			self.extrusionVolume[i] = (self.extrusionAmount[i] * (math.pi * radius * radius)) / 1000
		self.totalMoveTimeMinute = totalMoveTimeMinute
Exemple #40
0
def firstRunSetup():
    global userManager

    if not settings().getBoolean(["server", "firstRun"]):
        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
        settings().setBoolean(["accessControl", "enabled"], True)
        userManager.addUser(request.values["user"], request.values["pass1"],
                            True, ["user", "admin"])
        settings().setBoolean(["server", "firstRun"], False)
    elif "ac" in request.values.keys(
    ) and not request.values["ac"] in valid_boolean_trues:
        # disable access control
        settings().setBoolean(["accessControl", "enabled"], False)
        settings().setBoolean(["server", "firstRun"], False)

        userManager = None
        loginManager.anonymous_user = users.DummyUser
        principals.identity_loaders.appendleft(users.dummy_identity_loader)

    settings().save()
    return jsonify(SUCCESS)
Exemple #41
0
def downloadTimelapse(filename):
    if util.isAllowedFile(filename, set(["mpg"])):
        return send_from_directory(settings().getBaseFolder("timelapse"),
                                   filename,
                                   as_attachment=True)
Exemple #42
0
def getSettings():
    s = settings()

    [movementSpeedX, movementSpeedY, movementSpeedZ, movementSpeedE
     ] = s.get(["printerParameters", "movementSpeed", ["x", "y", "z", "e"]])

    connectionOptions = getConnectionOptions()

    return jsonify({
        "api": {
            "enabled": s.getBoolean(["api", "enabled"]),
            "key": s.get(["api", "key"])
        },
        "appearance": {
            "name": s.get(["appearance", "name"]),
            "color": s.get(["appearance", "color"])
        },
        "printer": {
            "movementSpeedX": movementSpeedX,
            "movementSpeedY": movementSpeedY,
            "movementSpeedZ": movementSpeedZ,
            "movementSpeedE": movementSpeedE,
        },
        "webcam": {
            "streamUrl": s.get(["webcam", "stream"]),
            "snapshotUrl": s.get(["webcam", "snapshot"]),
            "ffmpegPath": s.get(["webcam", "ffmpeg"]),
            "bitrate": s.get(["webcam", "bitrate"]),
            "watermark": s.getBoolean(["webcam", "watermark"]),
            "flipH": s.getBoolean(["webcam", "flipH"]),
            "flipV": s.getBoolean(["webcam", "flipV"])
        },
        "feature": {
            "gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]),
            "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]),
            "alwaysSendChecksum":
            s.getBoolean(["feature", "alwaysSendChecksum"]),
            "sdSupport": s.getBoolean(["feature", "sdSupport"])
        },
        "serial": {
            "port":
            connectionOptions["portPreference"],
            "baudrate":
            connectionOptions["baudratePreference"],
            "portOptions":
            connectionOptions["ports"],
            "baudrateOptions":
            connectionOptions["baudrates"],
            "autoconnect":
            s.getBoolean(["serial", "autoconnect"]),
            "timeoutConnection":
            s.getFloat(["serial", "timeout", "connection"]),
            "timeoutDetection":
            s.getFloat(["serial", "timeout", "detection"]),
            "timeoutCommunication":
            s.getFloat(["serial", "timeout", "communication"]),
            "log":
            s.getBoolean(["serial", "log"])
        },
        "folder": {
            "uploads": s.getBaseFolder("uploads"),
            "timelapse": s.getBaseFolder("timelapse"),
            "timelapseTmp": s.getBaseFolder("timelapse_tmp"),
            "logs": s.getBaseFolder("logs")
        },
        "temperature": {
            "profiles": s.get(["temperature", "profiles"])
        },
        "system": {
            "actions": s.get(["system", "actions"]),
            "events": s.get(["system", "events"])
        }
    })
Exemple #43
0
def getCustomControls():
    customControls = settings().get(["controls"])
    return jsonify(controls=customControls)
Exemple #44
0
def readGcodeFile(filename):
    return send_from_directory(settings().getBaseFolder("uploads"),
                               filename,
                               as_attachment=True)
Exemple #45
0
class Server():
    def __init__(self,
                 configfile=None,
                 basedir=None,
                 host="0.0.0.0",
                 port=5000,
                 debug=False,
                 allowRoot=False):
        self._configfile = configfile
        self._basedir = basedir
        self._host = host
        self._port = port
        self._debug = debug
        self._allowRoot = allowRoot

    def run(self):
        if not self._allowRoot:
            self._checkForRoot()

        # Global as I can't work out a way to get it into PrinterStateConnection
        global printer
        global gcodeManager
        global userManager
        global eventManager
        global loginManager

        from tornado.wsgi import WSGIContainer
        from tornado.httpserver import HTTPServer
        from tornado.ioloop import IOLoop
        from tornado.web import Application, FallbackHandler

        # first initialize the settings singleton and make sure it uses given configfile and basedir if available
        self._initSettings(self._configfile, self._basedir)

        # then initialize logging
        self._initLogging(self._debug)
        logger = logging.getLogger(__name__)

        eventManager = events.eventManager()
        gcodeManager = gcodefiles.GcodeManager()
        printer = Printer(gcodeManager)

        # setup system and gcode command triggers
        events.SystemCommandTrigger(printer)
        events.GcodeCommandTrigger(printer)
        if self._debug:
            events.DebugEventListener()

        if settings().getBoolean(["accessControl", "enabled"]):
            userManagerName = settings().get(["accessControl", "userManager"])
            try:
                clazz = util.getClass(userManagerName)
                userManager = clazz()
            except AttributeError, e:
                logger.exception(
                    "Could not instantiate user manager %s, will run with accessControl disabled!"
                    % userManagerName)

        app.secret_key = "k3PuVYgtxNm8DXKKTw2nWmFQQun9qceV"
        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().getInt(["server", "port"])

        logger.info("Listening on http://%s:%d" % (self._host, self._port))
        app.debug = self._debug

        self._router = tornadio2.TornadioRouter(self._createSocketConnection)

        self._tornado_app = Application(self._router.urls +
                                        [(".*", FallbackHandler, {
                                            "fallback": WSGIContainer(app)
                                        })])
        self._server = HTTPServer(self._tornado_app)
        self._server.listen(self._port, address=self._host)

        eventManager.fire("Startup")
        if settings().getBoolean(["serial", "autoconnect"]):
            (port, baudrate) = settings().get(
                ["serial", "port"]), settings().getInt(["serial", "baudrate"])
            connectionOptions = getConnectionOptions()
            if port in connectionOptions["ports"]:
                printer.connect(port, baudrate)
        IOLoop.instance().start()
Exemple #46
0
 def decorated_view(*args, **kwargs):
     if settings().getBoolean([
             "server", "firstRun"
     ]) and (userManager is None or not userManager.hasBeenCustomized()):
         return make_response("OctoPrint isn't setup yet", 403)
     return login_required(func)(*args, **kwargs)
Exemple #47
0
def _process_templates():
    first_run = settings().getBoolean(["server", "firstRun"])

    ##~~ prepare templates

    templates = defaultdict(lambda: dict(order=[], entries=dict()))

    # rules for transforming template configs to template entries
    template_rules = dict(
        navbar=dict(div=lambda x: "navbar_plugin_" + x,
                    template=lambda x: x + "_navbar.jinja2",
                    to_entry=lambda data: data),
        sidebar=dict(div=lambda x: "sidebar_plugin_" + x,
                     template=lambda x: x + "_sidebar.jinja2",
                     to_entry=lambda data: (data["name"], data)),
        tab=dict(div=lambda x: "tab_plugin_" + x,
                 template=lambda x: x + "_tab.jinja2",
                 to_entry=lambda data: (data["name"], data)),
        settings=dict(div=lambda x: "settings_plugin_" + x,
                      template=lambda x: x + "_settings.jinja2",
                      to_entry=lambda data: (data["name"], data)),
        usersettings=dict(div=lambda x: "usersettings_plugin_" + x,
                          template=lambda x: x + "_usersettings.jinja2",
                          to_entry=lambda data: (data["name"], data)),
        wizard=dict(div=lambda x: "wizard_plugin_" + x,
                    template=lambda x: x + "_wizard.jinja2",
                    to_entry=lambda data: (data["name"], data)),
        about=dict(div=lambda x: "about_plugin_" + x,
                   template=lambda x: x + "_about.jinja2",
                   to_entry=lambda data: (data["name"], data)),
        generic=dict(template=lambda x: x + ".jinja2",
                     to_entry=lambda data: data))

    # sorting orders
    template_sorting = dict(
        navbar=dict(add="prepend", key=None),
        sidebar=dict(add="append", key="name"),
        tab=dict(add="append", key="name"),
        settings=dict(
            add="custom_append",
            key="name",
            custom_add_entries=lambda missing: dict(section_plugins=(gettext(
                "Plugins"), None)),
            custom_add_order=lambda missing: ["section_plugins"] + missing),
        usersettings=dict(add="append", key="name"),
        wizard=dict(add="append",
                    key="name",
                    key_extractor=lambda d, k: "0:{}".format(d[0])
                    if "mandatory" in d[1] and d[1]["mandatory"] else "1:{}".
                    format(d[0])),
        about=dict(add="append", key="name"),
        generic=dict(add="append", key=None))

    hooks = pluginManager.get_hooks("octoprint.ui.web.templatetypes")
    for name, hook in hooks.items():
        try:
            result = hook(dict(template_sorting), dict(template_rules))
        except:
            _logger.exception(
                "Error while retrieving custom template type definitions from plugin {name}"
                .format(**locals()))
        else:
            if not isinstance(result, list):
                continue

            for entry in result:
                if not isinstance(entry, tuple) or not len(entry) == 3:
                    continue

                key, order, rule = entry

                # order defaults
                if "add" not in order:
                    order["add"] = "prepend"
                if "key" not in order:
                    order["key"] = "name"

                # rule defaults
                if "div" not in rule:
                    # default div name: <hook plugin>_<template_key>_plugin_<plugin>
                    div = "{name}_{key}_plugin_".format(**locals())
                    rule["div"] = lambda x: div + x
                if "template" not in rule:
                    # default template name: <plugin>_plugin_<hook plugin>_<template key>.jinja2
                    template = "_plugin_{name}_{key}.jinja2".format(**locals())
                    rule["template"] = lambda x: x + template
                if "to_entry" not in rule:
                    # default to_entry assumes existing "name" property to be used as label for 2-tuple entry data structure (<name>, <properties>)
                    rule["to_entry"] = lambda data: (data["name"], data)

                template_rules["plugin_" + name + "_" + key] = rule
                template_sorting["plugin_" + name + "_" + key] = order
    template_types = template_rules.keys()

    # navbar

    templates["navbar"]["entries"] = dict(
        fabapp=dict(template="navbar/fabapp.jinja2", _div="navbar_fabapp"),
        settings=dict(template="navbar/settings.jinja2",
                      _div="navbar_settings",
                      styles=["display: none"],
                      data_bind="visible: loginState.isAdmin"),
        systemmenu=dict(template="navbar/systemmenu.jinja2",
                        _div="navbar_systemmenu",
                        styles=["display: none"],
                        classes=["dropdown"],
                        data_bind="visible: loginState.isAdmin",
                        custom_bindings=False),
        login=dict(template="navbar/login.jinja2",
                   _div="navbar_login",
                   classes=["dropdown"],
                   custom_bindings=False),
    )

    # sidebar

    templates["sidebar"]["entries"] = dict(
        connection=(gettext("Connection"),
                    dict(template="sidebar/connection.jinja2",
                         _div="connection",
                         icon="signal",
                         styles_wrapper=["display: none"],
                         data_bind="visible: loginState.isUser")),
        state=(gettext("State"),
               dict(template="sidebar/state.jinja2",
                    _div="state",
                    icon="info-sign")),
        files=(gettext("Files"),
               dict(template="sidebar/files.jinja2",
                    _div="files",
                    icon="list",
                    classes_content=["overflow_visible"],
                    template_header="sidebar/files_header.jinja2")))

    # tabs

    templates["tab"]["entries"] = dict(
        temperature=(gettext("Temperature"),
                     dict(template="tabs/temperature.jinja2", _div="temp")),
        control=(gettext("Control"),
                 dict(template="tabs/control.jinja2", _div="control")),
        gcodeviewer=(gettext("GCode Viewer"),
                     dict(template="tabs/gcodeviewer.jinja2", _div="gcode")),
        terminal=(gettext("Terminal"),
                  dict(template="tabs/terminal.jinja2", _div="term")),
        timelapse=(gettext("Timelapse"),
                   dict(template="tabs/timelapse.jinja2", _div="timelapse")))

    # settings dialog

    templates["settings"]["entries"] = dict(
        section_printer=(gettext("Printer"), None),
        serial=(gettext("Serial Connection"),
                dict(template="dialogs/settings/serialconnection.jinja2",
                     _div="settings_serialConnection",
                     custom_bindings=False)),
        printerprofiles=(
            gettext("Printer Profiles"),
            dict(template="dialogs/settings/printerprofiles.jinja2",
                 _div="settings_printerProfiles",
                 custom_bindings=False)),
        temperatures=(gettext("Temperatures"),
                      dict(template="dialogs/settings/temperatures.jinja2",
                           _div="settings_temperature",
                           custom_bindings=False)),
        terminalfilters=(
            gettext("Terminal Filters"),
            dict(template="dialogs/settings/terminalfilters.jinja2",
                 _div="settings_terminalFilters",
                 custom_bindings=False)),
        gcodescripts=(gettext("GCODE Scripts"),
                      dict(template="dialogs/settings/gcodescripts.jinja2",
                           _div="settings_gcodeScripts",
                           custom_bindings=False)),
        section_features=(gettext("Features"), None),
        features=(gettext("Features"),
                  dict(template="dialogs/settings/features.jinja2",
                       _div="settings_features",
                       custom_bindings=False)),
        webcam=(gettext("Webcam & Timelapse"),
                dict(template="dialogs/settings/webcam.jinja2",
                     _div="settings_webcam",
                     custom_bindings=False)),
        gcodevisualizer=(
            gettext("GCODE Visualizer"),
            dict(template="dialogs/settings/gcodevisualizer.jinja2",
                 _div="settings_gcodegcodevisualizer",
                 custom_bindings=False)),
        api=(gettext("API"),
             dict(template="dialogs/settings/api.jinja2",
                  _div="settings_api",
                  custom_bindings=False)),
        section_octoprint=(gettext("OctoPrint"), None),
        accesscontrol=(gettext("Access Control"),
                       dict(template="dialogs/settings/accesscontrol.jinja2",
                            _div="settings_users",
                            custom_bindings=False)),
        folders=(gettext("Folders"),
                 dict(template="dialogs/settings/folders.jinja2",
                      _div="settings_folders",
                      custom_bindings=False)),
        appearance=(gettext("Appearance"),
                    dict(template="dialogs/settings/appearance.jinja2",
                         _div="settings_appearance",
                         custom_bindings=False)),
        logs=(gettext("Logs"),
              dict(template="dialogs/settings/logs.jinja2",
                   _div="settings_logs")),
        server=(gettext("Server"),
                dict(template="dialogs/settings/server.jinja2",
                     _div="settings_server",
                     custom_bindings=False)),
    )

    # user settings dialog

    templates["usersettings"]["entries"] = dict(
        access=(gettext("Access"),
                dict(template="dialogs/usersettings/access.jinja2",
                     _div="usersettings_access",
                     custom_bindings=False)),
        interface=(gettext("Interface"),
                   dict(template="dialogs/usersettings/interface.jinja2",
                        _div="usersettings_interface",
                        custom_bindings=False)),
    )

    # wizard

    if first_run:

        def custom_insert_order(existing, missing):
            if "firstrunstart" in missing:
                missing.remove("firstrunstart")
            if "firstrunend" in missing:
                missing.remove("firstrunend")

            return ["firstrunstart"] + existing + missing + ["firstrunend"]

        template_sorting["wizard"].update(
            dict(add="custom_insert",
                 custom_insert_entries=lambda missing: dict(),
                 custom_insert_order=custom_insert_order))
        templates["wizard"]["entries"] = dict(
            firstrunstart=(gettext("Start"),
                           dict(
                               template="dialogs/wizard/firstrun_start.jinja2",
                               _div="wizard_firstrun_start")),
            firstrunend=(gettext("Finish"),
                         dict(template="dialogs/wizard/firstrun_end.jinja2",
                              _div="wizard_firstrun_end")),
        )

    # about dialog

    templates["about"]["entries"] = dict(
        about=("About OctoPrint",
               dict(template="dialogs/about/about.jinja2",
                    _div="about_about",
                    custom_bindings=False)),
        license=("OctoPrint License",
                 dict(template="dialogs/about/license.jinja2",
                      _div="about_license",
                      custom_bindings=False)),
        thirdparty=("Third Party Licenses",
                    dict(template="dialogs/about/thirdparty.jinja2",
                         _div="about_thirdparty",
                         custom_bindings=False)),
        authors=("Authors",
                 dict(template="dialogs/about/authors.jinja2",
                      _div="about_authors",
                      custom_bindings=False)),
        changelog=("Changelog",
                   dict(template="dialogs/about/changelog.jinja2",
                        _div="about_changelog",
                        custom_bindings=False)),
        supporters=("Supporters",
                    dict(template="dialogs/about/supporters.jinja2",
                         _div="about_sponsors",
                         custom_bindings=False)))

    # extract data from template plugins

    template_plugins = pluginManager.get_implementations(
        octoprint.plugin.TemplatePlugin)

    plugin_vars = dict()
    plugin_names = set()
    seen_wizards = settings().get(["server", "seenWizards"
                                   ]) if not first_run else dict()
    for implementation in template_plugins:
        name = implementation._identifier
        plugin_names.add(name)
        wizard_required = False
        wizard_ignored = False

        try:
            vars = implementation.get_template_vars()
            configs = implementation.get_template_configs()
            if isinstance(implementation, octoprint.plugin.WizardPlugin):
                wizard_required = implementation.is_wizard_required()
                wizard_ignored = octoprint.plugin.WizardPlugin.is_wizard_ignored(
                    seen_wizards, implementation)
        except:
            _logger.exception(
                "Error while retrieving template data for plugin {}, ignoring it"
                .format(name))
            continue

        if not isinstance(vars, dict):
            vars = dict()
        if not isinstance(configs, (list, tuple)):
            configs = []

        for var_name, var_value in vars.items():
            plugin_vars["plugin_" + name + "_" + var_name] = var_value

        includes = _process_template_configs(name, implementation, configs,
                                             template_rules)

        if not wizard_required or wizard_ignored:
            includes["wizard"] = list()

        for t in template_types:
            for include in includes[t]:
                if t == "navbar" or t == "generic":
                    data = include
                else:
                    data = include[1]

                key = data["_key"]
                if "replaces" in data:
                    key = data["replaces"]
                templates[t]["entries"][key] = include

    #~~ order internal templates and plugins

    # make sure that
    # 1) we only have keys in our ordered list that we have entries for and
    # 2) we have all entries located somewhere within the order

    for t in template_types:
        default_order = settings().get(
            ["appearance", "components", "order", t],
            merged=True,
            config=dict()) or []
        configured_order = settings().get(
            ["appearance", "components", "order", t], merged=True) or []
        configured_disabled = settings().get(
            ["appearance", "components", "disabled", t]) or []

        # first create the ordered list of all component ids according to the configured order
        templates[t]["order"] = [
            x for x in configured_order
            if x in templates[t]["entries"] and not x in configured_disabled
        ]

        # now append the entries from the default order that are not already in there
        templates[t]["order"] += [
            x for x in default_order if not x in templates[t]["order"]
            and x in templates[t]["entries"] and not x in configured_disabled
        ]

        all_ordered = set(templates[t]["order"])
        all_disabled = set(configured_disabled)

        # check if anything is missing, if not we are done here
        missing_in_order = set(templates[t]["entries"].keys()).difference(
            all_ordered).difference(all_disabled)
        if len(missing_in_order) == 0:
            continue

        # works with entries that are dicts and entries that are 2-tuples with the
        # entry data at index 1
        def config_extractor(item, key, default_value=None):
            if isinstance(item, dict) and key in item:
                return item[key] if key in item else default_value
            elif isinstance(item, tuple) and len(item) > 1 and isinstance(
                    item[1], dict) and key in item[1]:
                return item[1][key] if key in item[1] else default_value

            return default_value

        # finally add anything that's not included in our order yet
        if template_sorting[t]["key"] is not None:
            # we'll use our config extractor as default key extractor
            extractor = config_extractor

            # if template type provides custom extractor, make sure its exceptions are handled
            if "key_extractor" in template_sorting[t] and callable(
                    template_sorting[t]["key_extractor"]):

                def create_safe_extractor(extractor):
                    def f(x, k):
                        try:
                            return extractor(x, k)
                        except:
                            _logger.exception(
                                "Error while extracting sorting keys for template {}"
                                .format(t))
                            return None

                    return f

                extractor = create_safe_extractor(
                    template_sorting[t]["key_extractor"])

            sort_key = template_sorting[t]["key"]

            def key_func(x):
                config = templates[t]["entries"][x]
                entry_order = config_extractor(config,
                                               "order",
                                               default_value=None)
                return entry_order is None, entry_order, extractor(
                    config, sort_key)

            sorted_missing = sorted(missing_in_order, key=key_func)
        else:

            def key_func(x):
                config = templates[t]["entries"][x]
                entry_order = config_extractor(config,
                                               "order",
                                               default_value=None)
                return entry_order is None, entry_order

            sorted_missing = sorted(missing_in_order, key=key_func)

        if template_sorting[t]["add"] == "prepend":
            templates[t]["order"] = sorted_missing + templates[t]["order"]
        elif template_sorting[t]["add"] == "append":
            templates[t]["order"] += sorted_missing
        elif template_sorting[t][
                "add"] == "custom_prepend" and "custom_add_entries" in template_sorting[
                    t] and "custom_add_order" in template_sorting[t]:
            templates[t]["entries"].update(
                template_sorting[t]["custom_add_entries"](sorted_missing))
            templates[t]["order"] = template_sorting[t]["custom_add_order"](
                sorted_missing) + templates[t]["order"]
        elif template_sorting[t][
                "add"] == "custom_append" and "custom_add_entries" in template_sorting[
                    t] and "custom_add_order" in template_sorting[t]:
            templates[t]["entries"].update(
                template_sorting[t]["custom_add_entries"](sorted_missing))
            templates[t]["order"] += template_sorting[t]["custom_add_order"](
                sorted_missing)
        elif template_sorting[t][
                "add"] == "custom_insert" and "custom_insert_entries" in template_sorting[
                    t] and "custom_insert_order" in template_sorting[t]:
            templates[t]["entries"].update(
                template_sorting[t]["custom_insert_entries"](sorted_missing))
            templates[t]["order"] = template_sorting[t]["custom_insert_order"](
                templates[t]["order"], sorted_missing)

    return templates, plugin_names, plugin_vars
Exemple #48
0
def uploadGcodeFile(target):
    input_name = "file"
    input_upload_name = input_name + "." + settings().get(
        ["server", "uploads", "nameSuffix"])
    input_upload_path = input_name + "." + settings().get(
        ["server", "uploads", "pathSuffix"])
    if input_upload_name in request.values and input_upload_path in request.values:
        if not target in [FileDestinations.LOCAL, FileDestinations.SDCARD]:
            return make_response("Unknown target: %s" % target, 404)

        upload = octoprint.filemanager.util.DiskFileWrapper(
            request.values[input_upload_name],
            request.values[input_upload_path])

        # Store any additional user data the caller may have passed.
        userdata = None
        if "userdata" in request.values:
            import json
            try:
                userdata = json.loads(request.values["userdata"])
            except:
                return make_response("userdata contains invalid JSON", 400)

        if target == FileDestinations.SDCARD and not settings().getBoolean(
            ["feature", "sdSupport"]):
            return make_response("SD card support is disabled", 404)

        sd = target == FileDestinations.SDCARD
        selectAfterUpload = "select" in list(request.values.keys(
        )) and request.values["select"] in valid_boolean_trues
        printAfterSelect = "print" in list(request.values.keys(
        )) and request.values["print"] in valid_boolean_trues

        if sd:
            # validate that all preconditions for SD upload are met before attempting it
            if not (printer.is_operational()
                    and not (printer.is_printing() or printer.is_paused())):
                return make_response(
                    "Can not upload to SD card, printer is either not operational or already busy",
                    409)
            if not printer.is_sd_ready():
                return make_response(
                    "Can not upload to SD card, not yet initialized", 409)

        # determine future filename of file to be uploaded, abort if it can't be uploaded
        try:
            # FileDestinations.LOCAL = should normally be target, but can't because SDCard handling isn't implemented yet
            futurePath, futureFilename = fileManager.sanitize(
                FileDestinations.LOCAL, upload.filename)
        except:
            futurePath = None
            futureFilename = None

        if futureFilename is None:
            return make_response(
                "Can not upload file %s, wrong format?" % upload.filename, 415)

        if "path" in request.values and request.values["path"]:
            # we currently only support uploads to sdcard via local, so first target is local instead of "target"
            futurePath = fileManager.sanitize_path(FileDestinations.LOCAL,
                                                   request.values["path"])

        # prohibit overwriting currently selected file while it's being printed
        futureFullPath = fileManager.join_path(FileDestinations.LOCAL,
                                               futurePath, futureFilename)
        futureFullPathInStorage = fileManager.path_in_storage(
            FileDestinations.LOCAL, futureFullPath)

        if not printer.can_modify_file(futureFullPathInStorage, sd):
            return make_response(
                "Trying to overwrite file that is currently being printed: %s"
                % futureFullPath, 409)

        reselect = printer.is_current_file(futureFullPathInStorage, sd)

        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 selectAndOrPrint.
			"""

            if destination == FileDestinations.SDCARD and octoprint.filemanager.valid_file_type(
                    filename, "gcode"):
                return filename, printer.add_sd_file(filename, absFilename,
                                                     selectAndOrPrint)
            else:
                selectAndOrPrint(filename, absFilename, destination)
                return filename

        def selectAndOrPrint(filename, absFilename, destination):
            """
			Callback for when the file is ready to be selected and optionally printed. For SD file uploads this is 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.
			"""
            if octoprint.filemanager.valid_file_type(
                    added_file, "gcode") and (selectAfterUpload
                                              or printAfterSelect or reselect):
                printer.select_file(absFilename,
                                    destination == FileDestinations.SDCARD,
                                    printAfterSelect)

        try:
            added_file = fileManager.add_file(FileDestinations.LOCAL,
                                              futureFullPathInStorage,
                                              upload,
                                              allow_overwrite=True)
        except octoprint.filemanager.storage.StorageError as e:
            if e.code == octoprint.filemanager.storage.StorageError.INVALID_FILE:
                return make_response(
                    "Could not upload the file \"{}\", invalid type".format(
                        upload.filename), 400)
            else:
                return make_response(
                    "Could not upload the file \"{}\"".format(upload.filename),
                    500)

        if octoprint.filemanager.valid_file_type(added_file, "stl"):
            filename = added_file
            done = True
        else:
            filename = fileProcessingFinished(
                added_file,
                fileManager.path_on_disk(FileDestinations.LOCAL, added_file),
                target)
            done = not sd

        if userdata is not None:
            # upload included userdata, add this now to the metadata
            fileManager.set_additional_metadata(FileDestinations.LOCAL,
                                                added_file, "userdata",
                                                userdata)

        sdFilename = None
        if isinstance(filename, tuple):
            filename, sdFilename = filename

        eventManager.fire(
            Events.UPLOAD,
            {
                "name": futureFilename,
                "path": filename,
                "target": target,

                # TODO deprecated, remove in 1.4.0
                "file": filename
            })

        files = {}
        location = url_for(".readGcodeFile",
                           target=FileDestinations.LOCAL,
                           filename=filename,
                           _external=True)
        files.update({
            FileDestinations.LOCAL: {
                "name": futureFilename,
                "path": filename,
                "origin": FileDestinations.LOCAL,
                "refs": {
                    "resource":
                    location,
                    "download":
                    url_for("index", _external=True) + "downloads/files/" +
                    FileDestinations.LOCAL + "/" + filename
                }
            }
        })

        if sd and sdFilename:
            location = url_for(".readGcodeFile",
                               target=FileDestinations.SDCARD,
                               filename=sdFilename,
                               _external=True)
            files.update({
                FileDestinations.SDCARD: {
                    "name": sdFilename,
                    "path": sdFilename,
                    "origin": FileDestinations.SDCARD,
                    "refs": {
                        "resource": location
                    }
                }
            })

        r = make_response(jsonify(files=files, done=done), 201)
        r.headers["Location"] = location
        return r

    elif "foldername" in request.values:
        foldername = request.values["foldername"]

        if not target in [FileDestinations.LOCAL]:
            return make_response("Unknown target: %s" % target, 400)

        futurePath, futureName = fileManager.sanitize(target, foldername)
        if not futureName or not futurePath:
            return make_response("Can't create a folder with an empty name",
                                 400)

        if "path" in request.values and request.values["path"]:
            futurePath = fileManager.sanitize_path(FileDestinations.LOCAL,
                                                   request.values["path"])

        futureFullPath = fileManager.join_path(target, futurePath, futureName)
        if octoprint.filemanager.valid_file_type(futureName):
            return make_response(
                "Can't create a folder named %s, please try another name" %
                futureName, 409)

        try:
            added_folder = fileManager.add_folder(target, futureFullPath)
        except octoprint.filemanager.storage.StorageError as e:
            if e.code == octoprint.filemanager.storage.StorageError.INVALID_DIRECTORY:
                return make_response(
                    "Could not create folder {}, invalid directory".format(
                        futureName))
            else:
                return make_response(
                    "Could not create folder {}".format(futureName))

        location = url_for(".readGcodeFile",
                           target=FileDestinations.LOCAL,
                           filename=added_folder,
                           _external=True)
        folder = dict(name=futureName,
                      path=added_folder,
                      origin=target,
                      refs=dict(resource=location))

        r = make_response(jsonify(folder=folder, done=True), 201)
        r.headers["Location"] = location
        return r

    else:
        return make_response("No file to upload and no folder to create", 400)
Exemple #49
0
def getCustomControls():
	# TODO: document me
	customControls = settings().get(["controls"])
	return jsonify(controls=customControls)
Exemple #50
0
def index():
    global _templates, _plugin_names, _plugin_vars

    preemptive_cache_enabled = settings().getBoolean(
        ["devel", "cache", "preemptive"])

    locale = g.locale.language if g.locale else "en"

    # helper to check if wizards are active
    def wizard_active(templates):
        return templates is not None and bool(templates["wizard"]["order"])

    # we force a refresh if the client forces one or if we have wizards cached
    force_refresh = util.flask.cache_check_headers(
    ) or "_refresh" in request.values or wizard_active(_templates.get(locale))

    # if we need to refresh our template cache or it's not yet set, process it
    if force_refresh or _templates.get(
            locale) is None or _plugin_names is None or _plugin_vars is None:
        _templates[locale], _plugin_names, _plugin_vars = _process_templates()

    now = datetime.datetime.utcnow()

    enable_accesscontrol = userManager.enabled
    enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
    enable_timelapse = bool(settings().get(["webcam", "snapshot"])
                            and settings().get(["webcam", "ffmpeg"]))

    def default_template_filter(template_type, template_key):
        if template_type == "navbar":
            return template_key != "login" or enable_accesscontrol
        elif template_type == "tab":
            return (template_key != "gcodeviewer" or enable_gcodeviewer) and \
                   (template_key != "timelapse" or enable_timelapse)
        elif template_type == "settings":
            return template_key != "accesscontrol" or enable_accesscontrol
        elif template_type == "usersettings":
            return enable_accesscontrol
        else:
            return True

    default_additional_etag = [
        enable_accesscontrol, enable_gcodeviewer, enable_timelapse
    ]

    def get_preemptively_cached_view(key,
                                     view,
                                     data=None,
                                     additional_request_data=None,
                                     additional_unless=None):
        if (data is None
                and additional_request_data is None) or g.locale is None:
            return view

        d = _preemptive_data(key,
                             data=data,
                             additional_request_data=additional_request_data)

        def unless():
            return _preemptive_unless(base_url=request.url_root,
                                      additional_unless=additional_unless)

        # finally decorate our view
        return util.flask.preemptively_cached(cache=preemptiveCache,
                                              data=d,
                                              unless=unless)(view)

    def get_cached_view(key,
                        view,
                        additional_key_data=None,
                        additional_files=None,
                        additional_etag=None,
                        custom_files=None,
                        custom_etag=None,
                        custom_lastmodified=None):
        if additional_etag is None:
            additional_etag = []

        def cache_key():
            return _cache_key(key, additional_key_data=additional_key_data)

        def check_etag_and_lastmodified():
            files = collect_files()
            lastmodified = compute_lastmodified(files)
            lastmodified_ok = util.flask.check_lastmodified(lastmodified)
            etag_ok = util.flask.check_etag(
                compute_etag(files=files,
                             lastmodified=lastmodified,
                             additional=[cache_key()] + additional_etag))
            return lastmodified_ok and etag_ok

        def validate_cache(cached):
            etag_different = compute_etag(
                additional=[cache_key()] +
                additional_etag) != cached.get_etag()[0]
            return force_refresh or etag_different

        def collect_files():
            if callable(custom_files):
                try:
                    files = custom_files()
                    if files:
                        return files
                except:
                    _logger.exception(
                        "Error while trying to retrieve tracked files for plugin {}"
                        .format(key))

            templates = _get_all_templates()
            assets = _get_all_assets()
            translations = _get_all_translationfiles(
                g.locale.language if g.locale else "en", "messages")

            files = templates + assets + translations

            if callable(additional_files):
                try:
                    af = additional_files()
                    if af:
                        files += af
                except:
                    _logger.exception(
                        "Error while trying to retrieve additional tracked files for plugin {}"
                        .format(key))

            return sorted(set(files))

        def compute_lastmodified(files=None):
            if callable(custom_lastmodified):
                try:
                    lastmodified = custom_lastmodified()
                    if lastmodified:
                        return lastmodified
                except:
                    _logger.exception(
                        "Error while trying to retrieve custom LastModified value for plugin {}"
                        .format(key))

            if files is None:
                files = collect_files()
            return _compute_date(files)

        def compute_etag(files=None, lastmodified=None, additional=None):
            if callable(custom_etag):
                try:
                    etag = custom_etag()
                    if etag:
                        return etag
                except:
                    _logger.exception(
                        "Error while trying to retrieve custom ETag value for plugin {}"
                        .format(key))

            if files is None:
                files = collect_files()
            if lastmodified is None:
                lastmodified = compute_lastmodified(files)
            if lastmodified and not isinstance(lastmodified, basestring):
                from werkzeug.http import http_date
                lastmodified = http_date(lastmodified)
            if additional is None:
                additional = []

            import hashlib
            hash = hashlib.sha1()
            hash.update(octoprint.__version__)
            hash.update(octoprint.server.UI_API_KEY)
            hash.update(",".join(sorted(files)))
            if lastmodified:
                hash.update(lastmodified)
            for add in additional:
                hash.update(str(add))
            return hash.hexdigest()

        decorated_view = view
        decorated_view = util.flask.lastmodified(
            lambda _: compute_lastmodified())(decorated_view)
        decorated_view = util.flask.etagged(lambda _: compute_etag(
            additional=[cache_key()] + additional_etag))(decorated_view)
        decorated_view = util.flask.cached(
            timeout=-1,
            refreshif=validate_cache,
            key=cache_key,
            unless_response=lambda response: util.flask.
            cache_check_response_headers(response) or util.flask.
            cache_check_status_code(response, _valid_status_for_cache))(
                decorated_view)
        decorated_view = util.flask.conditional(check_etag_and_lastmodified,
                                                NOT_MODIFIED)(decorated_view)
        return decorated_view

    def plugin_view(p):
        cached = get_cached_view(
            p._identifier,
            p.on_ui_render,
            additional_key_data=p.get_ui_additional_key_data_for_cache,
            additional_files=p.get_ui_additional_tracked_files,
            custom_files=p.get_ui_custom_tracked_files,
            custom_etag=p.get_ui_custom_etag,
            custom_lastmodified=p.get_ui_custom_lastmodified,
            additional_etag=p.get_ui_additional_etag(default_additional_etag))

        if preemptive_cache_enabled and p.get_ui_preemptive_caching_enabled():
            view = get_preemptively_cached_view(
                p._identifier, cached, p.get_ui_data_for_preemptive_caching,
                p.get_ui_additional_request_data_for_preemptive_caching,
                p.get_ui_preemptive_caching_additional_unless)
        else:
            view = cached

        template_filter = p.get_ui_custom_template_filter(
            default_template_filter)
        if template_filter is not None and callable(template_filter):
            filtered_templates = _filter_templates(_templates[locale],
                                                   template_filter)
        else:
            filtered_templates = _templates[locale]

        render_kwargs = _get_render_kwargs(filtered_templates, _plugin_names,
                                           _plugin_vars, now)

        return view(now, request, render_kwargs)

    def default_view():
        filtered_templates = _filter_templates(_templates[locale],
                                               default_template_filter)

        wizard = wizard_active(filtered_templates)
        accesscontrol_active = enable_accesscontrol and userManager.hasBeenCustomized(
        )

        render_kwargs = _get_render_kwargs(filtered_templates, _plugin_names,
                                           _plugin_vars, now)

        render_kwargs.update(
            dict(
                webcamStream=settings().get(["webcam", "stream"]),
                enableTemperatureGraph=settings().get(
                    ["feature", "temperatureGraph"]),
                enableAccessControl=enable_accesscontrol,
                accessControlActive=accesscontrol_active,
                enableSdSupport=settings().get(["feature", "sdSupport"]),
                gcodeMobileThreshold=settings().get(
                    ["gcodeViewer", "mobileSizeThreshold"]),
                gcodeThreshold=settings().get(["gcodeViewer",
                                               "sizeThreshold"]),
                wizard=wizard,
                now=now,
            ))

        # no plugin took an interest, we'll use the default UI
        def make_default_ui():
            r = make_response(render_template("index.jinja2", **render_kwargs))
            if wizard:
                # if we have active wizard dialogs, set non caching headers
                r = util.flask.add_non_caching_response_headers(r)
            return r

        cached = get_cached_view("_default",
                                 make_default_ui,
                                 additional_etag=default_additional_etag)
        preemptively_cached = get_preemptively_cached_view(
            "_default", cached, dict(), dict())
        return preemptively_cached()

    response = None

    forced_view = request.headers.get("X-Force-View", None)

    if forced_view:
        # we have view forced by the preemptive cache
        _logger.debug("Forcing rendering of view {}".format(forced_view))
        if forced_view != "_default":
            plugin = pluginManager.get_plugin_info(forced_view,
                                                   require_enabled=True)
            if plugin is not None and isinstance(plugin.implementation,
                                                 octoprint.plugin.UiPlugin):
                response = plugin_view(plugin.implementation)
        else:
            response = default_view()

    else:
        # select view from plugins and fall back on default view if no plugin will handle it
        ui_plugins = pluginManager.get_implementations(
            octoprint.plugin.UiPlugin, sorting_context="UiPlugin.on_ui_render")
        for plugin in ui_plugins:
            if plugin.will_handle_ui(request):
                # plugin claims responsibility, let it render the UI
                response = plugin_view(plugin)
                if response is not None:
                    break
                else:
                    _logger.warn(
                        "UiPlugin {} returned an empty response".format(
                            plugin._identifier))
        else:
            response = default_view()

    if response is None:
        return abort(404)
    return response
Exemple #51
0
    def _render(self):
        """Rendering runnable."""

        ffmpeg = settings().get(["webcam", "ffmpeg"])
        bitrate = settings().get(["webcam", "bitrate"])
        if ffmpeg is None or bitrate is None:
            self._logger.warning(
                "Cannot create movie, path to ffmpeg or desired bitrate is unset"
            )
            return

        if self._videocodec == 'mpeg2video':
            extension = "mpg"
        else:
            extension = "mp4"

        input = os.path.join(
            self._capture_dir,
            self._capture_format.format(
                prefix=self._prefix,
                postfix=self._postfix if self._postfix is not None else ""))

        output_name = self._output_format.format(
            prefix=self._prefix,
            postfix=self._postfix if self._postfix is not None else "",
            extension=extension)
        temporary = os.path.join(self._output_dir, ".{}".format(output_name))
        output = os.path.join(self._output_dir, output_name)

        for i in range(4):
            if os.path.exists(input % i):
                break
        else:
            self._logger.warning("Cannot create a movie, no frames captured")
            self._notify_callback("fail",
                                  output,
                                  returncode=0,
                                  stdout="",
                                  stderr="",
                                  reason="no_frames")
            return

        hflip = settings().getBoolean(["webcam", "flipH"])
        vflip = settings().getBoolean(["webcam", "flipV"])
        rotate = settings().getBoolean(["webcam", "rotate90"])

        watermark = None
        if settings().getBoolean(["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(":", "\\\\:")

        # prepare ffmpeg command
        command_str = self._create_ffmpeg_command_string(ffmpeg,
                                                         self._fps,
                                                         bitrate,
                                                         self._threads,
                                                         input,
                                                         temporary,
                                                         self._videocodec,
                                                         hflip=hflip,
                                                         vflip=vflip,
                                                         rotate=rotate,
                                                         watermark=watermark)
        self._logger.debug("Executing command: {}".format(command_str))

        with self.render_job_lock:
            try:
                self._notify_callback("start", output)

                self._logger.debug("Parsing ffmpeg output")

                c = CommandlineCaller()
                c.on_log_stderr = self._process_ffmpeg_output
                returncode, stdout_text, stderr_text = c.call(command_str,
                                                              delimiter=b'\r',
                                                              buffer_size=512)

                self._logger.debug("Done with parsing")

                if returncode == 0:
                    shutil.move(temporary, output)
                    self._notify_callback("success", output)
                else:
                    self._logger.warning(
                        "Could not render movie, got return code %r: %s" %
                        (returncode, stderr_text))
                    self._notify_callback("fail",
                                          output,
                                          returncode=returncode,
                                          stdout=stdout_text,
                                          stderr=stderr_text,
                                          reason="returncode")
            except Exception:
                self._logger.exception(
                    "Could not render movie due to unknown error")
                self._notify_callback("fail", output, reason="unknown")
            finally:
                try:
                    if os.path.exists(temporary):
                        os.remove(temporary)
                except Exception:
                    self._logger.warn(
                        "Could not delete temporary timelapse {}".format(
                            temporary))
                self._notify_callback("always", output)
Exemple #52
0
def printerSdState():
	if not settings().getBoolean(["feature", "sdSupport"]):
		return make_response("SD support is disabled", 404)

	return jsonify(ready=printer.isSdReady())
Exemple #53
0
def last_modified_unrendered():
    return os.stat(settings().getBaseFolder("timelapse_tmp",
                                            check_writable=False)).st_mtime
Exemple #54
0
def configure_timelapse(config=None, persist=False):
    global current

    if config is None:
        config = settings().get(["webcam", "timelapse"], merged=True)

    if current is not None:
        current.unload()

    snapshot_url = settings().get(["webcam", "snapshot"])
    ffmpeg_path = settings().get(["webcam", "ffmpeg"])
    timelapse_enabled = settings().getBoolean(["webcam", "timelapseEnabled"])
    timelapse_precondition = snapshot_url is not None and snapshot_url.strip() != "" \
                             and ffmpeg_path is not None and ffmpeg_path.strip() != ""

    type = config["type"]
    if not timelapse_precondition and timelapse_precondition:
        logging.getLogger(__name__).warn(
            "Essential timelapse settings unconfigured (snapshot URL or FFMPEG path) "
            "but timelapse enabled.")

    if not timelapse_enabled or not timelapse_precondition or type is None or "off" == type:
        current = None

    else:
        postRoll = 0
        if "postRoll" in config and config["postRoll"] >= 0:
            postRoll = config["postRoll"]

        fps = 25
        if "fps" in config and config["fps"] > 0:
            fps = config["fps"]

        if "zchange" == type:
            retractionZHop = 0
            if "options" in config and "retractionZHop" in config[
                    "options"] and config["options"]["retractionZHop"] >= 0:
                retractionZHop = config["options"]["retractionZHop"]

            minDelay = 5
            if "options" in config and "minDelay" in config[
                    "options"] and config["options"]["minDelay"] > 0:
                minDelay = config["options"]["minDelay"]

            current = ZTimelapse(post_roll=postRoll,
                                 retraction_zhop=retractionZHop,
                                 min_delay=minDelay,
                                 fps=fps)

        elif "timed" == type:
            interval = 10
            if "options" in config and "interval" in config[
                    "options"] and config["options"]["interval"] > 0:
                interval = config["options"]["interval"]

            current = TimedTimelapse(post_roll=postRoll,
                                     interval=interval,
                                     fps=fps)

    notify_callbacks(current)

    if persist:
        settings().set(["webcam", "timelapse"], config)
        settings().save()
Exemple #55
0
 def __init__(self):
     self.settings = settings()
Exemple #56
0
def last_modified_finished():
    return os.stat(settings().getBaseFolder("timelapse",
                                            check_writable=False)).st_mtime
Exemple #57
0
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().getBoolean(["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)
Exemple #58
0
 def _initSettings(self, configfile, basedir):
     settings(init=True, basedir=basedir, configfile=configfile)
Exemple #59
0
	def __init__(self):
		self.broadcastTraffic = 0 #Number of clients that wish to receive serial link traffic
		self.doIdleTempReports = True #Let's the client know if periodic temperature reports should be queries to the printer

		self._comm = None
		self._selectedFile = None
		self._printAfterSelect = False
		self._currentZ = None
		self._progress = None
		self._printTime = None
		self._printTimeLeft = None
		self._currentLayer = None
		self._currentPrintJobId = None

		self._profileManager = printerProfileManager()

		self._fileManager= printFileManagerMap[self._fileManagerClass.name]()
		self._fileManager.registerCallback(self)
		self._state = self.STATE_NONE
		self._logger = logging.getLogger(__name__)

		self._temp = {}
		self._bedTemp = None
		self._temps = deque([], 300)
		self._shutdown = False

		self._messages = deque([], 300)

		# callbacks
		self._callbacks = []
		#self._lastProgressReport = None

		self._stateMonitor = StateMonitor(
			ratelimit= 1.0,
			updateCallback= self._sendCurrentDataCallbacks,
			addTemperatureCallback= self._sendAddTemperatureCallbacks,
			addLogCallback= self._sendAddLogCallbacks,
			addMessageCallback= self._sendAddMessageCallbacks
		)

		self._stateMonitor.reset(
			state={"text": self.getStateString(), "flags": self._getStateFlags()},
			jobData={
				"file": {
					"name": None,
					"size": None,
					"origin": None,
					"date": None
				},
				"estimatedPrintTime": None,
				"filament": {
					"length": None,
					"volume": None
				}
			},
			progress={"completion": None, "filepos": None, "printTime": None, "printTimeLeft": None},
			currentZ=None
		)

		eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED, self.onMetadataAnalysisFinished);

		s = settings()

		# don't try to connect when the device hasn't been setup
		if not s.getBoolean(['server', 'firstRun']):
			self.connect(s.get(["serial", "port"]), s.get(["serial", "baudrate"]))
	def _generate_device_registration(self):

		s = settings()
		name_defaults = dict(appearance=dict(name="OctoPrint"))

		_node_name = s.get(["appearance", "name"], defaults=name_defaults)
		_node_uuid = self._settings.get(["unique_id"])
		_node_id = (_node_uuid[:6]).upper()

		_config_device = {
			"ids": [_node_id],
			"cns": [["mac", self._get_mac_address()]],
			"name": _node_name,
			"mf": "Clifford Roche",
			"mdl": "HomeAssistant Discovery for OctoPrint",
			"sw": self._plugin_version
		}

		##~~ Configure Connected Sensor

		_topic_connected = "homeassistant/binary_sensor/" + _node_id + "_CONNECTED/config"
		_config_connected = {
			"name": _node_name + " Connected",
			"uniq_id": _node_id + "_CONNECTED",
			"stat_t": "~" + self._generate_topic("eventTopic", "Connected"),
			"json_attr_t": "~" + self._generate_topic("eventTopic", "Connected"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"pl_on": "Connected",
			"pl_off": "Disconnected",
			"val_tpl": '{{value_json._event}}',
			"device": _config_device,
			"dev_cla": "connectivity",
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_connected, _config_connected, allow_queueing=True)

		##~~ Configure Printing Sensor

		_topic_printing = "homeassistant/binary_sensor/" + _node_id + "_PRINTING/config"
		_config_printing = {
			"name": _node_name + " Printing",
			"uniq_id": _node_id + "_PRINTING",
			"stat_t": "~" + self._generate_topic("progressTopic", "printing"),
			"json_attr_t": "~" + self._generate_topic("progressTopic", "printing"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"pl_on": "True",
			"pl_off": "False",
			"val_tpl": '{{value_json.progress > 0}}',
			"device": _config_device,
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_printing, _config_printing, allow_queueing=True)

		##~~ Configure Last Event Sensor

		_topic_last_event = "homeassistant/sensor/" + _node_id + "_EVENT/config"
		_config_last_event = {
			"name": _node_name + " Last Event",
			"uniq_id": _node_id + "_EVENT",
			"stat_t": "~" + self._generate_topic("eventTopic", "+"),
			"json_attr_t": "~" + self._generate_topic("eventTopic", "+"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"val_tpl": "{{value_json._event}}",
			"device": _config_device,
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_last_event, _config_last_event, allow_queueing=True)

		##~~ Configure Print Status

		_topic_printing_p = "homeassistant/sensor/" + _node_id + "_PRINTING_P/config"
		_config_printing_p = {
			"name": _node_name + " Print Progress",
			"uniq_id": _node_id + "_PRINTING_P",
			"stat_t": "~" + self._generate_topic("progressTopic", "printing"),
			"json_attr_t": "~" + self._generate_topic("progressTopic", "printing"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"unit_of_meas": "%",
			"val_tpl": "{{value_json.progress|float|default(0,true)}}",
			"device": _config_device,
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_printing_p, _config_printing_p, allow_queueing=True)

		##~~ Configure Print File

		_topic_printing_f = "homeassistant/sensor/" + _node_id + "_PRINTING_F/config"
		_config_printing_f = {
			"name": _node_name + " Print File",
			"uniq_id": _node_id + "_PRINTING_F",
			"stat_t": "~" + self._generate_topic("progressTopic", "printing"),
			"json_attr_t": "~" + self._generate_topic("progressTopic", "printing"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"val_tpl": "{{value_json.path}}",
			"device": _config_device,
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_printing_f, _config_printing_f, allow_queueing=True)

		##~~ Configure Slicing Status

		_topic_slicing_p = "homeassistant/sensor/" + _node_id + "_SLICING_P/config"
		_config_slicing_p = {
			"name": _node_name + " Slicing Progress",
			"uniq_id": _node_id + "_SLICING_P",
			"stat_t": "~" + self._generate_topic("progressTopic", "slicing"),
			"json_attr_t": "~" + self._generate_topic("progressTopic", "slicing"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"unit_of_meas": "%",
			"val_tpl": "{{value_json.progress|float|default(0,true)}}",
			"device": _config_device,
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_slicing_p, _config_slicing_p, allow_queueing=True)

		##~~ Configure Slicing File

		_topic_slicing_f = "homeassistant/sensor/" + _node_id + "_SLICING_F/config"
		_config_slicing_f = {
			"name": _node_name + " Slicing File",
			"uniq_id": _node_id + "_SLICING_F",
			"stat_t": "~" + self._generate_topic("progressTopic", "slicing"),
			"json_attr_t": "~" + self._generate_topic("progressTopic", "slicing"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"val_tpl": "{{value_json.source_path}}",
			"device": _config_device,
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_slicing_f, _config_slicing_f, allow_queueing=True)

		##~~ Tool Temperature
		_e = self._printer_profile_manager.get_current_or_default()["extruder"]["count"]
		for x in range(_e):
			_topic_e_temp = "homeassistant/sensor/" + _node_id + "_TOOL" + str(x) + "/config"
			_config_e_temp = {
				"name": _node_name + " Tool " + str(x) + " Temperature",
				"uniq_id": _node_id + "_TOOL" + str(x),
				"stat_t": "~" + self._generate_topic("temperatureTopic", "tool" + str(x)),
				"json_attr_t": "~" + self._generate_topic("temperatureTopic", "tool" + str(x)),
				"avty_t": "~mqtt",
				"pl_avail": "connected",
				"pl_not_avail": "disconnected",
				"unit_of_meas": "°C",
				"val_tpl": "{{value_json.actual|float}}",
				"device": _config_device,
				"dev_cla": "temperature",
				"~": self._generate_topic("baseTopic", "", full=True)
			}

			self.mqtt_publish(_topic_e_temp, _config_e_temp, allow_queueing=True)

		##~~ Bed Temperature

		_topic_bed_temp = "homeassistant/sensor/" + _node_id + "_BED/config"
		_config_bed_temp = {
			"name": _node_name + " Bed Temperature",
			"uniq_id": _node_id + "_BED",
			"stat_t": "~" + self._generate_topic("temperatureTopic", "bed"),
			"json_attr_t": "~" + self._generate_topic("temperatureTopic", "bed"),
			"avty_t": "~mqtt",
			"pl_avail": "connected",
			"pl_not_avail": "disconnected",
			"unit_of_meas": "°C",
			"val_tpl": "{{value_json.actual|float}}",
			"device": _config_device,
			"dev_cla": "temperature",
			"~": self._generate_topic("baseTopic", "", full=True)
		}

		self.mqtt_publish(_topic_bed_temp, _config_bed_temp, allow_queueing=True)

		##~~ For people who do not have retain setup, need to do this again to make sensors available
		_connected_topic = self._generate_topic("lwTopic", "", full=True)
		self.mqtt_publish(_connected_topic, "connected", allow_queueing=True)