diff --git a/src/printer.py b/src/printer.py index 7a03101..99022c4 100644 --- a/src/printer.py +++ b/src/printer.py @@ -34,31 +34,33 @@ class Printer(object): self.device_id = device_id self.vendor_id = vendor_id self.usb_args = {} - self.usb_args['idVendor'] = self.device_id - self.usb_args['idProduct'] = self.vendor_id + self.usb_args["idVendor"] = self.device_id + self.usb_args["idProduct"] = self.vendor_id def check_paper(self) -> bool: # Let's check paper status - self.app.logger.debug('Checking paper status...') + self.app.logger.debug("Checking paper status...") self.printer.open(self.usb_args) status = self.printer.paper_status() match status: case 0: - self.app.logger.error('Printer has no more paper, aborting...') + self.app.logger.error("Printer has no more paper, aborting...") self.printer.close() raise Exception("No more paper in the printer") case 1: - self.app.logger.warning('Printer needs paper to be changed very soon ! ') + self.app.logger.warning( + "Printer needs paper to be changed very soon ! " + ) self.printer.close() case 2: - self.app.logger.debug('Printer has paper, good to go') + self.app.logger.debug("Printer has paper, good to go") self.printer.close() def init_printer(self): # Is the printer online ? Is the communication with the printer successfull ? waiting_elapsed = 30 - self.app.logger.debug('Waiting for printer to get online...') + self.app.logger.debug("Waiting for printer to get online...") while not self.ready: try: @@ -66,56 +68,83 @@ class Printer(object): # or else the device will appear as busy. p = Usb(self.device_id, self.vendor_id, 0, profile="TM-P80") except USBNotFoundError as e: - self.app.logger.error("The USB device is not plugged in, trying again : " + str(e)) + self.app.logger.error( + "The USB device is not plugged in, trying again : " + str(e) + ) pass try: if p.is_online(): self.ready = True - self.app.logger.debug('Printer online !') + self.app.logger.debug("Printer online !") except Exception as e: pass sleep(1) waiting_elapsed -= 1 if waiting_elapsed < 1: - self.app.logger.error('Printer took more than 30 seconds to get online, aborting...') - waiting_elapsed = 30 # Reset the waiting time for the next print. + self.app.logger.error( + "Printer took more than 30 seconds to get online, aborting..." + ) + waiting_elapsed = 1 # Reset the waiting time for the next print. return False - - - # Setting up the printing options. - p.set(align='center', font='a', bold=False, underline=0, width=1, height=1, density=9, invert=False, smooth=False, flip=False, double_width=False, double_height=False, custom_size=False) + p.set( + align="center", + font="a", + bold=False, + underline=0, + width=1, + height=1, + density=9, + invert=False, + smooth=False, + flip=False, + double_width=False, + double_height=False, + custom_size=False, + ) # Beware : if we print every time the printer becomes ready, it means # we are printing before and after every print ! self.printer = p - self.ready = True; - self.printer.close(); + self.ready = True + self.printer.close() self.check_paper() return True - def print_sms(self, msg, signature="",bold=False): + def print_sms(self, msg, signature="", bold=False): clean_msg = str(msg) + "\n" clean_signature = str(signature) if len(clean_msg) > 4096: - self.app.logger.warning("Could not print message of this length: " + str(len(clean_msg))) - raise Exception("Could not print message of this length :" + str(len(clean_msg)) + ", needs to be below 4096 caracters long.") + self.app.logger.warning( + "Could not print message of this length: " + str(len(clean_msg)) + ) + raise Exception( + "Could not print message of this length :" + + str(len(clean_msg)) + + ", needs to be below 4096 caracters long." + ) if len(signature) > 256: - self.app.logger.warning("Could not print signature of this length: " + str(len(clean_signature))) - raise Exception("Could not print signature of this length :" + str(len(clean_signature)) + ", needs to be below 256 caracters long.") + self.app.logger.warning( + "Could not print signature of this length: " + str(len(clean_signature)) + ) + raise Exception( + "Could not print signature of this length :" + + str(len(clean_signature)) + + ", needs to be below 256 caracters long." + ) try: - self.printer.open(self.usb_args); - self.printer.set(align='center', font='a', bold=bold) - self.printer.textln(clean_msg ) + self.printer.open(self.usb_args) + self.printer.set(align="center", font="a", bold=bold) + self.printer.textln(clean_msg) if clean_signature: - self.printer.textln(clean_signature ) + self.printer.textln(clean_signature) self.printer.close() except Exception as e: self.app.logger.error("Unable to print because : " + str(e)) @@ -123,16 +152,26 @@ class Printer(object): self.app.logger.info("Printed text") return True - def print_img(self, path, sign="",center=True,process=False): + def print_img(self, path, sign="", center=True, process=False): clean_signature = str(sign) if len(sign) > 256: - self.app.logger.warning("Could not print signature of this length: " + str(len(clean_signature))) - raise Exception("Could not print signature of this length :" + str(len(clean_signature)) + ", needs to be below 256 caracters long.") + self.app.logger.warning( + "Could not print signature of this length: " + str(len(clean_signature)) + ) + raise Exception( + "Could not print signature of this length :" + + str(len(clean_signature)) + + ", needs to be below 256 caracters long." + ) if not os.path.isfile(str(path)): self.app.logger.warning("File does not exist : " + str(path)) - raise Exception('The file path for this image :' + str(path) + " wasn't found. Please try again.") + raise Exception( + "The file path for this image :" + + str(path) + + " wasn't found. Please try again." + ) else: self.app.logger.debug("Printing file from " + str(path)) @@ -146,10 +185,9 @@ class Printer(object): else: self.app.logger.warning("Not proccessing the image") - try: self.printer.open(self.usb_args) - self.printer.image(path,center=center) + self.printer.image(path, center=center) self.printer.close() self.app.logger.debug("Printed an image : " + str(path)) os.remove(path) @@ -175,8 +213,6 @@ class Printer(object): self.app.logger.info("Printed a QR") return True - - def cut(self): try: self.printer.open(self.usb_args) @@ -194,7 +230,7 @@ class Printer(object): def process_image(self, path): brightness_factor = 1.5 # Used only if image is too dark brightness_threshold = 100 # Brightness threshold (0–255) - contrast_factor = 0.6 # Less than 1.0 = lower contrast + contrast_factor = 0.6 # Less than 1.0 = lower contrast max_width = 575 max_height = 1000 @@ -215,13 +251,21 @@ def process_image(self, path): # Compute brightness of original image (grayscale average) grayscale = original_img.convert("L") avg_brightness = np.array(grayscale).mean() - self.app.logger.debug("Average brightness of the image : " + str(avg_brightness) ) + self.app.logger.debug( + "Average brightness of the image : " + str(avg_brightness) + ) # Dynamically compute brightness factor if too dark if avg_brightness < brightness_threshold: - brightness_factor = 1 + (brightness_threshold - avg_brightness) / brightness_threshold - brightness_factor = min(max(brightness_factor, 1.1), 2.5) # Clamp between 1.1 and 2.5 - self.app.logger.debug(f"Image too dark, increasing brightness by a factor of {brightness_factor:.2f}") + brightness_factor = ( + 1 + (brightness_threshold - avg_brightness) / brightness_threshold + ) + brightness_factor = min( + max(brightness_factor, 1.1), 2.5 + ) # Clamp between 1.1 and 2.5 + self.app.logger.debug( + f"Image too dark, increasing brightness by a factor of {brightness_factor:.2f}" + ) enhancer = ImageEnhance.Brightness(original_img) original_img = enhancer.enhance(brightness_factor) @@ -232,11 +276,13 @@ def process_image(self, path): # Final resize check if original_img.height > max_height: raise ValueError("Image is too long, sorry! Keep it below 575×1000 pixels.") - self.app.logger.error("Image is too long, sorry! Keep it below 575×1000 pixels.") + self.app.logger.error( + "Image is too long, sorry! Keep it below 575×1000 pixels." + ) return False # Convert to JPEG and save jpeg_path = os.path.splitext(path)[0] + "_processed.jpg" - original_img.save(jpeg_path, format='JPEG', quality=95, optimize=True) + original_img.save(jpeg_path, format="JPEG", quality=95, optimize=True) return jpeg_path diff --git a/src/raspberry.py b/src/raspberry.py index 246effa..d6b932d 100644 --- a/src/raspberry.py +++ b/src/raspberry.py @@ -2,10 +2,11 @@ from flask_socketio import SocketIO from gpiozero import Button, LED, DigitalOutputDevice from time import sleep, gmtime, strftime from PIL import Image -import io # To check if we are on a Raspberry Pi +import io # To check if we are on a Raspberry Pi import subprocess import os + class Raspberry(object): """ This class will manage three things : @@ -15,7 +16,17 @@ class Raspberry(object): - Flash an indicator light """ - def __init__(self, printer, app, socketio, button_gpio_port_number, indicator_gpio_port_number, flash_gpio_port_number, is_flash_present,): + + def __init__( + self, + printer, + app, + socketio, + button_gpio_port_number, + indicator_gpio_port_number, + flash_gpio_port_number, + is_flash_present, + ): self.printer = printer self.socketio = socketio self.app = app @@ -24,67 +35,71 @@ class Raspberry(object): self.is_flash_present = is_flash_present self.button_gpio = button_gpio_port_number self.led_gpio = indicator_gpio_port_number - self.image_path = self.app.config['UPLOAD_FOLDER'] + '/image.jpg' + self.image_path = self.app.config["UPLOAD_FOLDER"] + "/image.jpg" def is_raspberry_pi(self, raise_on_errors=False): # Check if we are running on a raspberry pi try: - with io.open('/proc/cpuinfo', 'r') as cpuinfo: + with io.open("/proc/cpuinfo", "r") as cpuinfo: found = False for line in cpuinfo: - if line.startswith('Hardware'): + if line.startswith("Hardware"): found = True - label, value = line.strip().split(':', 1) + label, value = line.strip().split(":", 1) value = value.strip() if value not in ( - 'BCM2708', - 'BCM2709', - 'BCM2711', - 'BCM2835', - 'BCM2836' + "BCM2708", + "BCM2709", + "BCM2711", + "BCM2835", + "BCM2836", ): - self.app.logger.debug('This system does not appear to be a Raspberry Pi.') + self.app.logger.debug( + "This system does not appear to be a Raspberry Pi." + ) return False if not found: - self.app.logger.error('Couldn\'t get sufficient hardware information from /proc/cpuinfo, Unable to determine if we are on a Raspberry Pi.') + self.app.logger.error( + "Couldn't get sufficient hardware information from /proc/cpuinfo, Unable to determine if we are on a Raspberry Pi." + ) return False except IOError: - self.app.logger.error('Unable to open `/proc/cpuinfo`.') + self.app.logger.error("Unable to open `/proc/cpuinfo`.") return False - self.app.logger.debug('It seems we are on a Raspberry Pi') + self.app.logger.debug("It seems we are on a Raspberry Pi") try: self.initialise_gpio() except Exception as e: - self.app.logger.debug('Could not init GPIO : ' + str(e)) + self.app.logger.debug("Could not init GPIO : " + str(e)) raise e return True def initialise_gpio(self): - self.app.logger.debug('Initializing GPIO') + self.app.logger.debug("Initializing GPIO") self.led = LED(self.led_gpio) - self.app.logger.debug('Activated indicator LED') + self.app.logger.debug("Activated indicator LED") self.indicator_countdown(iters=3) self.button = Button(self.button_gpio, pull_up=True, bounce_time=0.1) self.button.when_pressed = self.on_button_pressed - self.app.logger.debug('Activated button') + self.app.logger.debug("Activated button") # The "flash" is a relay-controlled device ( light bulb for example ) self.flash = DigitalOutputDevice(self.flash_gpio) self.flash_toggle() - self.app.logger.debug('Activated flash') + self.app.logger.debug("Activated flash") - def indicator_countdown(self,iters=10,multi=10): - for i in range(iters,0,-1): + def indicator_countdown(self, iters=10, multi=10): + for i in range(iters, 0, -1): self.led.on() - sleep(i/multi) + sleep(i / multi) self.led.off() - sleep(i/multi) + sleep(i / multi) - def indicator_led(self,timing=0.2,l=5): + def indicator_led(self, timing=0.2, l=5): for i in range(l): self.app.logger.debug("LED turned on") self.led.on() @@ -103,22 +118,28 @@ class Raspberry(object): def take_picture(self): # Validate if the image path is valid if not os.path.isdir(os.path.dirname(self.image_path)): - self.app.logger.error(f"Invalid directory for image path: {self.image_path}") + self.app.logger.error( + f"Invalid directory for image path: {self.image_path}" + ) return False try: result = subprocess.run( - ['fswebcam', '--no-banner', '-r', '1920x1080', self.image_path], + ["fswebcam", "--no-banner", "-r", "1920x1080", self.image_path], check=True, # Will raise CalledProcessError if the command fails stdout=subprocess.PIPE, # Capture standard output - stderr=subprocess.PIPE # Capture error output + stderr=subprocess.PIPE, # Capture error output ) # Optionally log the command output - self.app.logger.debug(f"Image captured successfully: {result.stdout.decode()}") + self.app.logger.debug( + f"Image captured successfully: {result.stdout.decode()}" + ) except subprocess.CalledProcessError as e: # Log error output if available - self.app.logger.error(f"Unable to take a picture. Error: {e.stderr.decode()}") + self.app.logger.error( + f"Unable to take a picture. Error: {e.stderr.decode()}" + ) return False except Exception as e: # Catch any unexpected errors @@ -132,32 +153,42 @@ class Raspberry(object): return True - - def overlay_logo(self, image_path, logo_path, output_path=None, position='bottom_right', margin=10): + def overlay_logo( + self, + image_path, + logo_path, + output_path=None, + position="bottom_right", + margin=10, + ): try: image = Image.open(image_path).convert("RGBA") logo = Image.open(logo_path).convert("RGBA") # Resize logo if it's too big (logo will be 30% the width of the image) - logo_ratio = 0.30 # You can change the ratio if you want the logo bigger or smaller + logo_ratio = ( + 0.30 # You can change the ratio if you want the logo bigger or smaller + ) logo_width = int(image.width * logo_ratio) logo_height = int(logo.height * (logo_width / logo.width)) logo = logo.resize((logo_width, logo_height), Image.Resampling.LANCZOS) # Calculate position based on the chosen location - if position == 'bottom_right': + if position == "bottom_right": x = image.width - logo.width - margin y = image.height - logo.height - margin - elif position == 'top_left': + elif position == "top_left": x, y = margin, margin - elif position == 'top_right': + elif position == "top_right": x = image.width - logo.width - margin y = margin - elif position == 'bottom_left': + elif position == "bottom_left": x = margin y = image.height - logo.height - margin else: - raise ValueError("Invalid position. Choose from 'bottom_right', 'top_left', 'top_right', or 'bottom_left'.") + raise ValueError( + "Invalid position. Choose from 'bottom_right', 'top_left', 'top_right', or 'bottom_left'." + ) # Composite the logo onto the image image.paste(logo, (x, y), logo) # Use logo as its own alpha mask @@ -204,27 +235,31 @@ class Raspberry(object): self.app.logger.debug("Button has been pressed") self.led.on() self.app.logger.debug("Counting down") - self.indicator_countdown(iters=4,multi=20) # The indicator will flash a countdown LED + self.indicator_countdown( + iters=4, multi=20 + ) # The indicator will flash a countdown LED self.app.logger.debug("Taking picture") try: self.flash.on() self.take_picture() except Exception as e: - self.app.logger.error("Could not take a picture after the button press : " + str(e)) + self.app.logger.error( + "Could not take a picture after the button press : " + str(e) + ) finally: self.flash.off() self.app.logger.debug("Printing picture") self.led.on() self.crop_to_square(self.image_path) - self.printer.print_img('src/static/images/extase-club.png',process=True) - self.printer.print_img(self.image_path,process=True) + self.printer.print_img("src/static/images/extase-club.png", process=True) + self.printer.print_img(self.image_path, process=True) self.printer.print_sms("") - self.printer.print_sms("With Love From Société.Vide",signature="",bold=True) - self.printer.print_sms("Printed by LittlePrynter",signature="") - self.printer.print_sms("n07070.xyz",signature="") - self.printer.print_sms(strftime("%Y-%m-%d %H:%M", gmtime()),signature="") + self.printer.print_sms("With Love From Société.Vide", signature="", bold=True) + self.printer.print_sms("Printed by LittlePrynter", signature="") + self.printer.print_sms("n07070.xyz", signature="") + self.printer.print_sms(strftime("%Y-%m-%d %H:%M", gmtime()), signature="") self.printer.qr("https://n07070.xyz/articles/littleprynter") self.printer.cut() self.led.off() self.app.logger.debug("Done printing picture") - return True \ No newline at end of file + return True diff --git a/src/user.py b/src/user.py index 1776c1b..a09b875 100644 --- a/src/user.py +++ b/src/user.py @@ -5,35 +5,37 @@ class User(object): super(User, self).__init__() self.arg = arg + # @app.route('/login', methods=['POST','GET']) # @limiter.limit("100 per minute", error_message=error_handler_limiter) def login(): - if request.method == 'POST': - if not session.get('logged_in'): - if request.form['username'] and request.form['password']: + if request.method == "POST": + if not session.get("logged_in"): + if request.form["username"] and request.form["password"]: # Get the json - with open('users.json') as f: + with open("users.json") as f: users_file = json.load(f) for user in users_file["users"]: - if users_file["users"][user] == request.form['password']: - session['logged_in'] = True - session['user'] = request.form['username'] + if users_file["users"][user] == request.form["password"]: + session["logged_in"] = True + session["user"] = request.form["username"] - if not session.get('logged_in'): - flash('Mot de passe ou pseudo invalide.','danger') - return redirect(url_for('login')) + if not session.get("logged_in"): + flash("Mot de passe ou pseudo invalide.", "danger") + return redirect(url_for("login")) else: - return redirect(url_for('display_index_page')) + return redirect(url_for("display_index_page")) else: - flash('Incorrect logins') - return render_template('password.html') + flash("Incorrect logins") + return render_template("password.html") else: - return render_template('password.html') + return render_template("password.html") else: - return render_template('password.html') + return render_template("password.html") + @app.route("/logout") def logout(): - session['logged_in'] = False - flash('Tu est déconnecté', 'info') - return redirect(url_for('login')) + session["logged_in"] = False + flash("Tu est déconnecté", "info") + return redirect(url_for("login")) diff --git a/src/web.py b/src/web.py index e1a57c0..7c70beb 100644 --- a/src/web.py +++ b/src/web.py @@ -4,10 +4,11 @@ from printer import Printer import time import os + class Web(object): """docstring for web.""" - def __init__(self, app, printer ): + def __init__(self, app, printer): super(Web).__init__() self.printer = printer self.app = app @@ -20,10 +21,9 @@ class Web(object): self.printer.cut() except Exception as e: self.app.logger.error(e) - flash("Error while printing the SMS : "+ str(e)) - - flash("You message " + str( texte ) + " has been printed :)") + flash("Error while printing the SMS : " + str(e)) + flash("You message " + str(texte) + " has been printed :)") def print_image(self, image, sign): self.app.logger.debug("Uploading file") @@ -31,7 +31,14 @@ class Web(object): self.app.logger.debug("Uploading file from " + str(sign)) if self.upload_file(image): self.app.logger.debug("File has been uploaded, printing...") - self.printer.print_img(os.path.join(self.app.config['UPLOAD_FOLDER'], secure_filename(image.filename)), sign=sign,process=True) + self.printer.print_img( + os.path.join( + self.app.config["UPLOAD_FOLDER"], + secure_filename(image.filename), + ), + sign=sign, + process=True, + ) self.printer.cut() except Exception as e: self.app.logger.error(e) @@ -39,7 +46,7 @@ class Web(object): flash("Your image has been printed :)") - def login(username: str,password: str) -> bool: + def login(username: str, password: str) -> bool: pass def logout(username: str, password: str) -> bool: @@ -47,22 +54,29 @@ class Web(object): def allowed_file(self, filename) -> bool: self.app.logger.debug("Is the filename allowed ?") - return '.' in filename and filename.rsplit('.', 1)[1].lower() in self.app.config['ALLOWED_EXTENSIONS'] + return ( + "." in filename + and filename.rsplit(".", 1)[1].lower() + in self.app.config["ALLOWED_EXTENSIONS"] + ) - def upload_file(self, image)-> bool: + def upload_file(self, image) -> bool: self.app.logger.debug("Validating file") if image and self.allowed_file(image.filename): filename = secure_filename(image.filename) self.app.logger.debug("File valid") try: - image.save(os.path.join(self.app.config['UPLOAD_FOLDER'], filename)) + image.save(os.path.join(self.app.config["UPLOAD_FOLDER"], filename)) self.app.logger.debug("File saved") except Exception as e: self.app.logger.error("Could not save file") - flash(str(e),'error') + flash(str(e), "error") return False - self.app.logger.debug("File saved to " + str(os.path.join(self.app.config['UPLOAD_FOLDER'], filename))) + self.app.logger.debug( + "File saved to " + + str(os.path.join(self.app.config["UPLOAD_FOLDER"], filename)) + ) return True else: self.app.logger.error("Could not save file " + str(filename))