import json import hashlib import hmac import base64 import requests import dateutil.parser from datetime import datetime, timedelta from time import mktime from email.utils import formatdate from requests.packages import urllib3 from intelab_python_sdk.logger import log class HikVisionClient(object): def __init__(self, app_key, app_secret, host, port, https=True): self.app_key = app_key self.app_secret = app_secret self.host = host self.port = port self.https = https self.url = '{}://{}:{}'.format('https' if https else 'http', self.host, self.port) @staticmethod def iso_format(t): return '{}+08:00'.format(t.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]) def _sign(self, uri, body, method='POST'): content_md5 = hashlib.md5() content_md5.update(json.dumps(body).encode('utf-8')) date = formatdate(timeval=mktime(datetime.now().timetuple()), localtime=False, usegmt=True) string_to_be_signed = '{method}\n*/*\napplication/json\n{date}\nx-ca-key:{app_key}\n{uri}' signature = str(base64.b64encode(hmac.new( self.app_secret.encode('utf-8'), string_to_be_signed.format(method=method, date=date, app_key=self.app_key, uri=uri).encode('utf-8'), digestmod=hashlib.sha256).digest()), 'utf-8') return { "Accept": "*/*", "Content-Type": "application/json", "Date": date, "X-Ca-Key": self.app_key, "X-Ca-Signature": signature, "X-Ca-Signature-Headers": "X-Ca-Key" } def _request(self, uri, body): url = self.url + uri urllib3.disable_warnings() response = requests.post(url, headers=self._sign(uri, body, 'POST'), json=body, verify=False) data = {} if response.status_code == 200: data['code'] = 200 res_json = response.json() if res_json.get('code', '0x000000') == '0': if 'data' in res_json: data = res_json['data'] else: log.warning(res_json['msg']) else: log.error('error: %s', response.text) return data def get_camera_preview_url(self, camera_index_code, protocol='rtsp', stream_type=0): """ :param camera_index_code: :param protocol: :param stream_type: 流类型,0 主码流; 1 子码流 :return: """ streamform = 'rtp' if protocol == 'rtsp' else 'ps' body = { 'cameraIndexCode': camera_index_code, 'protocol': protocol, 'streamType': stream_type, 'transmode': 1, 'expand': 'streamform={}'.format(streamform) } uri = "/artemis/api/video/v1/cameras/previewURLs" return self._request(uri, body) def get_encode_device(self, page_size=100, page_no=1): """ 根据条件查询目录下有权限的编码设备列表 """ uri = '/artemis/api/resource/v2/encodeDevice/search' body = { "pageSize": page_size, "pageNo": page_no } return self._request(uri, body) def get_cameras(self, page_size=100, page_no=1, expressions=None): """ 分页获取摄像头信息 :param page_size: :param page_no: :param expressions: 文档详见 https://open.hikvision.com/docs/aec7933a37f34aedb03853ce289e74b7#ef459f4b :return: """ body = { "pageSize": page_size, "pageNo": page_no } if expressions: body['expressions'] = expressions uri = '/artemis/api/resource/v2/camera/search' result = self._request(uri, body) camera_list = [] if result: camera_list = result['list'] return camera_list def get_cameras_playback_urls(self, camera_index_code, begin_time, end_time, protocol='rtmp'): """ 回放取流 """ streamform = 'ps' if protocol == 'rtmp' else 'rtp' uri = '/artemis/api/video/v2/cameras/playbackURLs' body = { 'cameraIndexCode': camera_index_code, 'beginTime': begin_time, 'endTime': end_time, 'recordLocation': 1, 'protocol': protocol, 'expand': 'fileSize=1024', 'streamform': streamform } log.info('requests body: {}'.format(body)) expired_time = datetime.now() + timedelta(minutes=5) res = self._request(uri, body) results = [] for f in res.get('list') or []: start_time = dateutil.parser.parse(f['beginTime']) end_time = dateutil.parser.parse(f['endTime']) results.append({ 'start_time': start_time, 'end_time': end_time, 'expired_time': expired_time, 'stream_url': { 'url': res.get('url'), 'extra_args': 'playBackMode=1', 'protocol': protocol } }) return results def event_subscription(self, callback): """ 事件订阅接口 :param callback: 指定事件接收的地址,采用restful回调模式,支持http和https """ uri = '/artemis/api/eventService/v1/eventSubscriptionByEventTypes' body = { 'eventTypes': [131331], # 移动侦测 'eventDest': callback } res = self._request(uri, body) return res def event_unsubscription(self): """ 指定事件类型取消订阅 """ uri = '/artemis/api/eventService/v1/eventUnSubscriptionByEventTypes' body = { 'eventTypes': [131331] } res = self._request(uri, body) return res def get_camera_online(self, camera_index_code): """ 获取监控点在线状态(貌似无用) """ uri = '/artemis/api/nms/v1/online/encode_device/get' # uri = '/artemis/api/nms/v1/online/camera/get' body = { 'indexCodes': [camera_index_code] } res = self._request(uri, body) return res