From 9c6633523ae373f724648c7a15fb581c0c4b27c2 Mon Sep 17 00:00:00 2001 From: Andreas Boeckler Date: Sat, 24 Feb 2024 15:57:02 +0100 Subject: [PATCH 1/2] gphoto2 integration [timelapse] use_gphoto2=True --- component/timelapse.py | 51 ++++++++++++++++++++++++++++++++++++++---- docs/configuration.md | 10 +++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/component/timelapse.py b/component/timelapse.py index f25138a..b97fae3 100644 --- a/component/timelapse.py +++ b/component/timelapse.py @@ -48,6 +48,7 @@ def __init__(self, confighelper: ConfigHelper) -> None: self.byrendermacro = False self.hyperlapserunning = False self.printing = False + self.gphoto2Working = False self.noWebcamDb = False self.confighelper = confighelper @@ -62,6 +63,8 @@ def __init__(self, confighelper: ConfigHelper) -> None: "frame_path", "/tmp/timelapse/") self.ffmpeg_binary_path = confighelper.get( "ffmpeg_binary_path", "/usr/bin/ffmpeg") + self.gphoto2_binary_path = confighelper.get( + "gphoto2_binary_path", "/usr/bin/gphoto2") self.wget_skip_cert = confighelper.getboolean( "wget_skip_cert_check", False) @@ -71,6 +74,8 @@ def __init__(self, confighelper: ConfigHelper) -> None: 'mode': "layermacro", 'camera': "", 'snapshoturl': "http://localhost:8080/?action=snapshot", + 'use_gphoto2': False, + 'gphoto2_waittime': 10.0, 'stream_delay_compensation': 0.05, 'gcode_verbose': False, 'parkhead': False, @@ -169,6 +174,8 @@ def __init__(self, confighelper: ConfigHelper) -> None: async def component_init(self) -> None: await self.getWebcamConfig() + if self.config['use_gphoto2'] == True: + self.gphoto2Working = await self.check_gphoto2() def overwriteDbconfigWithConfighelper(self) -> None: blockedsettings = [] @@ -460,6 +467,27 @@ async def stop_hyperlapse(self) -> None: logging.exception(msg) self.hyperlapserunning = False + async def check_gphoto2(self) -> bool: + gphoto2_installed = os.path.isfile(self.gphoto2_binary_path) + if not gphoto2_installed: + logging.info(f"timelapse: {self.gphoto2_binary_path} \ + not found please install to use gphoto2 functionality") + + cmd = self.gphoto2_binary_path + " --get-config /main/imgsettings/imageformat" + shell_cmd: SCMDComp = self.server.lookup_component('shell_command') + scmd = shell_cmd.build_shell_command(cmd, None) + try: + cmdstatus = await scmd.run(timeout=2., verbose=False) + except Exception: + logging.exception(f"Error running cmd '{cmd}'") + return False + + logging.info(f"check_gphoto2: {cmd} -> {cmdstatus}") + if cmdstatus: + return True + else: + return False + async def newframe(self) -> None: # make sure webcamconfig is uptodate before grabbing a new frame await self.getWebcamConfig() @@ -469,16 +497,27 @@ async def newframe(self) -> None: options += "--no-check-certificate " self.framecount += 1 - framefile = "frame" + str(self.framecount).zfill(6) + ".jpg" - cmd = "wget " + options + self.config['snapshoturl'] \ - + " -O " + self.temp_dir + framefile + framebasefile = "frame" + str(self.framecount).zfill(6) + framefile = framebasefile + ".jpg" + if self.config['use_gphoto2'] == True and not self.gphoto2Working: + logging.info(f"gphoto2_check failed on startup - using fallback webcam") + + waittime = 2. + if self.config['use_gphoto2'] == True and self.gphoto2Working: + cmd = self.gphoto2_binary_path + " --quiet --capture-image-and-download --filename=\"" \ + + self.temp_dir + framebasefile + ".%C\"" + waittime = self.config['gphoto2_waittime'] + else: + cmd = "wget " + options + self.config['snapshoturl'] \ + + " -O " + self.temp_dir + framefile + self.lastframefile = framefile logging.debug(f"cmd: {cmd}") shell_cmd: SCMDComp = self.server.lookup_component('shell_command') scmd = shell_cmd.build_shell_command(cmd, None) try: - cmdstatus = await scmd.run(timeout=2., verbose=False) + cmdstatus = await scmd.run(timeout=waittime, verbose=False) except Exception: logging.exception(f"Error running cmd '{cmd}'") @@ -512,6 +551,10 @@ async def handle_gcode_response(self, gresponse: str) -> None: # print_started self.cleanup() self.printing = True + self.gphoto2Working = False + + if self.config['use_gphoto2'] == True: + self.gphoto2Working = await self.check_gphoto2() # start hyperlapse if mode is set if self.config['mode'] == "hyperlapse": diff --git a/docs/configuration.md b/docs/configuration.md index 0ead318..aa2f00a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -51,6 +51,8 @@ Advanced Settings -> Gcode -> Layer Change Gcode -> ``TIMELAPSE_TAKE_FRAME`` ## Directory where the temporary frames are saved #ffmpeg_binary_path: /usr/bin/ffmpeg ## Directory where ffmpeg is installed +#gphoto2_binary_path: /usr/bin/gphoto2 +## Directory where gphoto2 is installed ``` @@ -98,6 +100,14 @@ It depends on the 'webcam' namespace in the moonraker DB and uses the 'snapshoturl', 'flip_x' and 'flip_y' in the moonraker.conf if your frontend doesn't support the webcams namespace of moonraker DB. +#### use_gphoto2 +'true' enables snapshot_cmd processing. +Make sure, that you have gphoto2 installed. + +#### gphoto2_waittime +This defines the wait time for the snapshot to be created. +You should increase the 'park_time' setting for better results. + #### gcode_verbose 'true' enables or 'false' disables verbosity of the Macros From 0cafc36c7a00f3f4eb2130459af1f15ebca11b81 Mon Sep 17 00:00:00 2001 From: Andreas Boeckler Date: Sat, 24 Feb 2024 16:04:50 +0100 Subject: [PATCH 2/2] check_gphoto2 fix --- component/timelapse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/component/timelapse.py b/component/timelapse.py index b97fae3..9253dcd 100644 --- a/component/timelapse.py +++ b/component/timelapse.py @@ -472,6 +472,7 @@ async def check_gphoto2(self) -> bool: if not gphoto2_installed: logging.info(f"timelapse: {self.gphoto2_binary_path} \ not found please install to use gphoto2 functionality") + return False cmd = self.gphoto2_binary_path + " --get-config /main/imgsettings/imageformat" shell_cmd: SCMDComp = self.server.lookup_component('shell_command')