Beispiel #1
0
    def __init__(self,
                 iso=200,
                 resolution=(1920, 1080),
                 rotation=0,
                 flip=False,
                 delete_internal_memory=False):
        BaseCamera.__init__(self, resolution, delete_internal_memory)
        self._preview_hflip = False
        self._capture_hflip = flip
        self._rotation = rotation
        self._iso = iso
        self._overlay_alpha = 255

        self._cam = cv2.VideoCapture(0)
        self.preview_resolution = (self._cam.get(cv2.CAP_PROP_FRAME_WIDTH),
                                   self._cam.get(cv2.CAP_PROP_FRAME_HEIGHT))
        LOGGER.debug("Preview resolution is %s", self.preview_resolution)
        self._cam.set(cv2.CAP_PROP_ISO_SPEED, self._iso)
Beispiel #2
0
def gp_set_config_value(config, section, option, value):
    """Set camera configuration. This method don't send the updated
    configuration to the camera (avoid connection flooding if several
    values have to be changed)
    """
    try:
        LOGGER.debug('Setting option %s/%s=%s', section, option, value)
        child = config.get_child_by_name(section).get_child_by_name(option)
        choices = [c for c in child.get_choices()]
        if not choices or value in choices:
            child.set_value(str(value))
        else:
            LOGGER.warning(
                "Invalid value '%s' for option %s (possible choices: %s)",
                value, option, choices)
    except gp.GPhoto2Error:
        raise ValueError('Unsupported setting {}/{}={}'.format(
            section, option, value))
Beispiel #3
0
def get_translated_text(key):
    """Return the text corresponding to the key in the language defined in the config.

    :param key: key in the translation file
    :type key: str
    """
    if not getattr(PARSER, 'filename', None):
        raise EnvironmentError("Translation system is not initialized")

    if PARSER.has_section(CURRENT) and PARSER.has_option(CURRENT, key):
        return PARSER.get(CURRENT, key).strip('"')
    elif PARSER.has_option('en', key):
        LOGGER.warning("Unsupported language '%s', fallback to English",
                       CURRENT)
        return PARSER.get('en', key).strip('"')

    LOGGER.debug("No translation defined for '%s/%s' key", CURRENT, key)
    return None
Beispiel #4
0
 def _on_button_printer_held(self):
     """Called when the printer button is pressed.
     """
     if all(self.buttons.value):
         # Printer was held while capture was pressed
         # but don't do anything here, let capture_held handle it instead
         pass
     else:
         # Printer was held but capture not pressed
         if self._menu and self._menu.is_shown():
             # Convert HW button events to keyboard events for menu
             event = self._menu.create_click_event()
             LOGGER.debug("BUTTONDOWN: generate MENU-APPLY event")
         else:
             event = pygame.event.Event(BUTTONDOWN, capture=0, printer=1,
                                        button=self.buttons.printer)
             LOGGER.debug("BUTTONDOWN: generate PRINTER event")
         pygame.event.post(event)
Beispiel #5
0
def pibooth_startup(app, cfg):
    """Create the GooglePhotosUpload instance."""
    app.previous_picture_url = None
    client_id_file = cfg.getpath('GOOGLE', 'client_id_file')

    if not client_id_file:
        LOGGER.debug(
            "No credentials file defined in [GOOGLE][client_id_file], upload deactivated"
        )
    elif not os.path.exists(client_id_file):
        LOGGER.error(
            "No such file [GOOGLE][client_id_file]='%s', please check config",
            client_id_file)
    elif client_id_file and os.path.getsize(client_id_file) == 0:
        LOGGER.error(
            "Empty file [GOOGLE][client_id_file]='%s', please check config",
            client_id_file)
    else:
        app.google_photos = GooglePhotosApi(client_id_file)
Beispiel #6
0
    def _update_foreground(self, pil_image, pos=CENTER, resize=True):
        """Show a PIL image on the foreground.
        Only one is bufferized to avoid memory leak.
        """
        image_name = id(pil_image)

        if pos == self.FULLSCREEN:
            image_size_max = (self.surface.get_size()[0] * 0.9,
                              self.surface.get_size()[1] * 0.9)
        else:
            image_size_max = (self.surface.get_size()[0] * 0.48,
                              self.surface.get_size()[1])

        buff_size, buff_image = self._buffered_images.get(
            image_name, (None, None))
        if buff_image and image_size_max == buff_size:
            image = buff_image
        else:
            if resize:
                image = pil_image.resize(
                    sizing.new_size_keep_aspect_ratio(pil_image.size,
                                                      image_size_max),
                    Image.ANTIALIAS)
            else:
                image = pil_image
            image = pygame.image.frombuffer(image.tobytes(), image.size,
                                            image.mode)
            if self._current_foreground:
                self._buffered_images.pop(id(self._current_foreground[0]),
                                          None)
            LOGGER.debug("Add to buffer the image '%s'", image_name)
            self._buffered_images[image_name] = (image_size_max, image)

        self._current_foreground = (pil_image, pos, resize)

        if self.debug and resize:
            # Build rectangle around picture area for debuging purpose
            outlines = pygame.Surface(image_size_max, pygame.SRCALPHA, 32)
            pygame.draw.rect(outlines, pygame.Color(255, 0, 0),
                             outlines.get_rect(), 2)
            self.surface.blit(outlines, self._pos_map[pos](outlines))

        return self.surface.blit(image, self._pos_map[pos](image))
Beispiel #7
0
def init(filename, clear=False):
    """Initialize the translation system.

    :param filename: path to the translations file
    :type filename: str
    :param clear: restore default translations
    :type clear: bool
    """
    PARSER.filename = osp.abspath(osp.expanduser(filename))

    if not osp.isfile(PARSER.filename) or clear:
        LOGGER.info("Generate the translation file in '%s'", PARSER.filename)
        dirname = osp.dirname(PARSER.filename)
        if not osp.isdir(dirname):
            os.makedirs(dirname)

        with io.open(PARSER.filename, 'w', encoding="utf-8") as fp:
            for section, options in DEFAULT.items():
                fp.write("[{}]\n".format(section))
                for name, value in options.items():
                    value = value.splitlines()
                    fp.write("{} = {}\n".format(name, value[0]))
                    if len(value) > 1:
                        for part in value[1:]:
                            fp.write("    {}\n".format(part))
                fp.write("\n\n")

    PARSER.read(PARSER.filename, encoding='utf-8')

    # Complete with missing language
    changed = False
    for section, options in DEFAULT.items():
        if not PARSER.has_section(section):
            changed = True
            LOGGER.debug("Add [%s] to available language list", section)
            PARSER.add_section(section)
            for option, value in options.items():
                PARSER.set(section, option, value)

    if changed:
        with io.open(PARSER.filename, 'w', encoding="utf-8") as fp:
            PARSER.write(fp)
Beispiel #8
0
    def load_all_plugins(self, paths, disabled=None):
        """Register the core plugins, load plugins from setuptools entry points
        and the load given module/package paths.

        note:: by default hooks are called in LIFO registered order thus
               plugins register order is important.

        :param paths: list of Python module/package paths to load
        :type paths: list
        :param disabled: list of plugins name to be disabled after loaded
        :type disabled: list
        """
        # Load plugins declared by setuptools entry points
        self.load_setuptools_entrypoints(hookspecs.hookspec.project_name)

        plugins = []
        for path in paths:
            plugin = load_module(path)
            if plugin:
                LOGGER.debug("Plugin found at '%s'", path)
                plugins.append(plugin)

        plugins += [
            LightsPlugin(self),  # Last called
            ViewPlugin(self),
            PrinterPlugin(self),
            PicturePlugin(self),
            CameraPlugin(self)
        ]  # First called

        for plugin in plugins:
            self.register(plugin)

        # Check that each hookimpl is defined in the hookspec
        # except for hookimpl with kwarg 'optionalhook=True'.
        self.check_pending()

        # Disable unwanted plugins
        if disabled:
            for name in disabled:
                self.unregister(name=name)
Beispiel #9
0
 def _get_authorized_session(self):
     """Check if credentials file exists"""
     # check the default path of save credentials to allow keep None
     if self.google_credentials is None:
         self.google_credentials = os.path.join(
             os.path.dirname(self.client_id_file) +
             "/google_credentials.dat")
     # if first instance of application:
     if not os.path.exists(self.google_credentials) or \
             os.path.getsize(self.google_credentials) == 0:
         self._auth()
         self.session = AuthorizedSession(self.credentials)
         LOGGER.debug("First run of application create credentials file %s",
                      self.google_credentials)
         try:
             self._save_cred()
         except OSError as err:
             LOGGER.debug("Could not save auth tokens - %s", err)
     else:
         try:
             self.credentials = Credentials.from_authorized_user_file(
                 self.google_credentials, self.scopes)
             self.session = AuthorizedSession(self.credentials)
         except ValueError:
             LOGGER.debug("Error loading auth tokens - Incorrect format")
Beispiel #10
0
def get_gp_camera_proxy(port=None):
    """Return camera proxy if a gPhoto2 compatible camera is found
    else return None.

    .. note:: try to kill any process using gPhoto2 as it may block camera access.

    :param port: look on given port number
    :type port: str
    """
    if not gp:
        return None  # gPhoto2 is not installed

    pkill('*gphoto2*')
    if hasattr(gp, 'gp_camera_autodetect'):
        # gPhoto2 version 2.5+
        cameras = gp.check_result(gp.gp_camera_autodetect())
    else:
        port_info_list = gp.PortInfoList()
        port_info_list.load()
        abilities_list = gp.CameraAbilitiesList()
        abilities_list.load()
        cameras = abilities_list.detect(port_info_list)
    if cameras:
        LOGGER.debug("Found gPhoto2 cameras on ports: '%s'",
                     "' / '".join([p for _, p in cameras]))
        # Initialize first camera proxy and return it
        camera = gp.Camera()
        if port is not None:
            port_info_list = gp.PortInfoList()
            port_info_list.load()
            idx = port_info_list.lookup_path(port)
            camera.set_port_info(port_info_list[idx])

        try:
            camera.init()
            return camera
        except gp.GPhoto2Error as ex:
            LOGGER.warning("Could not connect gPhoto2 camera: %s", ex)

    return None
Beispiel #11
0
 def set_config_value(self, section, option, value):
     """Set camera configuration. This method don't send the updated
     configuration to the camera (avoid connection flooding if several
     values have to be changed)
     """
     try:
         LOGGER.debug('Setting option %s/%s=%s', section, option, value)
         config = self._cam.get_config()
         child = config.get_child_by_name(section).get_child_by_name(option)
         if child.get_type() == gp.GP_WIDGET_RADIO:
             choices = [c for c in child.get_choices()]
         else:
             choices = None
         data_type = type(child.get_value())
         value = data_type(value)  # Cast value
         if choices and value not in choices:
             LOGGER.warning(
                 "Invalid value '%s' for option %s (possible choices: %s), trying to set it anyway", value, option, choices)
         child.set_value(value)
         self._cam.set_config(config)
     except gp.GPhoto2Error as ex:
         LOGGER.error('Unsupported option %s/%s=%s (%s), configure your DSLR manually', section, option, value, ex)
Beispiel #12
0
    def _post_process_capture(self, capture_path):
        """Rework and return a Image object from file.
        """
        gp_path, effect = self._captures[capture_path]
        camera_file = self._cam.file_get(gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL)
        if self.delete_internal_memory:
            LOGGER.debug("Delete capture '%s' from internal memory", gp_path.name)
            self._cam.file_delete(gp_path.folder, gp_path.name)
        image = Image.open(io.BytesIO(camera_file.get_data_and_size()))

        # Crop to keep aspect ratio of the resolution
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution))
        # Resize to fit the resolution
        image = image.resize(sizing.new_size_keep_aspect_ratio(image.size, self.resolution, 'outer'))

        if self._capture_hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image
Beispiel #13
0
 def _on_button_capture_held(self):
     """Called when the capture button is pressed.
     """
     if all(self.buttons.value):
         self.buttons.capture.hold_repeat = True
         if self._multipress_timer.elapsed() == 0:
             self._multipress_timer.start()
         if self._multipress_timer.is_timeout():
             # Capture was held while printer was pressed
             if self._menu and self._menu.is_shown():
                 # Convert HW button events to keyboard events for menu
                 event = self._menu.create_back_event()
                 LOGGER.debug("BUTTONDOWN: generate MENU-ESC event")
             else:
                 event = pygame.event.Event(BUTTONDOWN,
                                            capture=1,
                                            printer=1,
                                            button=self.buttons)
                 LOGGER.debug("BUTTONDOWN: generate DOUBLE buttons event")
             self.buttons.capture.hold_repeat = False
             self._multipress_timer.reset()
             pygame.event.post(event)
     else:
         # Capture was held but printer not pressed
         if self._menu and self._menu.is_shown():
             # Convert HW button events to keyboard events for menu
             event = self._menu.create_next_event()
             LOGGER.debug("BUTTONDOWN: generate MENU-NEXT event")
         else:
             event = pygame.event.Event(BUTTONDOWN,
                                        capture=1,
                                        printer=0,
                                        button=self.buttons.capture)
             LOGGER.debug("BUTTONDOWN: generate CAPTURE button event")
         self.buttons.capture.hold_repeat = False
         self._multipress_timer.reset()
         pygame.event.post(event)
Beispiel #14
0
    def set_state(self, state_name):
        """Change state machine's active state
        """
        try:
            # Perform any exit actions of the current state
            if self.active_state is not None:
                hook = getattr(self.pm.hook,
                               'state_{}_exit'.format(self.active_state))
                hook(cfg=self.cfg, app=self.app, win=self.win)
        except Exception as ex:
            if self.failsafe_state and self.active_state != self.failsafe_state:
                LOGGER.error(str(ex))
                LOGGER.debug('Back to failsafe state due to error:',
                             exc_info=True)
                state_name = self.failsafe_state
            else:
                raise

        if state_name not in self.states:
            raise ValueError(
                '"{}" not in registered states...'.format(state_name))

        # Switch to the new state and perform its entry actions
        LOGGER.debug("Activate state '%s'", state_name)
        self.active_state = state_name

        try:
            hook = getattr(self.pm.hook,
                           'state_{}_enter'.format(self.active_state))
            hook(cfg=self.cfg, app=self.app, win=self.win)
        except Exception as ex:
            if self.failsafe_state and self.active_state != self.failsafe_state:
                LOGGER.error(str(ex))
                LOGGER.debug('Back to failsafe state due to error:',
                             exc_info=True)
                self.set_state(self.failsafe_state)
            else:
                raise
Beispiel #15
0
 def _on_receive_signal(self, pin, frame, callback, bouncetime):
     last = self._last_signal_time.setdefault(pin, 0)
     if abs(time.time() - last) * 1000 >= bouncetime:
         LOGGER.debug('GPIO Mock: pin %s triggered', pin)
         self._last_signal_time[pin] = time.time()
         callback(pin)
Beispiel #16
0
 def on_button_down(self, pin):
     """Post a pygame event when the button is pressed.
     """
     LOGGER.debug('Hardware button (pin %s) triggered', pin)
     event = pygame.event.Event(BUTTON_DOWN, pin=pin)
     pygame.event.post(event)
Beispiel #17
0
 def setmode(self, mode):
     LOGGER.debug("GPIO Mock: set mode %s", mode)
Beispiel #18
0
 def setup(self, pin, direction, **kwargs):
     LOGGER.debug("GPIO Mock: setup pin %s to %s", pin, direction)
Beispiel #19
0
    def upload(self, filename, album_name):
        """Upload a photo file to the given Google Photos album.

        :param filename: photo file full path
        :type filename: str
        :param album_name: name of albums to upload
        :type album_name: str

        :returns: URL of the uploaded photo
        :rtype: str
        """
        photo_url = None

        if not self.is_reachable():
            LOGGER.error(
                "Google Photos upload failure: no internet connexion!")
            return photo_url

        if not self._credentials:
            # Plugin was disabled at startup but activated after
            self._session = self._get_authorized_session()

        album_id = self.get_album_id(album_name)
        if not album_id:
            album_id = self.create_album(album_name)
        if not album_id:
            LOGGER.error("Google Photos upload failure: album '%s' not found!",
                         album_name)
            return photo_url

        self._session.headers["Content-type"] = "application/octet-stream"
        self._session.headers["X-Goog-Upload-Protocol"] = "raw"

        with open(filename, mode='rb') as fp:
            data = fp.read()

        self._session.headers["X-Goog-Upload-File-Name"] = os.path.basename(
            filename)

        LOGGER.info("Uploading picture '%s' to Google Photos", filename)
        upload_token = self._session.post(self.URL + '/uploads', data)

        if upload_token.status_code == 200 and upload_token.content:
            create_body = json.dumps({
                "albumId":
                album_id,
                "newMediaItems": [{
                    "description": "",
                    "simpleMediaItem": {
                        "uploadToken": upload_token.content.decode()
                    }
                }]
            })

            resp = self._session.post(self.URL + '/mediaItems:batchCreate',
                                      create_body).json()
            LOGGER.debug("Google Photos server response: %s", resp)

            if "newMediaItemResults" in resp:
                status = resp["newMediaItemResults"][0]["status"]
                if status.get("code") and (status.get("code") > 0):
                    LOGGER.error(
                        "Google Photos upload failure: can not add '%s' to library: %s",
                        os.path.basename(filename), status["message"])
                else:
                    photo_url = resp["newMediaItemResults"][0][
                        'mediaItem'].get('productUrl')
                    LOGGER.info(
                        "Google Photos upload successful: '%s' added to album '%s'",
                        os.path.basename(filename), album_name)
            else:
                LOGGER.error(
                    "Google Photos upload failure: can not add '%s' to library",
                    os.path.basename(filename))

        elif upload_token.status_code != 200:
            LOGGER.error(
                "Google Photos upload failure: can not connect to '%s' (HTTP error %s)",
                self.URL, upload_token.status_code)
        else:
            LOGGER.error(
                "Google Photos upload failure: no response content from server '%s'",
                self.URL)

        try:
            del self._session.headers["Content-type"]
            del self._session.headers["X-Goog-Upload-Protocol"]
            del self._session.headers["X-Goog-Upload-File-Name"]
        except KeyError:
            pass

        return photo_url
Beispiel #20
0
    def upload_photos(self, photo_file_list, album_name, activate):
        """Funtion use to upload list of photos to google album

        :param photo_file_list: list of photos name with full path
        :type photo_file_list: file
        :param album_name: name of albums to upload
        :type album_name: str
        :param activate: use to disable the upload
        :type activate: bool
        """
        self.activate = activate
        # interrupt upload no internet
        if not self._is_internet():
            LOGGER.error("Interrupt upload no internet connexion!!!!")
            return
        # if plugin is disable
        if not self.activate:
            return
        # plugin is disable at startup but activate after so check credential file
        elif not self.credentials:
            self._get_authorized_session()

        self.album_name = album_name
        self._create_or_retrieve_album()

        # interrupt upload if no album id can't read or create
        if self.album_name and not self.album_id:
            LOGGER.error("Interrupt upload album not found!!!!")
            return

        self.session.headers["Content-type"] = "application/octet-stream"
        self.session.headers["X-Goog-Upload-Protocol"] = "raw"

        for photo_file_name in photo_file_list:

            try:
                photo_file = open(photo_file_name, mode='rb')
                photo_bytes = photo_file.read()
            except OSError as err:
                LOGGER.error("Could not read file '%s' -- %s", photo_file_name,
                             err)
                continue

            self.session.headers["X-Goog-Upload-File-Name"] = os.path.basename(
                photo_file_name)

            LOGGER.info("Uploading photo -- '%s'", photo_file_name)

            upload_token = self.session.post(
                'https://photoslibrary.googleapis.com/v1/uploads', photo_bytes)

            if (upload_token.status_code == 200) and upload_token.content:

                create_body = json.dumps(
                    {
                        "albumId":
                        self.album_id,
                        "newMediaItems": [{
                            "description": "",
                            "simpleMediaItem": {
                                "uploadToken": upload_token.content.decode()
                            }
                        }]
                    },
                    indent=4)

                resp = self.session.post(
                    'https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate',
                    create_body).json()

                LOGGER.debug("Server response: %s", resp)

                if "newMediaItemResults" in resp:
                    status = resp["newMediaItemResults"][0]["status"]
                    if status.get("code") and (status.get("code") > 0):
                        LOGGER.error("Could not add '%s' to library -- %s",
                                     os.path.basename(photo_file_name),
                                     status["message"])
                    else:
                        LOGGER.info("Added '%s' to library and album '%s' ",
                                    os.path.basename(photo_file_name),
                                    album_name)
                else:
                    LOGGER.error(
                        "Could not add '%s' to library. Server Response -- %s",
                        os.path.basename(photo_file_name), resp)

            else:
                LOGGER.error("Could not upload '%s'. Server Response -- %s",
                             os.path.basename(photo_file_name), upload_token)

        try:
            del self.session.headers["Content-type"]
            del self.session.headers["X-Goog-Upload-Protocol"]
            del self.session.headers["X-Goog-Upload-File-Name"]
        except KeyError:
            pass
Beispiel #21
0
 def cleanup(self):
     LOGGER.debug("GPIO Mock: quit")