# Module `lib.video.video`

Class to manage the camera of the ESP32CAM. This requires the modified firmware. I added in the firmware the possibility of detecting movements, as well as a lot of adjustment on the camera, not available on the other firmware that manages the esp32cam.

Expand source code

    # Distributed under Pycameresp License
    # Copyright (c) 2023 Remi BERTHOLET
    # pylint:disable=consider-using-f-string
    """ Class to manage the camera of the ESP32CAM.
    This requires the modified firmware.
    I added in the firmware the possibility of detecting movements,
    as well as a lot of adjustment on the camera, not available on the other firmware that manages the esp32cam.
    """
    # pylint: disable=multiple-statements
    import time
    import uasyncio
    import os
    import tools.info
    import tools.system
    import tools.jsonconfig
    import tools.logger
    if tools.info.iscamera():
            import camera

    class CameraConfig(tools.jsonconfig.JsonConfig):
            """ Class that collects the camera rendering configuration """
            def __init__(self):
                    """ Constructor """
                    tools.jsonconfig.JsonConfig.__init__(self)
                    self.activated  = True
                    self.framesize  = b"640x480"
                    self.pixformat  = b"JPEG"
                    self.quality    = 25
                    self.brightness = 0
                    self.contrast   = 0
                    self.saturation = 0
                    self.hmirror    = False
                    self.vflip      = False
                    self.flash_level = 0

    class Reservation:
            """ Manage the camera reservation """
            def __init__(self):
                    """ Constructor """
                    self.identifier = None
                    self.count = 0
                    self.lock = uasyncio.Lock()
                    self.suspended = 0

            async def reserve(self, object_, timeout=0, suspension=None):
                    """ Wait the ressource and reserve """
                    result = False
                    # Wait
                    while True:
                            result = await self.acquire(object_, suspension)
                            if result:
                                    break
                            timeout -= 1
                            if timeout < 0:
                                    break
                            await uasyncio.sleep_ms(1000)
                    return result

            async def acquire(self, object_, suspension=None):
                    """ Reserve the camera, is used to stream the output of the camera
                    to the web page. It stop the motion detection during this phase """
                    result = False
                    await self.lock.acquire()
                    identifier = id(object_)
                    try:
                            # If not reserved
                            if self.identifier is None:
                                    # If suspension not required
                                    if suspension is None:
                                            # If previous suspension ended
                                            if self.suspended <= 0:
                                                    # Reserve
                                                    self.identifier = identifier
                                                    self.count = 1
                                                    self.suspended = 0
                                                    result = True
                                            else:
                                                    # Decrease suspension counter
                                                    self.suspended -= 1
                                    else:
                                            # Reserve
                                            self.identifier = identifier
                                            self.count = 1
                                            self.suspended = suspension
                                            result = True
                            # If already reserved by the current object_
                            elif self.identifier == identifier:
                                    # Increase reservation counter
                                    self.count += 1
                                    self.suspended = suspension
                                    result = True
                    finally:
                            self.lock.release()
                    return result

            async def unreserve(self, object_):
                    """ Unreserve the camera """
                    result = False
                    await self.lock.acquire()
                    identifier = id(object_)
                    try:
                            if self.identifier == identifier:
                                    if self.count <= 1:
                                            self.count = 0
                                            self.identifier = None
                                    else:
                                            self.count -= 1
                                    result = True
                    finally:
                            self.lock.release()
                    return result

    class Camera:
            """ Singleton class to manage the camera """
            reservation = Reservation()
            opened = False
            lock = uasyncio.Lock()
            modified = [False]
            success = [0]
            failed  = [0]
            new_failed = [0]
            config = None
            flash_enabled = [True]
            aquisition = [False]

            @staticmethod
            def gpio_config(**kwargs):
                    """ Configure the structure for camera initialization. It must be done before the first call of Camera.open.
                            The defaults values are for ESP32CAM.
                            - pin_pwdn           : GPIO pin for camera power down line
                            - pin_reset          : GPIO pin for camera pin_reset line
                            - pin_xclk           : GPIO pin for camera XCLK line
                            - pin_sscb_sda       : GPIO pin for camera SDA line (SIOD)
                            - pin_sscb_scl       : GPIO pin for camera SCL line (SIOC)
                            - pin_d7             : GPIO pin for camera D7 or Y9 line
                            - pin_d6             : GPIO pin for camera D6 or Y8 line
                            - pin_d5             : GPIO pin for camera D5 or Y7 line
                            - pin_d4             : GPIO pin for camera D4 or Y6 line
                            - pin_d3             : GPIO pin for camera D3 or Y5 line
                            - pin_d2             : GPIO pin for camera D2 or Y4 line
                            - pin_d1             : GPIO pin for camera D1 or Y3 line
                            - pin_d0             : GPIO pin for camera D0 or Y2 line
                            - pin_vsync          : GPIO pin for camera VSYNC line
                            - pin_href           : GPIO pin for camera HREF line
                            - pin_pclk           : GPIO pin for camera PCLK line
                            - xclk_freq_hz       : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental)
                            - ledc_timer         : LEDC timer to be used for generating XCLK
                            - ledc_channel       : LEDC channel to be used for generating XCLK
                            - pixel_format       : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
                            - frame_size         : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
                            - jpeg_quality       : Quality of JPEG output. 0-63 lower means higher quality
                            - fb_count           : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
                            - flash_led          : GPIO pin for flash led or 0 to disable
                            - grab_mode          : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST
                            - fb_location        : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM
    """
                    Camera.get_config()
                    if Camera.is_activated():
                            camera.configure(**kwargs)

            @staticmethod
            def open():
                    """ Open the camera """
                    Camera.get_config()
                    if Camera.is_activated():
                            result = True
                            if Camera.opened is False:
                                    for i in range(10):
                                            res = camera.init()
                                            if res is False:
                                                    # print("Camera not initialized")
                                                    camera.deinit()
                                                    time.sleep(0.5)
                                            else:
                                                    break
                                    else:
                                            result = False

                                    if result:
                                            # Photo on 800x600, motion detection / 8 (100x75), each square detection 8x8 (12.5 x 9.375)
                                            Camera.opened = True
                                            Camera.aquisition[0] = False
                    else:
                            result = False
                    return result

            @staticmethod
            def get_stat():
                    """ Statistic """
                    return Camera.success[0], Camera.failed[0]

            @staticmethod
            def reset_stat():
                    """ Reset statistic """
                    Camera.success[0] = 0
                    Camera.failed [0] = 0
                    Camera.new_failed[0] = 0

            @staticmethod
            def close():
                    """ Close the camera """
                    if Camera.opened is True:
                            camera.deinit()
                            Camera.opened = False

            @staticmethod
            def reset():
                    """ Reset the camera """
                    if Camera.opened is True:
                            camera.reset()

            @staticmethod
            def is_opened():
                    """ Indicates if the camera opened """
                    return Camera.opened

            @staticmethod
            def capture():
                    """ Capture an image on the camera """
                    Camera.aquisition[0] = True
                    return Camera.retry(camera.capture)

            @staticmethod
            def motion():
                    """ Get the motion informations.
                    This contains a jpeg image, with matrices of the different color RGB """
                    Camera.aquisition[0] = True
                    return Camera.retry(camera.motion)

            @staticmethod
            def flash(level=0):
                    """ Start or stop the flash """
                    if Camera.flash_enabled[0]:
                            camera.flash(level)

            @staticmethod
            def set_flash(state):
                    """ On the esp32cam, we use the output of the led flash to communicate with the dfplayer,
                    and this disrupts the operation, then we can turn it off """
                    Camera.flash_enabled[0] = state

            @staticmethod
            def retry(callback):
                    """ Retry camera action and manage error """
                    result = None
                    if Camera.opened:
                            retry = 10
                            while 1:
                                    if retry <= 0:
                                            tools.system.reboot("Reboot forced after camera problem")
                                    try:
                                            result = callback()
                                            Camera.success[0] += 1
                                            break
                                    except ValueError:
                                            Camera.failed[0] += 1
                                            Camera.new_failed[0] += 1
                                            if retry <= 3:
                                                    tools.logger.syslog("Failed to get image %d retry before reset"%retry)
                                            retry -= 1
                                            time.sleep(0.5)
                            total = Camera.success[0] + Camera.failed[0]
                            STAT_CAMERA=20000
                            if (total % STAT_CAMERA) == 0:
                                    if Camera.success[0] != 0:
                                            new_failed = 100.-((Camera.new_failed[0]*100)/STAT_CAMERA)
                                            failed    = 100.-((Camera.failed[0]*100)/total)
                                    else:
                                            new_failed = 0.
                                            failed    = 0.
                                    tools.logger.syslog("Camera stat : last %-3.1f%%, total %-3.1f%% success on %d"%(new_failed, failed, total))
                                    Camera.new_failed[0] = 0
                    return result

            @staticmethod
            async def reserve(object_, timeout=0, suspension=None):
                    """ Reserve the camera, is used to stream the output of the camera
                    to the web page. It stop the motion detection during this phase """
                    return await Camera.reservation.reserve(object_, timeout, suspension)

            @staticmethod
            async def unreserve(object_):
                    """ Unreserve the camera """
                    return await Camera.reservation.unreserve(object_)

            @staticmethod
            def is_modified():
                    """ Indicates that the camera configuration has been changed """
                    return Camera.modified[0]

            @staticmethod
            def clear_modified():
                    """ Reset the indicator of configuration modification """
                    Camera.modified[0] = False

            @staticmethod
            def reinit():
                    """ Close and open camera if get image previously asked, it avoid a DMA problem """
                    if Camera.opened:
                            if Camera.aquisition[0] is True:
                                    if "FREENOVE CAM" in os.uname().machine:
                                            Camera.close()
                                            Camera.open()

            @staticmethod
            def framesize(resolution=None):
                    """ Configure the frame size """
                    result = None
                    val = None
                    Camera.modified[0] = True
                    if   resolution == b"UXGA"  or resolution == b"1600x1200" :val = camera.FRAMESIZE_UXGA
                    elif resolution == b"SXGA"  or resolution == b"1280x1024" :val = camera.FRAMESIZE_SXGA
                    elif resolution == b"XGA"   or resolution == b"1024x768"  :val = camera.FRAMESIZE_XGA
                    elif resolution == b"SVGA"  or resolution == b"800x600"   :val = camera.FRAMESIZE_SVGA
                    elif resolution == b"VGA"   or resolution == b"640x480"   :val = camera.FRAMESIZE_VGA
                    elif resolution == b"CIF"   or resolution == b"400x296"   :val = camera.FRAMESIZE_CIF
                    elif resolution == b"QVGA"  or resolution == b"320x240"   :val = camera.FRAMESIZE_QVGA
                    elif resolution == b"HQVGA" or resolution == b"240x176"   :val = camera.FRAMESIZE_HQVGA
                    elif resolution == b"QQVGA" or resolution == b"160x120"   :val = camera.FRAMESIZE_QQVGA
                    if Camera.opened:
                            # print("Framesize %s"%strings.tostrings(resolution))
                            result = camera.framesize(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.framesize(val)
                    return result

            @staticmethod
            def pixformat(format_=None):
                    """ Change the format of image """
                    result = None
                    Camera.modified[0] = True
                    val = None
                    if   format_ == b"RGB565"    : val=camera.PIXFORMAT_RGB565
                    elif format_ == b"YUV422"    : val=camera.PIXFORMAT_YUV422
                    elif format_ == b"GRAYSCALE" : val=camera.PIXFORMAT_GRAYSCALE
                    elif format_ == b"JPEG"      : val=camera.PIXFORMAT_JPEG
                    elif format_ == b"RGB888"    : val=camera.PIXFORMAT_RGB888
                    elif format_ == b"RAW"       : val=camera.PIXFORMAT_RAW
                    elif format_ == b"RGB444"    : val=camera.PIXFORMAT_RGB444
                    elif format_ == b"RGB555"    : val=camera.PIXFORMAT_RGB555
                    if Camera.opened:
                            # print("Pixformat %s"%strings.tostrings(format_))
                            result = camera.pixformat(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.pixformat(val)
                    return result

            @staticmethod
            def quality(val=None, modified=True):
                    """ Configure the compression """
                    result = None
                    Camera.modified[0] = modified
                    if Camera.opened:
                            # print("Quality %d"%val)
                            result = camera.quality(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.quality(val)
                    return result

            @staticmethod
            def brightness(val=None):
                    """ Change the brightness """
                    result = None
                    Camera.modified[0] = True
                    if Camera.opened:
                            # print("Brightness %d"%val)
                            result = camera.brightness(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.brightness(val)
                    return result

            @staticmethod
            def contrast(val=None):
                    """ Change the contrast """
                    result = None
                    Camera.modified[0] = True
                    if Camera.opened:
                            # print("Contrast %d"%val)
                            result = camera.contrast(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.contrast(val)
                    return result

            @staticmethod
            def saturation(val=None):
                    """ Change the saturation """
                    result = None
                    Camera.modified[0] = True
                    if Camera.opened:
                            # print("Saturation %d"%val)
                            result = camera.saturation(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.saturation(val)
                    return result

            @staticmethod
            def sharpness(val=None):
                    """ Change the sharpness """
                    result = None
                    Camera.modified[0] = True
                    if Camera.opened:
                            # print("Sharpness %d"%val)
                            result = camera.sharpness(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.sharpness(val)
                    return result

            @staticmethod
            def hmirror(val=None):
                    """ Set horizontal mirroring """
                    result = None
                    Camera.modified[0] = True
                    if Camera.opened:
                            # print("Hmirror %d"%val)
                            result = camera.hmirror(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.hmirror(val)
                    return result

            @staticmethod
            def vflip(val=None):
                    """ Set the vertical flip """
                    result = None
                    Camera.modified[0] = True
                    if Camera.opened:
                            # print("Vflip %d"%val)
                            result = camera.vflip(val)
                            if result is False and val is not None:
                                    Camera.reinit()
                                    result = camera.vflip(val)
                    return result

            @staticmethod
            def configure(config):
                    """ Configure the camera """
                    if Camera.opened:
                            Camera.pixformat (config.pixformat)
                            Camera.framesize (config.framesize)
                            Camera.quality   (config.quality)
                            Camera.brightness(config.brightness)
                            Camera.contrast  (config.contrast)
                            Camera.saturation(config.saturation)
                            Camera.hmirror   (config.hmirror)
                            Camera.vflip     (config.vflip)
                            Camera.flash     (config.flash_level)

            @staticmethod
            def get_config():
                    """ Reload configuration if it changed """
                    if Camera.config is None:
                            Camera.config = CameraConfig()
                            if Camera.config.load() is False:
                                    Camera.config.save()
                    else:
                            Camera.config.refresh()
                    return Camera.config

            @staticmethod
            def is_activated():
                    """ Indicates if the camera is configured to be activated """
                    if Camera.config is None:
                            Camera.get_config()
                    if Camera.config is not None:
                            return Camera.config.activated
                    else:
                            return False

## Classes

`class Camera`

  Singleton class to manage the camera

  Expand source code

      class Camera:
              """ Singleton class to manage the camera """
              reservation = Reservation()
              opened = False
              lock = uasyncio.Lock()
              modified = [False]
              success = [0]
              failed  = [0]
              new_failed = [0]
              config = None
              flash_enabled = [True]
              aquisition = [False]

              @staticmethod
              def gpio_config(**kwargs):
                      """ Configure the structure for camera initialization. It must be done before the first call of Camera.open.
                              The defaults values are for ESP32CAM.
                              - pin_pwdn           : GPIO pin for camera power down line
                              - pin_reset          : GPIO pin for camera pin_reset line
                              - pin_xclk           : GPIO pin for camera XCLK line
                              - pin_sscb_sda       : GPIO pin for camera SDA line (SIOD)
                              - pin_sscb_scl       : GPIO pin for camera SCL line (SIOC)
                              - pin_d7             : GPIO pin for camera D7 or Y9 line
                              - pin_d6             : GPIO pin for camera D6 or Y8 line
                              - pin_d5             : GPIO pin for camera D5 or Y7 line
                              - pin_d4             : GPIO pin for camera D4 or Y6 line
                              - pin_d3             : GPIO pin for camera D3 or Y5 line
                              - pin_d2             : GPIO pin for camera D2 or Y4 line
                              - pin_d1             : GPIO pin for camera D1 or Y3 line
                              - pin_d0             : GPIO pin for camera D0 or Y2 line
                              - pin_vsync          : GPIO pin for camera VSYNC line
                              - pin_href           : GPIO pin for camera HREF line
                              - pin_pclk           : GPIO pin for camera PCLK line
                              - xclk_freq_hz       : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental)
                              - ledc_timer         : LEDC timer to be used for generating XCLK
                              - ledc_channel       : LEDC channel to be used for generating XCLK
                              - pixel_format       : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
                              - frame_size         : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
                              - jpeg_quality       : Quality of JPEG output. 0-63 lower means higher quality
                              - fb_count           : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
                              - flash_led          : GPIO pin for flash led or 0 to disable
                              - grab_mode          : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST
                              - fb_location        : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM
      """
                      Camera.get_config()
                      if Camera.is_activated():
                              camera.configure(**kwargs)

              @staticmethod
              def open():
                      """ Open the camera """
                      Camera.get_config()
                      if Camera.is_activated():
                              result = True
                              if Camera.opened is False:
                                      for i in range(10):
                                              res = camera.init()
                                              if res is False:
                                                      # print("Camera not initialized")
                                                      camera.deinit()
                                                      time.sleep(0.5)
                                              else:
                                                      break
                                      else:
                                              result = False

                                      if result:
                                              # Photo on 800x600, motion detection / 8 (100x75), each square detection 8x8 (12.5 x 9.375)
                                              Camera.opened = True
                                              Camera.aquisition[0] = False
                      else:
                              result = False
                      return result

              @staticmethod
              def get_stat():
                      """ Statistic """
                      return Camera.success[0], Camera.failed[0]

              @staticmethod
              def reset_stat():
                      """ Reset statistic """
                      Camera.success[0] = 0
                      Camera.failed [0] = 0
                      Camera.new_failed[0] = 0

              @staticmethod
              def close():
                      """ Close the camera """
                      if Camera.opened is True:
                              camera.deinit()
                              Camera.opened = False

              @staticmethod
              def reset():
                      """ Reset the camera """
                      if Camera.opened is True:
                              camera.reset()

              @staticmethod
              def is_opened():
                      """ Indicates if the camera opened """
                      return Camera.opened

              @staticmethod
              def capture():
                      """ Capture an image on the camera """
                      Camera.aquisition[0] = True
                      return Camera.retry(camera.capture)

              @staticmethod
              def motion():
                      """ Get the motion informations.
                      This contains a jpeg image, with matrices of the different color RGB """
                      Camera.aquisition[0] = True
                      return Camera.retry(camera.motion)

              @staticmethod
              def flash(level=0):
                      """ Start or stop the flash """
                      if Camera.flash_enabled[0]:
                              camera.flash(level)

              @staticmethod
              def set_flash(state):
                      """ On the esp32cam, we use the output of the led flash to communicate with the dfplayer,
                      and this disrupts the operation, then we can turn it off """
                      Camera.flash_enabled[0] = state

              @staticmethod
              def retry(callback):
                      """ Retry camera action and manage error """
                      result = None
                      if Camera.opened:
                              retry = 10
                              while 1:
                                      if retry <= 0:
                                              tools.system.reboot("Reboot forced after camera problem")
                                      try:
                                              result = callback()
                                              Camera.success[0] += 1
                                              break
                                      except ValueError:
                                              Camera.failed[0] += 1
                                              Camera.new_failed[0] += 1
                                              if retry <= 3:
                                                      tools.logger.syslog("Failed to get image %d retry before reset"%retry)
                                              retry -= 1
                                              time.sleep(0.5)
                              total = Camera.success[0] + Camera.failed[0]
                              STAT_CAMERA=20000
                              if (total % STAT_CAMERA) == 0:
                                      if Camera.success[0] != 0:
                                              new_failed = 100.-((Camera.new_failed[0]*100)/STAT_CAMERA)
                                              failed    = 100.-((Camera.failed[0]*100)/total)
                                      else:
                                              new_failed = 0.
                                              failed    = 0.
                                      tools.logger.syslog("Camera stat : last %-3.1f%%, total %-3.1f%% success on %d"%(new_failed, failed, total))
                                      Camera.new_failed[0] = 0
                      return result

              @staticmethod
              async def reserve(object_, timeout=0, suspension=None):
                      """ Reserve the camera, is used to stream the output of the camera
                      to the web page. It stop the motion detection during this phase """
                      return await Camera.reservation.reserve(object_, timeout, suspension)

              @staticmethod
              async def unreserve(object_):
                      """ Unreserve the camera """
                      return await Camera.reservation.unreserve(object_)

              @staticmethod
              def is_modified():
                      """ Indicates that the camera configuration has been changed """
                      return Camera.modified[0]

              @staticmethod
              def clear_modified():
                      """ Reset the indicator of configuration modification """
                      Camera.modified[0] = False

              @staticmethod
              def reinit():
                      """ Close and open camera if get image previously asked, it avoid a DMA problem """
                      if Camera.opened:
                              if Camera.aquisition[0] is True:
                                      if "FREENOVE CAM" in os.uname().machine:
                                              Camera.close()
                                              Camera.open()

              @staticmethod
              def framesize(resolution=None):
                      """ Configure the frame size """
                      result = None
                      val = None
                      Camera.modified[0] = True
                      if   resolution == b"UXGA"  or resolution == b"1600x1200" :val = camera.FRAMESIZE_UXGA
                      elif resolution == b"SXGA"  or resolution == b"1280x1024" :val = camera.FRAMESIZE_SXGA
                      elif resolution == b"XGA"   or resolution == b"1024x768"  :val = camera.FRAMESIZE_XGA
                      elif resolution == b"SVGA"  or resolution == b"800x600"   :val = camera.FRAMESIZE_SVGA
                      elif resolution == b"VGA"   or resolution == b"640x480"   :val = camera.FRAMESIZE_VGA
                      elif resolution == b"CIF"   or resolution == b"400x296"   :val = camera.FRAMESIZE_CIF
                      elif resolution == b"QVGA"  or resolution == b"320x240"   :val = camera.FRAMESIZE_QVGA
                      elif resolution == b"HQVGA" or resolution == b"240x176"   :val = camera.FRAMESIZE_HQVGA
                      elif resolution == b"QQVGA" or resolution == b"160x120"   :val = camera.FRAMESIZE_QQVGA
                      if Camera.opened:
                              # print("Framesize %s"%strings.tostrings(resolution))
                              result = camera.framesize(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.framesize(val)
                      return result

              @staticmethod
              def pixformat(format_=None):
                      """ Change the format of image """
                      result = None
                      Camera.modified[0] = True
                      val = None
                      if   format_ == b"RGB565"    : val=camera.PIXFORMAT_RGB565
                      elif format_ == b"YUV422"    : val=camera.PIXFORMAT_YUV422
                      elif format_ == b"GRAYSCALE" : val=camera.PIXFORMAT_GRAYSCALE
                      elif format_ == b"JPEG"      : val=camera.PIXFORMAT_JPEG
                      elif format_ == b"RGB888"    : val=camera.PIXFORMAT_RGB888
                      elif format_ == b"RAW"       : val=camera.PIXFORMAT_RAW
                      elif format_ == b"RGB444"    : val=camera.PIXFORMAT_RGB444
                      elif format_ == b"RGB555"    : val=camera.PIXFORMAT_RGB555
                      if Camera.opened:
                              # print("Pixformat %s"%strings.tostrings(format_))
                              result = camera.pixformat(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.pixformat(val)
                      return result

              @staticmethod
              def quality(val=None, modified=True):
                      """ Configure the compression """
                      result = None
                      Camera.modified[0] = modified
                      if Camera.opened:
                              # print("Quality %d"%val)
                              result = camera.quality(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.quality(val)
                      return result

              @staticmethod
              def brightness(val=None):
                      """ Change the brightness """
                      result = None
                      Camera.modified[0] = True
                      if Camera.opened:
                              # print("Brightness %d"%val)
                              result = camera.brightness(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.brightness(val)
                      return result

              @staticmethod
              def contrast(val=None):
                      """ Change the contrast """
                      result = None
                      Camera.modified[0] = True
                      if Camera.opened:
                              # print("Contrast %d"%val)
                              result = camera.contrast(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.contrast(val)
                      return result

              @staticmethod
              def saturation(val=None):
                      """ Change the saturation """
                      result = None
                      Camera.modified[0] = True
                      if Camera.opened:
                              # print("Saturation %d"%val)
                              result = camera.saturation(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.saturation(val)
                      return result

              @staticmethod
              def sharpness(val=None):
                      """ Change the sharpness """
                      result = None
                      Camera.modified[0] = True
                      if Camera.opened:
                              # print("Sharpness %d"%val)
                              result = camera.sharpness(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.sharpness(val)
                      return result

              @staticmethod
              def hmirror(val=None):
                      """ Set horizontal mirroring """
                      result = None
                      Camera.modified[0] = True
                      if Camera.opened:
                              # print("Hmirror %d"%val)
                              result = camera.hmirror(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.hmirror(val)
                      return result

              @staticmethod
              def vflip(val=None):
                      """ Set the vertical flip """
                      result = None
                      Camera.modified[0] = True
                      if Camera.opened:
                              # print("Vflip %d"%val)
                              result = camera.vflip(val)
                              if result is False and val is not None:
                                      Camera.reinit()
                                      result = camera.vflip(val)
                      return result

              @staticmethod
              def configure(config):
                      """ Configure the camera """
                      if Camera.opened:
                              Camera.pixformat (config.pixformat)
                              Camera.framesize (config.framesize)
                              Camera.quality   (config.quality)
                              Camera.brightness(config.brightness)
                              Camera.contrast  (config.contrast)
                              Camera.saturation(config.saturation)
                              Camera.hmirror   (config.hmirror)
                              Camera.vflip     (config.vflip)
                              Camera.flash     (config.flash_level)

              @staticmethod
              def get_config():
                      """ Reload configuration if it changed """
                      if Camera.config is None:
                              Camera.config = CameraConfig()
                              if Camera.config.load() is False:
                                      Camera.config.save()
                      else:
                              Camera.config.refresh()
                      return Camera.config

              @staticmethod
              def is_activated():
                      """ Indicates if the camera is configured to be activated """
                      if Camera.config is None:
                              Camera.get_config()
                      if Camera.config is not None:
                              return Camera.config.activated
                      else:
                              return False

  ### Class variables

  `var aquisition`
  `var config`
  `var failed`
  `var flash_enabled`
  `var lock`
  `var modified`
  `var new_failed`
  `var opened`
  `var reservation`
  `var success`

  ### Static methods

  `def brightness(val=None)`

    Change the brightness

    Expand source code

        @staticmethod
        def brightness(val=None):
                """ Change the brightness """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Brightness %d"%val)
                        result = camera.brightness(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.brightness(val)
                return result

  `def capture()`

    Capture an image on the camera

    Expand source code

        @staticmethod
        def capture():
                """ Capture an image on the camera """
                Camera.aquisition[0] = True
                return Camera.retry(camera.capture)

  `def clear_modified()`

    Reset the indicator of configuration modification

    Expand source code

        @staticmethod
        def clear_modified():
                """ Reset the indicator of configuration modification """
                Camera.modified[0] = False

  `def close()`

    Close the camera

    Expand source code

        @staticmethod
        def close():
                """ Close the camera """
                if Camera.opened is True:
                        camera.deinit()
                        Camera.opened = False

  `def configure(config)`

    Configure the camera

    Expand source code

        @staticmethod
        def configure(config):
                """ Configure the camera """
                if Camera.opened:
                        Camera.pixformat (config.pixformat)
                        Camera.framesize (config.framesize)
                        Camera.quality   (config.quality)
                        Camera.brightness(config.brightness)
                        Camera.contrast  (config.contrast)
                        Camera.saturation(config.saturation)
                        Camera.hmirror   (config.hmirror)
                        Camera.vflip     (config.vflip)
                        Camera.flash     (config.flash_level)

  `def contrast(val=None)`

    Change the contrast

    Expand source code

        @staticmethod
        def contrast(val=None):
                """ Change the contrast """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Contrast %d"%val)
                        result = camera.contrast(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.contrast(val)
                return result

  `def flash(level=0)`

    Start or stop the flash

    Expand source code

        @staticmethod
        def flash(level=0):
                """ Start or stop the flash """
                if Camera.flash_enabled[0]:
                        camera.flash(level)

  `def framesize(resolution=None)`

    Configure the frame size

    Expand source code

        @staticmethod
        def framesize(resolution=None):
                """ Configure the frame size """
                result = None
                val = None
                Camera.modified[0] = True
                if   resolution == b"UXGA"  or resolution == b"1600x1200" :val = camera.FRAMESIZE_UXGA
                elif resolution == b"SXGA"  or resolution == b"1280x1024" :val = camera.FRAMESIZE_SXGA
                elif resolution == b"XGA"   or resolution == b"1024x768"  :val = camera.FRAMESIZE_XGA
                elif resolution == b"SVGA"  or resolution == b"800x600"   :val = camera.FRAMESIZE_SVGA
                elif resolution == b"VGA"   or resolution == b"640x480"   :val = camera.FRAMESIZE_VGA
                elif resolution == b"CIF"   or resolution == b"400x296"   :val = camera.FRAMESIZE_CIF
                elif resolution == b"QVGA"  or resolution == b"320x240"   :val = camera.FRAMESIZE_QVGA
                elif resolution == b"HQVGA" or resolution == b"240x176"   :val = camera.FRAMESIZE_HQVGA
                elif resolution == b"QQVGA" or resolution == b"160x120"   :val = camera.FRAMESIZE_QQVGA
                if Camera.opened:
                        # print("Framesize %s"%strings.tostrings(resolution))
                        result = camera.framesize(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.framesize(val)
                return result

  `def get_config()`

    Reload configuration if it changed

    Expand source code

        @staticmethod
        def get_config():
                """ Reload configuration if it changed """
                if Camera.config is None:
                        Camera.config = CameraConfig()
                        if Camera.config.load() is False:
                                Camera.config.save()
                else:
                        Camera.config.refresh()
                return Camera.config

  `def get_stat()`

    Statistic

    Expand source code

        @staticmethod
        def get_stat():
                """ Statistic """
                return Camera.success[0], Camera.failed[0]

  `def gpio_config(**kwargs)`

    Configure the structure for camera initialization. It must be done before the first call of Camera.open. The defaults values are for ESP32CAM. - pin_pwdn : GPIO pin for camera power down line - pin_reset : GPIO pin for camera pin_reset line - pin_xclk : GPIO pin for camera XCLK line - pin_sscb_sda : GPIO pin for camera SDA line (SIOD) - pin_sscb_scl : GPIO pin for camera SCL line (SIOC) - pin_d7 : GPIO pin for camera D7 or Y9 line - pin_d6 : GPIO pin for camera D6 or Y8 line - pin_d5 : GPIO pin for camera D5 or Y7 line - pin_d4 : GPIO pin for camera D4 or Y6 line - pin_d3 : GPIO pin for camera D3 or Y5 line - pin_d2 : GPIO pin for camera D2 or Y4 line - pin_d1 : GPIO pin for camera D1 or Y3 line - pin_d0 : GPIO pin for camera D0 or Y2 line - pin_vsync : GPIO pin for camera VSYNC line - pin_href : GPIO pin for camera HREF line - pin_pclk : GPIO pin for camera PCLK line - xclk_freq_hz : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental) - ledc_timer : LEDC timer to be used for generating XCLK - ledc_channel : LEDC channel to be used for generating XCLK - pixel_format : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG - frame_size : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA - jpeg_quality : Quality of JPEG output. 0-63 lower means higher quality - fb_count : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) - flash_led : GPIO pin for flash led or 0 to disable - grab_mode : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST - fb_location : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM

    Expand source code

                @staticmethod
                def gpio_config(**kwargs):
                        """ Configure the structure for camera initialization. It must be done before the first call of Camera.open.
                                The defaults values are for ESP32CAM.
                                - pin_pwdn           : GPIO pin for camera power down line
                                - pin_reset          : GPIO pin for camera pin_reset line
                                - pin_xclk           : GPIO pin for camera XCLK line
                                - pin_sscb_sda       : GPIO pin for camera SDA line (SIOD)
                                - pin_sscb_scl       : GPIO pin for camera SCL line (SIOC)
                                - pin_d7             : GPIO pin for camera D7 or Y9 line
                                - pin_d6             : GPIO pin for camera D6 or Y8 line
                                - pin_d5             : GPIO pin for camera D5 or Y7 line
                                - pin_d4             : GPIO pin for camera D4 or Y6 line
                                - pin_d3             : GPIO pin for camera D3 or Y5 line
                                - pin_d2             : GPIO pin for camera D2 or Y4 line
                                - pin_d1             : GPIO pin for camera D1 or Y3 line
                                - pin_d0             : GPIO pin for camera D0 or Y2 line
                                - pin_vsync          : GPIO pin for camera VSYNC line
                                - pin_href           : GPIO pin for camera HREF line
                                - pin_pclk           : GPIO pin for camera PCLK line
                                - xclk_freq_hz       : Frequency of XCLK signal, in Hz. Either 20KHz or 10KHz for OV2640 double FPS (Experimental)
                                - ledc_timer         : LEDC timer to be used for generating XCLK
                                - ledc_channel       : LEDC channel to be used for generating XCLK
                                - pixel_format       : Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG
                                - frame_size         : Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
                                - jpeg_quality       : Quality of JPEG output. 0-63 lower means higher quality
                                - fb_count           : Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
                                - flash_led          : GPIO pin for flash led or 0 to disable
                                - grab_mode          : 0=CAMERA_GRAB_WHEN_EMPTY, 1=CAMERA_GRAB_LATEST
                                - fb_location        : 0=CAMERA_FB_IN_PSRAM, 1=CAMERA_FB_IN_DRAM
        """
                        Camera.get_config()
                        if Camera.is_activated():
                                camera.configure(**kwargs)

  `def hmirror(val=None)`

    Set horizontal mirroring

    Expand source code

        @staticmethod
        def hmirror(val=None):
                """ Set horizontal mirroring """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Hmirror %d"%val)
                        result = camera.hmirror(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.hmirror(val)
                return result

  `def is_activated()`

    Indicates if the camera is configured to be activated

    Expand source code

        @staticmethod
        def is_activated():
                """ Indicates if the camera is configured to be activated """
                if Camera.config is None:
                        Camera.get_config()
                if Camera.config is not None:
                        return Camera.config.activated
                else:
                        return False

  `def is_modified()`

    Indicates that the camera configuration has been changed

    Expand source code

        @staticmethod
        def is_modified():
                """ Indicates that the camera configuration has been changed """
                return Camera.modified[0]

  `def is_opened()`

    Indicates if the camera opened

    Expand source code

        @staticmethod
        def is_opened():
                """ Indicates if the camera opened """
                return Camera.opened

  `def motion()`

    Get the motion informations. This contains a jpeg image, with matrices of the different color RGB

    Expand source code

        @staticmethod
        def motion():
                """ Get the motion informations.
                This contains a jpeg image, with matrices of the different color RGB """
                Camera.aquisition[0] = True
                return Camera.retry(camera.motion)

  `def open()`

    Open the camera

    Expand source code

        @staticmethod
        def open():
                """ Open the camera """
                Camera.get_config()
                if Camera.is_activated():
                        result = True
                        if Camera.opened is False:
                                for i in range(10):
                                        res = camera.init()
                                        if res is False:
                                                # print("Camera not initialized")
                                                camera.deinit()
                                                time.sleep(0.5)
                                        else:
                                                break
                                else:
                                        result = False

                                if result:
                                        # Photo on 800x600, motion detection / 8 (100x75), each square detection 8x8 (12.5 x 9.375)
                                        Camera.opened = True
                                        Camera.aquisition[0] = False
                else:
                        result = False
                return result

  `def pixformat(format_=None)`

    Change the format of image

    Expand source code

        @staticmethod
        def pixformat(format_=None):
                """ Change the format of image """
                result = None
                Camera.modified[0] = True
                val = None
                if   format_ == b"RGB565"    : val=camera.PIXFORMAT_RGB565
                elif format_ == b"YUV422"    : val=camera.PIXFORMAT_YUV422
                elif format_ == b"GRAYSCALE" : val=camera.PIXFORMAT_GRAYSCALE
                elif format_ == b"JPEG"      : val=camera.PIXFORMAT_JPEG
                elif format_ == b"RGB888"    : val=camera.PIXFORMAT_RGB888
                elif format_ == b"RAW"       : val=camera.PIXFORMAT_RAW
                elif format_ == b"RGB444"    : val=camera.PIXFORMAT_RGB444
                elif format_ == b"RGB555"    : val=camera.PIXFORMAT_RGB555
                if Camera.opened:
                        # print("Pixformat %s"%strings.tostrings(format_))
                        result = camera.pixformat(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.pixformat(val)
                return result

  `def quality(val=None, modified=True)`

    Configure the compression

    Expand source code

        @staticmethod
        def quality(val=None, modified=True):
                """ Configure the compression """
                result = None
                Camera.modified[0] = modified
                if Camera.opened:
                        # print("Quality %d"%val)
                        result = camera.quality(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.quality(val)
                return result

  `def reinit()`

    Close and open camera if get image previously asked, it avoid a DMA problem

    Expand source code

        @staticmethod
        def reinit():
                """ Close and open camera if get image previously asked, it avoid a DMA problem """
                if Camera.opened:
                        if Camera.aquisition[0] is True:
                                if "FREENOVE CAM" in os.uname().machine:
                                        Camera.close()
                                        Camera.open()

  `async def reserve(object_, timeout=0, suspension=None)`

    Reserve the camera, is used to stream the output of the camera to the web page. It stop the motion detection during this phase

    Expand source code

        @staticmethod
        async def reserve(object_, timeout=0, suspension=None):
                """ Reserve the camera, is used to stream the output of the camera
                to the web page. It stop the motion detection during this phase """
                return await Camera.reservation.reserve(object_, timeout, suspension)

  `def reset()`

    Reset the camera

    Expand source code

        @staticmethod
        def reset():
                """ Reset the camera """
                if Camera.opened is True:
                        camera.reset()

  `def reset_stat()`

    Reset statistic

    Expand source code

        @staticmethod
        def reset_stat():
                """ Reset statistic """
                Camera.success[0] = 0
                Camera.failed [0] = 0
                Camera.new_failed[0] = 0

  `def retry(callback)`

    Retry camera action and manage error

    Expand source code

        @staticmethod
        def retry(callback):
                """ Retry camera action and manage error """
                result = None
                if Camera.opened:
                        retry = 10
                        while 1:
                                if retry <= 0:
                                        tools.system.reboot("Reboot forced after camera problem")
                                try:
                                        result = callback()
                                        Camera.success[0] += 1
                                        break
                                except ValueError:
                                        Camera.failed[0] += 1
                                        Camera.new_failed[0] += 1
                                        if retry <= 3:
                                                tools.logger.syslog("Failed to get image %d retry before reset"%retry)
                                        retry -= 1
                                        time.sleep(0.5)
                        total = Camera.success[0] + Camera.failed[0]
                        STAT_CAMERA=20000
                        if (total % STAT_CAMERA) == 0:
                                if Camera.success[0] != 0:
                                        new_failed = 100.-((Camera.new_failed[0]*100)/STAT_CAMERA)
                                        failed    = 100.-((Camera.failed[0]*100)/total)
                                else:
                                        new_failed = 0.
                                        failed    = 0.
                                tools.logger.syslog("Camera stat : last %-3.1f%%, total %-3.1f%% success on %d"%(new_failed, failed, total))
                                Camera.new_failed[0] = 0
                return result

  `def saturation(val=None)`

    Change the saturation

    Expand source code

        @staticmethod
        def saturation(val=None):
                """ Change the saturation """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Saturation %d"%val)
                        result = camera.saturation(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.saturation(val)
                return result

  `def set_flash(state)`

    On the esp32cam, we use the output of the led flash to communicate with the dfplayer, and this disrupts the operation, then we can turn it off

    Expand source code

        @staticmethod
        def set_flash(state):
                """ On the esp32cam, we use the output of the led flash to communicate with the dfplayer,
                and this disrupts the operation, then we can turn it off """
                Camera.flash_enabled[0] = state

  `def sharpness(val=None)`

    Change the sharpness

    Expand source code

        @staticmethod
        def sharpness(val=None):
                """ Change the sharpness """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Sharpness %d"%val)
                        result = camera.sharpness(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.sharpness(val)
                return result

  `async def unreserve(object_)`

    Unreserve the camera

    Expand source code

        @staticmethod
        async def unreserve(object_):
                """ Unreserve the camera """
                return await Camera.reservation.unreserve(object_)

  `def vflip(val=None)`

    Set the vertical flip

    Expand source code

        @staticmethod
        def vflip(val=None):
                """ Set the vertical flip """
                result = None
                Camera.modified[0] = True
                if Camera.opened:
                        # print("Vflip %d"%val)
                        result = camera.vflip(val)
                        if result is False and val is not None:
                                Camera.reinit()
                                result = camera.vflip(val)
                return result

`class CameraConfig`

  Class that collects the camera rendering configuration

  Constructor

  Expand source code

      class CameraConfig(tools.jsonconfig.JsonConfig):
              """ Class that collects the camera rendering configuration """
              def __init__(self):
                      """ Constructor """
                      tools.jsonconfig.JsonConfig.__init__(self)
                      self.activated  = True
                      self.framesize  = b"640x480"
                      self.pixformat  = b"JPEG"
                      self.quality    = 25
                      self.brightness = 0
                      self.contrast   = 0
                      self.saturation = 0
                      self.hmirror    = False
                      self.vflip      = False
                      self.flash_level = 0

  ### Ancestors

  - tools.jsonconfig.JsonConfig
`class Reservation`

  Manage the camera reservation

  Constructor

  Expand source code

      class Reservation:
              """ Manage the camera reservation """
              def __init__(self):
                      """ Constructor """
                      self.identifier = None
                      self.count = 0
                      self.lock = uasyncio.Lock()
                      self.suspended = 0

              async def reserve(self, object_, timeout=0, suspension=None):
                      """ Wait the ressource and reserve """
                      result = False
                      # Wait
                      while True:
                              result = await self.acquire(object_, suspension)
                              if result:
                                      break
                              timeout -= 1
                              if timeout < 0:
                                      break
                              await uasyncio.sleep_ms(1000)
                      return result

              async def acquire(self, object_, suspension=None):
                      """ Reserve the camera, is used to stream the output of the camera
                      to the web page. It stop the motion detection during this phase """
                      result = False
                      await self.lock.acquire()
                      identifier = id(object_)
                      try:
                              # If not reserved
                              if self.identifier is None:
                                      # If suspension not required
                                      if suspension is None:
                                              # If previous suspension ended
                                              if self.suspended <= 0:
                                                      # Reserve
                                                      self.identifier = identifier
                                                      self.count = 1
                                                      self.suspended = 0
                                                      result = True
                                              else:
                                                      # Decrease suspension counter
                                                      self.suspended -= 1
                                      else:
                                              # Reserve
                                              self.identifier = identifier
                                              self.count = 1
                                              self.suspended = suspension
                                              result = True
                              # If already reserved by the current object_
                              elif self.identifier == identifier:
                                      # Increase reservation counter
                                      self.count += 1
                                      self.suspended = suspension
                                      result = True
                      finally:
                              self.lock.release()
                      return result

              async def unreserve(self, object_):
                      """ Unreserve the camera """
                      result = False
                      await self.lock.acquire()
                      identifier = id(object_)
                      try:
                              if self.identifier == identifier:
                                      if self.count <= 1:
                                              self.count = 0
                                              self.identifier = None
                                      else:
                                              self.count -= 1
                                      result = True
                      finally:
                              self.lock.release()
                      return result

  ### Methods

  `async def acquire(self, object_, suspension=None)`

    Reserve the camera, is used to stream the output of the camera to the web page. It stop the motion detection during this phase

    Expand source code

        async def acquire(self, object_, suspension=None):
                """ Reserve the camera, is used to stream the output of the camera
                to the web page. It stop the motion detection during this phase """
                result = False
                await self.lock.acquire()
                identifier = id(object_)
                try:
                        # If not reserved
                        if self.identifier is None:
                                # If suspension not required
                                if suspension is None:
                                        # If previous suspension ended
                                        if self.suspended <= 0:
                                                # Reserve
                                                self.identifier = identifier
                                                self.count = 1
                                                self.suspended = 0
                                                result = True
                                        else:
                                                # Decrease suspension counter
                                                self.suspended -= 1
                                else:
                                        # Reserve
                                        self.identifier = identifier
                                        self.count = 1
                                        self.suspended = suspension
                                        result = True
                        # If already reserved by the current object_
                        elif self.identifier == identifier:
                                # Increase reservation counter
                                self.count += 1
                                self.suspended = suspension
                                result = True
                finally:
                        self.lock.release()
                return result

  `async def reserve(self, object_, timeout=0, suspension=None)`

    Wait the ressource and reserve

    Expand source code

        async def reserve(self, object_, timeout=0, suspension=None):
                """ Wait the ressource and reserve """
                result = False
                # Wait
                while True:
                        result = await self.acquire(object_, suspension)
                        if result:
                                break
                        timeout -= 1
                        if timeout < 0:
                                break
                        await uasyncio.sleep_ms(1000)
                return result

  `async def unreserve(self, object_)`

    Unreserve the camera

    Expand source code

        async def unreserve(self, object_):
                """ Unreserve the camera """
                result = False
                await self.lock.acquire()
                identifier = id(object_)
                try:
                        if self.identifier == identifier:
                                if self.count <= 1:
                                        self.count = 0
                                        self.identifier = None
                                else:
                                        self.count -= 1
                                result = True
                finally:
                        self.lock.release()
                return result