#!/usr/local/bin/python """Amcrest Text User Interface.""" # -*- coding: utf-8 -*- # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # vim:sw=4:ts=4:et # pylint: disable=invalid-name from __future__ import print_function import os import signal import subprocess import tempfile import time try: # import for Python 2.x from ConfigParser import ConfigParser, NoOptionError, NoSectionError except ImportError: # import for Python 3.x from configparser import ConfigParser, NoOptionError, NoSectionError from argparse import ArgumentParser from amcrest import AmcrestCamera class AmcrestTuiViewer(object): """Amcrest Text User Interface.""" AMCREST_CONF = "{0}/.config/amcrest.conf".format(os.path.expanduser("~")) def __init__(self, config_file_name, channel_snapshot, timer_between_snapshots, timeout_error_msg, merge_snapshots, framebuffer_device): """ Define AmcrestTuiViever init method. Config_file_name - optional amcrest config file Timer_between_snapshots - timer between each snapshot """ if not os.path.exists(self.AMCREST_CONF): raise RuntimeError( "Please set the initial {0}".format(self.AMCREST_CONF) ) # Timeout settings self.timer_between_snapshots = timer_between_snapshots self.timeout_error_msg = timeout_error_msg # Channel for snapshot self.channel_snapshot = channel_snapshot # Hold fbi pid self.cmd = None # pylint: disable=fixme # FIXME: User might be able to provide config file path self.config_file_name = config_file_name # FIXME: Add such feature via convert tool self.merge_snapshots = merge_snapshots self.fb_device = framebuffer_device # Setting config stuff for amcrest.conf self.config = ConfigParser() self.config.read(self.AMCREST_CONF) # Creating dict to hold cameras data from config file self.amcrest_config = {} # Filling the dictionary amcrest_config with all cameras available for camera_number in range(len(self.config.sections())): camera_name = self.config.sections()[camera_number] try: self.amcrest_config[camera_name] = { 'hostname': self.config.get(camera_name, 'hostname'), 'port': self.config.get(camera_name, 'port'), 'username': self.config.get(camera_name, 'username'), 'password': self.config.get(camera_name, 'password'), 'device': AmcrestCamera( host=self.config.get(camera_name, 'hostname'), port=self.config.get(camera_name, 'port'), user=self.config.get(camera_name, 'username'), password=self.config.get( camera_name, 'password')).camera } except (NoSectionError, NoOptionError) as e: print("ERROR! %s found at %s" % (e, self.AMCREST_CONF)) raise def _kill_process(self, alarm, stack): # As fbida waits users input to close the program, # we need to set timeout and as soon the time expire send # SIGTERM to program to open a new one and avoid race. os.kill(self.cmd.pid, signal.SIGTERM) def _view_image(self, image): self.cmd = subprocess.Popen( ['fbi', '-a', '-noverbose', '-d', self.fb_device, image], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False ) signal.signal(signal.SIGALRM, self._kill_process) signal.alarm(self.timer_between_snapshots) self.cmd.communicate() def _snapshot(self, tmpfile): # Execute the snapshot for each camera listed try: self.dev.snapshot(channel=self.channel_snapshot, path_file=tmpfile) except Exception as e: raise RuntimeError(e) from e # pylint: disable=unused-variable def run(self): """Call run method.""" for counter, camera in enumerate(self.amcrest_config): with tempfile.NamedTemporaryFile(dir='/tmp', delete=True) as tmpfile: # pylint: disable=attribute-defined-outside-init # Add new tmpfile name self.dev = self.amcrest_config[camera]['device'] os.system("clear") print("Loading new image from: {0}".format(camera)) try: self._snapshot(tmpfile.name) except RuntimeError as e: print(("Error trying to get a snapshot! " "{0}".format(str(e)))) time.sleep(self.timeout_error_msg) continue self._view_image(tmpfile.name) if __name__ == "__main__": parser = ArgumentParser() parser.add_argument( '-f', '--config-file-name', help="specify the amcrest config file", required=False ) parser.add_argument( '-c', '--channel-snapshot', help="channel for snapshot", default=0, required=False ) parser.add_argument( '-d', '--framebuffer-device', help="speficy the framebuffer device, default /dev/fb0", default="/dev/fb0", required=False ) parser.add_argument( '-t', '--timer-between-snapshots', help="timer between snapshots, default 2 sec", default=2, required=False ) parser.add_argument( '-e', '--timeout-error-msg', help="timeout in case errors messages should be displayed" ", default 5sec", default=5, required=False ) parser.add_argument( '-m', '--merge-snapshots', help="merge snapshots from all cameras in one photo to be displayed", required=False ) args = parser.parse_args() # Check if we have permission to framebuffer device with open(args.framebuffer_device, 'r') as f: pass # pylint: disable=fixme # FIXME: Add option to save the snapshots in the disk # (requires to set dir as well) tui_viewer = AmcrestTuiViewer( config_file_name=args.config_file_name, channel_snapshot=args.channel_snapshot, timer_between_snapshots=args.timer_between_snapshots, timeout_error_msg=args.timeout_error_msg, merge_snapshots=args.merge_snapshots, framebuffer_device=args.framebuffer_device ) while True: tui_viewer.run()