#!/usr/local/bin/python # -*- 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 sys import threading import getpass import argparse import argcomplete 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 amcrest import AmcrestCamera if os.name == 'nt': AMCREST_CONF = os.path.join(os.getenv("HOMEPATH"), '.config\\amcrest.conf') else: AMCREST_CONF = os.path.join(os.getenv("HOME"), '.config/amcrest.conf') def graceful_exit(_signo, _stack_frame): """ When the process get SIGTERM, we might see "Terminated" message in console. As this cli is user friendly, let's just terminate silently. """ sys.exit(0) def __sigtermproc(process_pid): """ For continuous flows, i.e: audio packets, there is no option in the http api to pass the maximum time for capturing the audio sequence. In that case, let's send a SIGTERM to amcrest-cli when the max time of capture is done and task finished. """ os.kill(process_pid, signal.SIGTERM) def main(): signal.signal(signal.SIGTERM, graceful_exit) parser = argparse.ArgumentParser( description='Command line interface for Amcrest cameras.', formatter_class=argparse.RawTextHelpFormatter ) # pylint: disable=fixme # FIXME: Add metavar in the options for better understanding in the # --help output parser.add_argument('-H', '--hostname', dest='hostname', help='hostname or ip address for Amcrest camera') parser.add_argument('-u', '--username', dest='username', help='username for Amcrest camera') parser.add_argument('-p', '--password', dest='password', help='password for Amcrest camera') parser.add_argument('-P', '--port', dest='port', default=80, help='port to Amcrest camera. Default: 80') parser.add_argument('--camera', dest='camera', help='specify which section to read from ' '~/.config/amcrest.conf') parser.add_argument('--info-user', dest='info_user', help='info about username') parser.add_argument('--info-all-users', action='store_true', help='info about all users') parser.add_argument('--info-all-active-users', action='store_true', help='info about all active users') parser.add_argument('--info-group', dest='info_group', help='info about group') parser.add_argument('--info-all-groups', action='store_true', help='info about all groups') parser.add_argument('--delete-user', dest='delete_user', help='delete username') parser.add_argument('--add-user', dest='add_user', metavar='username password group [sharable]' ' [reserved] [memo]', nargs='+', help='Add new user') parser.add_argument('--modify-password', dest='modify_password', nargs='+', metavar='username newpwd oldpwd', help='modify user password') parser.add_argument('--modify-user', dest='modify_user', nargs='+', metavar='username attribute newvalue', help='modify user attribute') # FIXME: Add param in the cli to users pass the delay option # in reboot parser.add_argument('--reboot', action='store_true', help='reboot the camera') parser.add_argument('--shutdown', action='store_true', help='shutdown the camera') parser.add_argument('--general-config', action='store_true', help='general configuration') parser.add_argument('--software-information', action='store_true', help='software information') parser.add_argument('--device-class', action='store_true', help='device class') parser.add_argument('--machine-name', action='store_true', help='machine name') parser.add_argument('--serial-number', action='store_true', help='serial number') parser.add_argument('--onvif-information', action='store_true', help='onvif information') parser.add_argument('--vendor-information', action='store_true', help='vendor information') parser.add_argument('--system-information', action='store_true', help='the system information') parser.add_argument('--device-type', action='store_true', help='device type') parser.add_argument('--config-backup', action='store_true', help='config backup') parser.add_argument('--hardware-version', action='store_true', help='hardware version') parser.add_argument('--audio-input-channels-numbers', action='store_true', help='audio input channels numbers') parser.add_argument('--audio-output-channels-numbers', action='store_true', help='audio output channels numbers') parser.add_argument('--snapshot', dest='snapshot_option', default='0', nargs='?', const='0', help='get snapshot, use 0, 1 or 2\n' '0 - regular snapshot\n' '1 - motion detection snapshot\n' '2 - alarm snapshot') parser.add_argument('--network-interfaces', action='store_true', help='Get network interfaces') parser.add_argument('--video-blind-detect-config', action='store_true', help='Get video blind detect config') parser.add_argument('--video-loss-detect-config', action='store_true', help='Get video loss detect config') parser.add_argument('--version-http-api', action='store_true', help='Get version of HTTP API') parser.add_argument('--scan-devices', dest='scan_devices', help='scan Amcrest cameras in the subnet provided\n' 'i.e: 192.168.1.0/24') parser.add_argument('--play-wav', dest='play_wav', help='play wav files') parser.add_argument('--timeout', dest='timeout', type=int, const=None, help='set timeout for operations like\n' ' scan-devices') parser.add_argument('--current-time', nargs='?', const=-1, help='camera current time') parser.add_argument('--motion-detection', nargs='?', const=-1, help='enable/disable motion detection (True or False)') parser.add_argument('--network-config', nargs='?', const=-1, help='network configuration') parser.add_argument('--upnp-status', nargs='?', const=-1, help='upnp status') parser.add_argument('--upnp-config', nargs='?', const=-1, help='upnp config') parser.add_argument('--ntp-config', nargs='?', const=-1, help='ntp config') parser.add_argument('--snapshot-config', nargs='?', const=-1, help='get snapshot configuration') parser.add_argument('--save', dest='save', help='save output as file for operations like:\n' '--snapshot\n' '--audio-stream-capture\n' '--config-backup') parser.add_argument('--event-handler-config', dest='event_handler_config', help='get event handler configname') parser.add_argument('--alarm-config', action='store_true', help='get alarm config') parser.add_argument('--alarm-out-config', action='store_true', help='get alarm out config') parser.add_argument('--alarm-states-output-channels', action='store_true', help='alarm states output channels') parser.add_argument('--alarm-states-input-channels', action='store_true', help='alarm states input channels') parser.add_argument('--alarm-input-channels', action='store_true', help='alarm input channels') parser.add_argument('--alarm-output-channels', action='store_true', help='alarm output channels') parser.add_argument('--event-login-failure', action='store_true', help='event login failure') parser.add_argument('--event-storage-not-exist', action='store_true', help='event storage not exist') parser.add_argument('--event-management', action='store_true', help='event management') parser.add_argument('--event-storage-access-failure', action='store_true', help='event storage access failure') parser.add_argument('--event-storage-low-space', action='store_true', help='event storage low space') parser.add_argument('--event-net-abort', action='store_true', help='event net abort') parser.add_argument('--event-ip-conflict', action='store_true', help='event ip conflict') parser.add_argument('--event-channels-happened', dest='event_channels_happened', help='params: VideoMotion, VideoLoss,\n' ' VideoBlind, Alarmlocal') parser.add_argument('--record-capability', action='store_true', help='show record capability') parser.add_argument('--factory-create', action='store_true', help='create a media file finder') parser.add_argument('--record-mode', nargs='?', const=-1, help='show/set record mode') parser.add_argument('--record-config', nargs='?', const=-1, help='show/set record config') parser.add_argument('--media-global-config', action='store_true', help='media global config') parser.add_argument('--video-max-extra-stream', action='store_true', help='max extra stream') parser.add_argument('--video-color-config', action='store_true', help='color config') parser.add_argument('--encode-capability', action='store_true', help='encode capability') parser.add_argument('--encode-config-capability', action='store_true', help='encode config capability') parser.add_argument('--encode-media', action='store_true', help='encode media') parser.add_argument('--encode-region-interested', action='store_true', help='encode region interested') parser.add_argument('--video-channel-title', action='store_true', help='channel title') parser.add_argument('--video-input-channels-device-supported', action='store_true', help='input channels device supported') parser.add_argument('--video-output-channels-device-supported', action='store_true', help='output channels device supported') parser.add_argument('--video-max-remote-input-channels', action='store_true', help='max remote input channels') parser.add_argument('--video-in-options', action='store_true', help='video in options') parser.add_argument('--video-widget-config', action='store_true', help='widget config') parser.add_argument('--video-out-options', action='store_true', help='video out options') parser.add_argument('--video-input-capability', action='store_true', help='video input capability') parser.add_argument('--video-coordinates-current-window', dest='coordinates_current_window', help='coordinates current window') parser.add_argument('--video-standard', nargs='?', const=-1, help='view/set video standard (PAL or NTSC)') parser.add_argument('--log-clear-all', action='store_true', help='clear all logs') parser.add_argument('--ptz-config', action='store_true', help='show ptz config') parser.add_argument('--ptz-auto-movement', action='store_true', help='show ptz auto movement') parser.add_argument('--ptz-presets-list', action='store_true', help='show ptz preset list') parser.add_argument('--ptz-goto-preset', dest='ptz_goto_preset', metavar='channel preset_point_number', nargs='+', help='move camera to preset configuration') parser.add_argument('--wlan-config', action='store_true', help='show/set wlan-config') parser.add_argument('--telnet-config', nargs='?', const=-1, help='enable/disable motion detection (True or False)') parser.add_argument('--scan-wlan-devices', dest='scan_wlan_devices', help='scan wlan devices') parser.add_argument('--mjpg-stream', metavar='timercapture [channelno] [typeno]', nargs='+', help='capture mjpg stream') parser.add_argument('--audio-stream-capture', dest='audio_stream_capture', metavar='httptype channel timercapture', nargs='+', help='capture audio stream') parser.add_argument('--audio-send-stream', dest='audio_send_stream', metavar='httptype channel filename', nargs='+', help='send audio stream') # FIXME: # parser.add_argument('--log-show', # help='show logs ') argcomplete.autocomplete(parser) args = parser.parse_args() if os.path.isfile(AMCREST_CONF) and not args.scan_devices: config = ConfigParser() config.read(AMCREST_CONF) # if more cameras found, requires --camera option if len(config.sections()) > 1 and not args.camera: print("More than 1 camera found at %s. " "Append the --camera option." % AMCREST_CONF) sys.exit(-1) if args.camera: section = args.camera else: section = config.sections()[0] try: args.hostname = config.get(section, 'hostname') args.port = config.getint(section, 'port') args.username = config.get(section, 'username') try: args.password = config.get(section, 'password', raw=True) except NoOptionError: args.password = getpass.getpass() except (NoSectionError, NoOptionError) as e: print("ERROR! %s found at %s" % (e, AMCREST_CONF)) sys.exit(-1) amcrest = AmcrestCamera( args.hostname, args.port, args.username, args.password ) camera = amcrest.camera if args.scan_devices: ret = camera.scan_devices(args.scan_devices, args.timeout) if ret: print(("Looks like amcrest device: {0}".format(ret))) else: print("No devices found, try again later!") if args.username and args.hostname and args.password: if args.telnet_config == -1: print((camera.telnet_config)) elif args.mjpg_stream: if not args.save: print("This option requires --save") timer_capture = float(args.mjpg_stream[0]) channelno = None typeno = None if len(args.mjpg_stream) > 1: channelno = args.mjpg_stream[1] if len(args.mjpg_stream) > 2: typeno = args.mjpg_stream[2] t = threading.Timer(timer_capture, __sigtermproc, [os.getpid()]) t.start() print(camera.mjpg_stream( channel=channelno, typeno=typeno, path_file=args.save)) elif args.reboot: print(camera.reboot()) elif args.shutdown: print(camera.shutdown()) elif args.audio_send_stream: if len(args.audio_send_stream) <= 3: print("Requires arguments: httptype, channel, filename, codec") sys.exit(-1) httptype = args.audio_send_stream[0] channel = args.audio_send_stream[1] filename = args.audio_send_stream[2] codec = args.audio_send_stream[3] camera.audio_send_stream(httptype, channel, filename, codec) elif args.audio_stream_capture: if not args.save: print("This option requires --save") if len(args.audio_stream_capture) <= 2: print("Requires arguments: httptype, channel, timer_capture!") sys.exit(-1) httptype = args.audio_stream_capture[0] channel = args.audio_stream_capture[1] timer_capture = float(args.audio_stream_capture[2]) t = threading.Timer(timer_capture, __sigtermproc, [os.getpid()]) t.start() if args.save: camera.audio_stream_capture(httptype, channel, args.save) else: camera.audio_stream_capture(httptype, channel) elif args.play_wav: camera.play_wav(path_file=args.play_wav) elif args.wlan_config: print(camera.wlan_config) elif args.scan_wlan_devices: print(camera.scan_wlan_devices(args.scan_wlan_devices)) elif args.telnet_config: camera.telnet_config = args.telnet_config elif args.ptz_config: print(camera.ptz_config) elif args.ptz_auto_movement: print(camera.ptz_auto_movement) elif args.ptz_presets_list: print(camera.ptz_presets_list()) elif args.ptz_goto_preset: if len(args.ptz_goto_preset) < 2: print("Requires arguments: channel preset_point_number") sys.exit(-1) channel = args.ptz_goto_preset[0] preset_point_number = args.ptz_goto_preset[1] camera.go_to_preset(channel=channel, preset_point_number=preset_point_number) elif args.log_clear_all: print(camera.log_clear_all()) elif args.video_standard == -1: print(camera.video_standard) elif args.video_standard: camera.video_standard = args.video_standard elif args.coordinates_current_window: print(camera.coordinates_current_window( args.coordinates_current_window)) elif args.video_widget_config: print(camera.video_widget_config) elif args.video_input_capability: print(camera.video_input_capability) elif args.video_in_options: print(camera.video_in_options) elif args.video_out_options: print(camera.video_out_options) elif args.video_max_remote_input_channels: print(camera.video_max_remote_input_channels) elif args.video_output_channels_device_supported: print(camera.video_output_channels_device_supported) elif args.video_input_channels_device_supported: print(camera.video_input_channels_device_supported) elif args.video_channel_title: print(camera.video_channel_title) elif args.encode_region_interested: print(camera.encode_region_interested) elif args.encode_media: print(camera.encode_media) elif args.encode_config_capability: print(camera.encode_config_capability) elif args.encode_capability: print(camera.encode_capability) elif args.video_max_extra_stream: print(camera.video_max_extra_stream) elif args.video_color_config: print(camera.video_color_config) elif args.factory_create: print(camera.factory_create) elif args.record_capability: print(camera.record_capability) elif args.record_config == -1: print(camera.record_config) elif args.record_config is not None: camera.record_config = args.record_config elif args.media_global_config: print(camera.media_global_config) elif args.audio_input_channels_numbers: print(camera.audio_input_channels_numbers) elif args.audio_output_channels_numbers: print(camera.audio_output_channels_numbers) elif args.event_storage_not_exist: print(camera.event_storage_not_exist) elif args.event_login_failure: print(camera.event_login_failure) elif args.event_net_abort: print(camera.event_net_abort) elif args.event_ip_conflict: print(camera.event_ip_conflict) elif args.event_management: print(camera.event_management) elif args.event_storage_low_space: print(camera.event_storage_low_space) elif args.event_storage_access_failure: print(camera.event_storage_access_failure) elif args.general_config: print(camera.general_config) elif args.video_blind_detect_config: print(camera.video_blind_detect_config) elif args.video_loss_detect_config: print(camera.video_loss_detect_config) elif args.alarm_config: print(camera.alarm_config) elif args.alarm_out_config: print(camera.alarm_out) elif args.alarm_output_channels: print(camera.alarm_output_channels) elif args.alarm_input_channels: print(camera.alarm_input_channels) elif args.alarm_states_output_channels: print(camera.alarm_states_output_channels) elif args.alarm_states_input_channels: print(camera.alarm_states_input_channels) elif args.event_channels_happened: print(camera.event_channels_happened( args.event_channels_happened)) elif args.event_handler_config: print(camera.event_handler_config(args.event_handler_config)) elif args.info_group: print(camera.info_user(args.info_group)) elif args.info_all_groups: print(camera.info_all_groups) elif args.add_user: if len(args.add_user) <= 2: print("This option requires at least: " "username, password and group") sys.exit(-1) user = args.add_user[0] pwd = args.add_user[1] grp = args.add_user[2] if len(args.add_user) >= 4: sharable = args.add_user[3] else: sharable = False if len(args.add_user) >= 5: reserved = args.add_user[4] else: reserved = False if len(args.add_user) >= 6: memo = args.add_user[5] else: memo = False print(camera.add_user(user, pwd, grp, sharable, reserved, memo)) elif args.modify_user: if len(args.modify_user) <= 2: print("This option requires at least: " "username, attribute and new value") sys.exit(-1) user = args.modify_user[0] attr = args.modify_user[1] newvalue = args.modify_user[2] print(camera.modify_user(user, attr, newvalue)) elif args.modify_password: if len(args.modify_password) <= 2: print("This option requires at least: " "username, new password and old password") sys.exit(-1) user = args.modify_password[0] newpwd = args.modify_password[1] oldpwd = args.modify_password[2] print(camera.modify_password(user, newpwd, oldpwd)) elif args.delete_user: print(camera.delete_user(args.delete_user)) elif args.info_all_users: print(camera.info_all_users) elif args.info_all_active_users: print(camera.info_all_active_users) elif args.info_user: print(camera.info_user(args.info_user)) elif args.device_class: print(camera.device_class) elif args.onvif_information: print(camera.onvif_information) elif args.vendor_information: print(camera.vendor_information) elif args.system_information: print(camera.system_information) elif args.machine_name: print(camera.machine_name) elif args.device_type: print(camera.device_type) elif args.serial_number: print(camera.serial_number) elif args.hardware_version: print(camera.hardware_version) elif args.version_http_api: print(camera.version_http_api) elif args.software_information: print(camera.software_information) elif args.network_config: print(camera.network_config) elif args.upnp_config == -1: print(camera.upnp_config) elif args.upnp_config is not None: camera.upnp_config = args.upnp_config elif args.upnp_status == -1: print(camera.upnp_status) elif args.ntp_config == -1: print(camera.ntp_config) elif args.ntp_config is not None: camera.ntp_config = args.ntp_config elif args.network_interfaces: print(camera.network_interfaces) elif args.current_time == -1: print(camera.current_time) elif args.motion_detection == -1: print(camera.motion_detection) elif args.motion_detection: camera.motion_detection = args.motion_detection elif args.current_time is not None: camera.current_time = args.current_time elif args.snapshot_config: print(camera.snapshot_config) elif args.config_backup: if args.save: camera.config_backup(args.save) else: print("This option requires --save") elif args.snapshot_option: if args.save: camera.snapshot(args.snapshot_option, args.save) else: print("This option requires --save") else: print('Error: You must specify a valid operation. See usage:') parser.print_usage() if __name__ == '__main__': main()