import boto3 from boto3.dynamodb.conditions import Key, Attr import logging import subprocess import os import glob import json from mutagen.mp3 import MP3 import math from time import sleep import os.path import sys from time import strftime from PIL import Image, ImageDraw, ImageFont from os import listdir from os.path import isfile, join try: #from picamera import PiCamera from gpiozero import LED, Button #from gpiozero import LED test_environment = False except (ImportError, RuntimeError): #import cv2 test_environment = True import cups import requests import sqlite3 from datetime import datetime from config import Configuration # --------- Module- Level Globals --------------- config = Configuration() print(config.__SQS_BACKEND_QUEUE__) # --------- End of Module-Level Globals ---------- ''' __CEREBRO_TEMP_DIR__ = "/tmp/project_cerebro" __CEREBRO_LOGS_DIR__ = "/tmp/project_cerebro/logs" __CEREBRO_MEDIA_DIR__ = "/tmp/project_cerebro/media" __CEREBRO_SYSTEM_DIR__ = "/tmp/project_cerebro/system" __CEREBRO_AUDIO_DIR__ = "../assets/audio" __SQS_QUEUE_NAME__ = 'cerebro_client' __SQS_BACKEND_QUEUE_NAME__ = 'cerebro_backend's __GREEN_LED__ = 27 __GREEN_BUTTON__ = 17 __YELLOW_LED__ = 26 __YELLOW_BUTTON__ = 16 __PUSHBUTTON_DELAY__ = 300 __PRINTER_TYPE__ = "selphy" __APIGW_X_API_KEY__ = "2YSoTXerhD4u3iat9CWUM9kn756MTJIp4c4Tgfqk" __APIGW_X_API_KEY_QR_CODE__ = "aLCOemQkKa6EzcOQU6xjI8T2hRzD4Cf050EwWdb1" __APIGW_API__ = "https://lqhvtjhlsc.execute-api.us-east-1.amazonaws.com/production/Cerebro_GetImages_S3List" __APIGW_API_QR_CODE__ = "https://lqhvtjhlsc.execute-api.us-east-1.amazonaws.com/production/Cerebro_GetQRCode" # AWS RESOURCE: Switch __QUEUE_URL__ = "https://queue.amazonaws.com/456038447595/cerebro_client" __SQS_QUEUE_NAME__ = 'cerebro_client' __SQS_BACKEND_QUEUE__ = 'cerebro_backend' __APIGW_X_API_KEY__ = "2YSoTXerhD4u3iat9CWUM9kn756MTJIp4c4Tgfqk" __APIGW_X_API_KEY_QR_CODE__ = "aLCOemQkKa6EzcOQU6xjI8T2hRzD4Cf050EwWdb1" __APIGW_API__ = "https://lqhvtjhlsc.execute-api.us-east-1.amazonaws.com/production/Cerebro_GetImages_S3List" __APIGW_API_QR_CODE__ = "https://lqhvtjhlsc.execute-api.us-east-1.amazonaws.com/production/Cerebro_GetQRCode" __S3_BUCKET__ = "project-cerebro" __CEREBRO_TEMP_DIR__ = "/tmp/project_cerebro" __CEREBRO_MEDIA_DIR__ = "/tmp/project_cerebro/media" __CEREBRO_LOGS_DIR__ = "/tmp/project_cerebro/logs" __CEREBRO_PROFILES_DIR__ = "/tmp/project_cerebro/profiles" __CEREBRO_SYSTEM_DIR__ = "/tmp/project_cerebro/system" __IMAGE_MAX_COUNT__ = 100 __GREEN_LED__ = 27 __GREEN_BUTTON__ = 17 __YELLOW_LED__ = 26 __YELLOW_BUTTON__ = 16 ''' def control_display(enable_display=False): control_display_logger = logging.getLogger('cerebro_utils.control_display') control_display_logger.info("Entered the control_display method ...") if enable_display: display_cmd = "vcgencmd display_power 1" else: display_cmd = "vcgencmd display_power 0" control_display_logger.info("Calling the display control now ...") status = subprocess.call( '%s' % (display_cmd), shell=True) control_display_logger.info("Display control now complete!") return True def show_images(media_dir=config.__CEREBRO_MEDIA_DIR__, debug_mode=False): show_images_logger = logging.getLogger('cerebro_utils.show_images') show_images_logger.info("Entered the switch_images method ...") if debug_mode: show_images_logger.info("In Debug mode, so no showing of images!") return True show_images_logger.info("And now, starting up the slideshow again ...") #slideshow_stop_util = "../scripts/stop-picture-frame.sh" slideshow_stop_util = "../scripts/shutoff-display.sh" slideshow_util = "../scripts/start-picture-frame.sh" slideshow_log = "%s/picframe.start.log" % config.__CEREBRO_LOGS_DIR__ status = subprocess.call( '%s && %s "%s" > %s 2>&1 &' % (slideshow_stop_util, slideshow_util, media_dir, slideshow_log), shell=True) show_images_logger.info("Switching of images is now complete!") return True def generate_audio(voice_id='Joanna', speech_text='sample text', filename='speech.mp3'): generate_audio_logger = logging.getLogger('cerebro_utils.generate_audio') generate_audio_logger.info("Starting the generate_audio method in cerebro_utils ...") polly_client = boto3.Session().client('polly') generate_audio_logger.info("Now, going to synthesize_speech with polly") speech_tokens = speech_text.split() token_cnt = len(speech_tokens) if token_cnt > 10: mid_token = int(token_cnt/2) part_one = " ".join(speech_tokens[:mid_token]) part_two = " ".join(speech_tokens[mid_token+1:]) speech_text_with_buffer = '%s%s' %\ (part_one, part_two) else: speech_text_with_buffer = '' + speech_text + '' file_path = "%s/%s" % (config.__CEREBRO_AUDIO_DIR__, filename) if os.path.isfile(file_path): print("\n\nFile already exists locally, not calling Polly..") else: print("\n\nFile does not exist, making it now. ") response = polly_client.synthesize_speech(VoiceId=voice_id, OutputFormat='mp3', TextType='ssml', Text = speech_text_with_buffer) generate_audio_logger.info("Next, call to polly completed - now to stream the audio file ...") file = open(file_path, 'wb') file.write(response['AudioStream'].read()) file.close() generate_audio_logger.info("Finally, audio file is written and ready @ %s" % file_path) return file_path def play_audio(file_path='', delay_secs=0, delay_to_start=0): play_audio_logger = logging.getLogger('cerebro_utils.play_audio') play_audio_logger.info("Starting the play_audio method in cerebro_utils ...") play_audio_logger.info("File_path provided : %s" % file_path) if not file_path: play_audio_logger.error("No file_path provided!") return False ''' HACK: RPi is not playing the mp3 reliably so pygame isn't working play_audio_logger.info("pygame to be started ...") #pygame.mixer.pre_init(44100, -16, 2, 2048) pygame.mixer.init() pygame.init() pygame.mixer.music.load(file_path) pygame.mixer.music.play() ''' # This is a hack to allow alexa to complete its playback if delay_to_start: sleep(delay_to_start) mp3_util = "sudo mpg321" status = subprocess.call( '%s %s' % (mp3_util, file_path), shell=True) if delay_secs: play_audio_logger.info("Sleeping for %d secs" % delay_secs) sleep(delay_secs) ''' else: audio = MP3(file_path) audio_ts = math.ceil(audio.info.length) play_audio_logger.info("Duration computed as : %d. Sleeping for this duration only ..." % audio_ts) sleep(audio_ts) ''' filename = os.path.basename(file_path).strip() if filename == "speech.mp3": # Delete the adhoc file os.remove(file_path) play_audio_logger.info("done here. audio played ?") return True def get_facial_landmarks(image_path=''): get_facial_landmarks_logger = logging.getLogger('selfie_with_filters.get_facial_landmarks') get_facial_landmarks_logger.info("In the get_facial_landmarks method ...") client=boto3.client('rekognition') get_facial_landmarks_logger.info("Running Detect Faces on the image: %s" % image_path) with open(image_path, 'rb') as image: response = client.detect_faces(Image={'Bytes': image.read()}, Attributes=['ALL']) get_facial_landmarks_logger.info('Completed the detect_faces API call' ) get_facial_landmarks_logger.info(response) if "FaceDetails" not in response: get_facial_landmarks_logger.error("No Faces found!") return False return response["FaceDetails"] def generate_print_image(image_selected="", \ bkgrnd_image="../assets/printing/reinvent_bkgrnd.jpg", \ logo_image="../assets/printing/reinventLogo.png"): generate_print_image_logger = logging.getLogger('rpi_utils.generate_print_image') generate_print_image_logger.info("In generate_print_image ...") if not bkgrnd_image: generate_print_image_logger.error("No background image provided") return False elif not image_selected: generate_print_image_logger.error("No image selected") return False elif not logo_image: generate_print_image_logger.error("No logo provided") return False generate_print_image_logger.info("All images provided correctly!") # first load the images image1 = Image.open(bkgrnd_image) #image2 = Image.open("/Users/sacholla/Downloads/large_user4515692_2951488_757.jpg") image2 = Image.open(image_selected) imageLogo = Image.open(logo_image) generate_print_image_logger.info(image1.size) generate_print_image_logger.info(image2.size) generate_print_image_logger.info(imageLogo.size) # Now, crop the selfie picture selfie_size = (image2.size[0]*0.875, image2.size[1]*0.875 ) image2.thumbnail(selfie_size, Image.ANTIALIAS) image2_rotated = image2.rotate(10) logo_size = (imageLogo.size[0]*0.25, imageLogo.size[1]*0.25) imageLogo.thumbnail(logo_size, Image.ANTIALIAS) background_size = (int(1920*0.975), int(1080*0.975)) generate_print_image_logger.info(background_size) background_image = image1.resize(background_size, Image.ANTIALIAS) im3_copy = background_image.copy() image_box = (int(im3_copy.size[0]*0.035), int(im3_copy.size[1]*0.1)) generate_print_image_logger.info(image_box) im3_copy.paste(image2, image_box) fnt = ImageFont.truetype('Arial_Bold', 48) d = ImageDraw.Draw(im3_copy) title_string = "Cerebro: A Connected Photobooth" title_box = (image_box[0]+105, image_box[1]-80) d.text(title_box, title_string, font=fnt, fill=(255,255,0)) logo_box = (image_box[0], image_box[1]-105) generate_print_image_logger.info(logo_box) im3_copy.paste(imageLogo, logo_box, imageLogo) fnt2 = ImageFont.truetype('Verdana', 18) d2 = ImageDraw.Draw(im3_copy) title_string = "Nov 16 2019 12:30:05 (PST)" title_box = (background_size[0]-400, background_size[1]-40) d2.text(title_box, title_string, font=fnt2, fill=(255,255,255)) #im3_copy.show() generated_print_image_path = "%s/print_image.jpg" % config.__CEREBRO_MEDIA_DIR__ im3_copy.save(generated_print_image_path) return generated_print_image_path def print_image(fileName=''): print_image_logger = logging.getLogger('rpi_utils.print_image') print_image_logger.info("Attempting to run a print job ...") # now generate the print image generated_image = generate_print_image(image_selected=fileName) print_image_logger.info("Generated Print Image: %s" % generated_image) # for now just test the underlying function only #return fileName = generated_image conn = cups.Connection() printers = conn.getPrinters() printer_name = "" for printer in printers: print_image_logger.info(printer, printers[printer]["device-uri"]) if config.__PRINTER_TYPE__ in printer.lower(): printer_name = printer #printer_name=list(printers.keys())[1] #fileName = "PollyResponse.txt" #fileName = "../assets/stock/Simone-Biles.jpg" print_image_logger.info("The Printer Chosen: %s , The Image Chosen is in path: %s" % \ (printer_name, fileName)) print_image_logger.info("Triggering the print job now for %s ..." % fileName) conn.printFile(printer_name, fileName, " ", {}) print_image_logger.info("Completed the print job!") return def download_media(profile_name="", media_dir='', ignore_stock_profiles=False): download_media_logger = logging.getLogger('cerebro_processor.download_media') download_media_logger.info("Entered Download_media ...") # Create an S3 client s3 = boto3.resource('s3') download_media_logger.info("Downloading Media now ...") payload={} payload['profile']=profile_name payload['audio']='yes' payload['image_max_count'] = str(config.__IMAGE_MAX_COUNT__) if ignore_stock_profiles: payload['ignore_stock_profiles'] = "1" headers = { 'Content-Type': "application/json", 'x-api-key': config.__APIGW_X_API_KEY__ } url = config.__APIGW_API__ download_media_logger.info("URL: %s, payload: %s" % (url, json.dumps(payload)) ) response = requests.request( "POST", url, data=json.dumps(payload), headers=headers) s3_bucket = config.__S3_BUCKET__ s3_key = "" download_media_logger.debug("Response: %s" % (json.dumps(response.json())) ) media_count = len(response.json()) download_media_logger.info("Total Number of Media files: %d" % media_count ) for item in response.json(): download_media_logger.info("Processing Media: %s ..." % item["image_key"]) # now download the s3 files one by one s3_key = item["image_key"] media_caption = item["image_caption"] if media_dir: local_file = "%s/%s" % (media_dir, os.path.basename(s3_key)) else: local_file = "%s/%s" % (config.__CEREBRO_MEDIA_DIR__, os.path.basename(s3_key)) download_media_logger.info("Try downloading file: %s" % s3_key) try: s3.Bucket(s3_bucket).download_file(s3_key, local_file) except botocore.exceptions.ClientError as e: print("Error seen!") if e.response['Error']['Code'] == "404": print("The object does not exist.") else: raise download_media_logger.info("Image Downloaded to %s." % local_file) download_media_logger.info("Media downloaded.") return media_count # Change - on 11/28/2019 - by Sachin - Start def persist_setting(setting_name='setting_name', setting_value='setting_value'): persist_setting_logger = logging.getLogger('cerebro_utils.persist_setting') persist_setting_logger.info("In persist_setting ...") if not setting_name: persist_setting_logger.error("Invalid parameter provided!") return False db_file = "cerebro.db" conn = sqlite3.connect(db_file) c = conn.cursor() persist_setting_logger.info("Created the DB connection...") # first delete the old entry dml_sql = """ DELETE from local_settings where setting_name = '%s' """ % setting_name persist_setting_logger.info("Now , executing sql: '%s', with params." % dml_sql) c.execute(dml_sql) conn.commit() # next insert the new entry dml_sql = """ INSERT INTO local_settings(setting_name,setting_value,last_updated,last_updated_dt) VALUES(?,?,?,?)""" epoch_time = datetime(1970,1,1) utc_time = datetime.now() last_updated = int((utc_time-epoch_time).total_seconds()) last_updated_dt = utc_time.strftime("%m-%d-%YT%H:%M:%S.%f") settings_entry = (setting_name, setting_value, last_updated, last_updated_dt) persist_setting_logger.info("Now , executing sql: '%s', with params." % dml_sql) c.execute(dml_sql, settings_entry) conn.commit() persist_setting_logger.info("Setting is persisted into the db!") return True def retrieve_setting(setting_name='setting_name'): retrieve_setting_logger = logging.getLogger('cerebro_utils.retrieve_setting') retrieve_setting_logger.info("In retrieve_setting ...") if not setting_name: retrieve_setting_logger.error("Invalid parameter provided!") return False db_file = "cerebro.db" conn = sqlite3.connect(db_file) c = conn.cursor() retrieve_setting_logger.info("Created the DB connection...") dml_sql = """ SELECT setting_value FROM local_settings WHERE setting_name='%s' """ % setting_name retrieve_setting_logger.info("Now , executing sql: '%s' ." % dml_sql) c.execute(dml_sql) setting_value = None rows = c.fetchall() if len(rows) >= 1: if len(rows[0]) >= 1: setting_value = rows[0][0] retrieve_setting_logger.info("Now , retrieved the setting value as: %s ." % setting_value) if not setting_value: setting_value = "" return setting_value # Change - on 11/29/2019 - by Sachin - End