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 subprocess import os class Raspberry(object): """ This class will manage three things : - Connecting to a USB webcam - Managing a push button - Activating a flash ( or light ) - 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,): self.printer = printer self.socketio = socketio self.app = app self.flash_gpio = flash_gpio_port_number 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' 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: found = False for line in cpuinfo: if line.startswith('Hardware'): found = True label, value = line.strip().split(':', 1) value = value.strip() if value not in ( 'BCM2708', 'BCM2709', 'BCM2711', 'BCM2835', 'BCM2836' ): 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.') return False except IOError: self.app.logger.error('Unable to open `/proc/cpuinfo`.') return False 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)) raise e return True def initialise_gpio(self): self.app.logger.debug('Initializing GPIO') self.led = LED(self.led_gpio) 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') # 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') def indicator_countdown(self,iters=10,multi=10): for i in range(iters,0,-1): self.led.on() sleep(i/multi) self.led.off() sleep(i/multi) def indicator_led(self,timing=0.2,l=5): for i in range(l): self.app.logger.debug("LED turned on") self.led.on() sleep(timing) self.led.off() self.app.logger.debug("LED turned off") sleep(timing) def flash_toggle(self): self.app.logger.debug("Flash turned on") self.flash.on() sleep(0.3) self.flash.off() self.app.logger.debug("Flash turned off") 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}") return False try: result = subprocess.run( ['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 ) # Optionally log the command output 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()}") return False except Exception as e: # Catch any unexpected errors self.app.logger.error(f"Unexpected error while taking picture: {str(e)}") return False # # Overlay logo # logo_path = 'src/static/images/requin.png' # Update path as needed # if not self.overlay_logo(self.image_path, logo_path): # self.app.logger.warning("Picture taken but failed to overlay logo.") return True 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_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': x = image.width - logo.width - margin y = image.height - logo.height - margin elif position == 'top_left': x, y = margin, margin elif position == 'top_right': x = image.width - logo.width - margin y = margin 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'.") # Composite the logo onto the image image.paste(logo, (x, y), logo) # Use logo as its own alpha mask # Save the result if not output_path: output_path = image_path # Overwrite the original image if no output path is given image.save(output_path) except Exception as e: self.app.logger.error(f"Error overlaying logo: {e}") return False return True def crop_to_square(self, image_path, output_path=None): try: image = Image.open(image_path) width, height = image.size # Determine shorter side new_edge = min(width, height) # Calculate cropping box (centered) left = (width - new_edge) // 2 top = (height - new_edge) // 2 right = left + new_edge bottom = top + new_edge image = image.crop((left, top, right, bottom)) if not output_path: output_path = image_path # Overwrite original image.save(output_path) except Exception as e: self.app.logger.error(f"Error cropping image to square: {e}") return False return True def on_button_pressed(self): 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.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)) 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_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.qr("https://n07070.xyz/articles/littleprynter") self.printer.cut() self.led.off() self.app.logger.debug("Done printing picture") return True