""" 萤石云开发平台API工具 """ import time from retrying import retry import requests import dynaconf from intelab_python_sdk.logger import log from ils_common_video.db.redis import redis_connect from ils_common_video.const import ACCOUNT_TOKEN_KEY, DEVICE_SERIAL_KEY account_secret = dynaconf.settings.get('YSYACCOUNT', {}) # TODO 萤石云账号APP_KEY 和 APP_SECRET 从数据库中获取 APP_KEY_AND_ACCOUNT = {} TOKEN_AND_ACCOUNT = {} class EvizVersionClient(object): """ 莹石云api 文档:https://open.ys7.com/doc/zh/book/index/device_select.html#device_select-api9 """ ret_code = { '20002': '设备{}不存在', '20007': '设备{}不在线', '20014': 'deviceSerial-{}不合法', '20018': '该用户不拥有该设备{}', '60060': '设备{}地址未绑定' } @staticmethod def _get_access_token(account): """ 根据帐号获取token值,莹石云的token有效期为7天 """ if not account: # 设备暂未绑定帐号 return '' secret = account_secret[account] APP_KEY_AND_ACCOUNT[secret['app_key']] = account with redis_connect() as pipe: token_key = ACCOUNT_TOKEN_KEY.format(secret['app_key']) if pipe.exists(token_key): access_token = pipe.get(token_key) else: data = { "appKey": secret['app_key'], "appSecret": secret['app_secret'] } result = EvizVersionClient._do_request( 'https://open.ys7.com/api/lapp/token/get', data) log.debug(result) if 'data' in result and 'code' in result and result['code'] == '200': access_token = result['data']['accessToken'] expire_time = result['data']['expireTime'] else: # 暂时未获取到Token, 15分钟后重试 access_token = '' expire_time = int((time.time() + 15 * 60) * 1000) # 缓存token pipe.set(token_key, access_token) pipe.expireat(token_key, int(expire_time / 1000)) # 设置失效时间 if access_token: TOKEN_AND_ACCOUNT[access_token] = account return access_token @staticmethod @retry(stop_max_attempt_number=8, wait_random_min=1000, wait_random_max=10000) def _do_request(url, data=None, method='POST'): """ 调用莹石云接口 """ res = requests.request(method, url, params=data, timeout=10) res.raise_for_status() return res.json() @staticmethod def get_access_token(sn): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) account = EvizVersionClient.choice_account_of_device(sn) return EvizVersionClient._get_access_token(account) @staticmethod def get_live_address(access_token, sn): """ 获取萤石云直播地址 """ sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) live_data = { "accessToken": access_token, "source": "{}:{}".format(sn, channel) } result = EvizVersionClient._do_request( 'https://open.ys7.com/api/lapp/live/address/get', data=live_data) return result @staticmethod def choice_account_of_device(sn): """ 查询设备属于哪个萤石云账号 """ sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) sn_key = DEVICE_SERIAL_KEY.format(sn) with redis_connect() as pipe: account = '' if pipe.exists(sn_key): account = pipe.get(sn_key) else: log.info('查询设备属于哪个帐号...') for cur_account in account_secret: log.info('查询设备%s是否属于%s的帐号', sn, cur_account) access_token = EvizVersionClient._get_access_token(cur_account) result = EvizVersionClient.get_device_info(access_token, sn) if result.get('code') == '200': account = cur_account break else: log.info('当前设备%s不在配置的帐号权限中', sn) # 保存摄像头和帐号之间的关系,暂时保存22小时 pipe.set(sn_key, account, ex=3600 * 22) return account @staticmethod def get_video_by_time(access_token, sn, start_time=None, end_time=None, rec_type=1): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/video/by/time' data = { 'accessToken': access_token, 'deviceSerial': sn, 'recType': rec_type } if start_time: data.update({'startTime': start_time}) if end_time: data.update({'endTime': end_time}) result = EvizVersionClient._do_request(url, data) return result @staticmethod def get_alarm_list(access_token, sn, start_time=None, end_time=None, page_size=50, page_start=0): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = ' https://open.ys7.com/api/lapp/alarm/device/list' data = { 'accessToken': access_token, 'deviceSerial': sn, 'status': 2, # 2-全部,1-已读,0-未读 'pageSize': page_size, 'pageStart': page_start, 'alarmType': -1 } if start_time: data.update({'startTime': start_time}) if end_time: data.update({'endTime': end_time}) result = EvizVersionClient._do_request(url, data) return result @staticmethod def get_device_info(access_token, sn): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/device/info' data = { 'accessToken': access_token, 'deviceSerial': sn } return EvizVersionClient._do_request(url, data) @staticmethod def get_device_status(access_token, sn): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/device/status/get' data = { 'accessToken': access_token, 'deviceSerial': sn } return EvizVersionClient._do_request(url, data) @staticmethod def open_device_live(access_token, sn): """ 该接口用于根据序列号和通道号批量开通直播功能 """ sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/live/video/open' data = { 'accessToken': access_token, 'source': '{}:{}'.format(sn, channel) } log.info('开通设备%s:%s的直播功能', sn, channel) return EvizVersionClient._do_request(url, data) @staticmethod def set_alarm_sound(access_token, sn): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/device/alarm/sound/set' data = { 'accessToken': access_token, 'deviceSerial': sn, 'type': 2 } log.info('设置%s的告警声音为%s(0-短叫,1-长叫,2-静音)', sn, 2) return EvizVersionClient._do_request(url, data) @staticmethod def get_device_traffic_detail(access_token, sn, start_time=None, end_time=None, page_size=50, page_start=0): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/traffic/device/detail' data = { 'accessToken': access_token, 'deviceSerial': sn, 'pageSize': page_size, 'pageStart': page_start, } if start_time: data.update({'startTime': start_time}) if end_time: data.update({'endTime': end_time}) result = EvizVersionClient._do_request(url, data) return result @staticmethod def set_algorithm_config(access_token, sn, value=4): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/device/algorithm/config/set' data = { 'accessToken': access_token, 'deviceSerial': sn, 'type': 0, 'channelNo': channel, 'value': value # 该值为0~6,6表示灵敏度最高 } return EvizVersionClient._do_request(url, data) @staticmethod def get_device_list(access_token, page_size=50, page_start=0): url = 'https://open.ys7.com/api/lapp/device/list' data = { 'accessToken': access_token, 'pageSize': page_size, 'pageStart': page_start } return EvizVersionClient._do_request(url, data) @staticmethod def get_offline_device_list(): offline_device_list = [] for cur_account in account_secret: t1 = time.time() log.info('正在查询帐号%s下所有设备的离线状态...', cur_account) access_token = EvizVersionClient._get_access_token(cur_account) result = EvizVersionClient.get_device_list(access_token) online = 0 offline = 0 while True: if not result.get('data'): break else: for device in result['data']: if device['status'] == 1: online += 1 else: offline_device_list.append(device['deviceSerial']) offline += 1 size = result['page']['size'] if len(result['data']) < size: break # 获取分页的下一页 page = result['page']['page'] page = page + 1 result = EvizVersionClient.get_device_list(access_token, page_start=page, page_size=size) log.info('查询耗时%s,帐号%s共有%s个设备,在线/离线:%s/%s', time.time() - t1, cur_account, offline + online, online, offline) return offline_device_list @staticmethod def set_encrypt_off(access_token, sn, validate_code): sn, channel = sn.split(':', 1) if ':' in sn else (sn, 1) url = 'https://open.ys7.com/api/lapp/device/encrypt/off' data = { 'accessToken': access_token, 'deviceSerial': sn, 'validateCode': validate_code } return EvizVersionClient._do_request(url, data) @staticmethod def create_open_cloud_project(access_token, project_id): url = 'https://open.ys7.com/api/open/cloud/v1/project/{}'.format(project_id) data = { 'accessToken': access_token, 'expireDays': 1, 'projectName': 'WZW_Test', } return EvizVersionClient._do_request(url, data) @staticmethod def record_cloud_video_save(access_token, sn, project_id, file_id, start_time, end_time, rec_type): url = 'https://open.ys7.com/api/open/cloud/v1/video/save' data = { 'accessToken': access_token, 'channelNo': 1, 'deviceSerial': sn, 'fileId': file_id, 'projectId': project_id, 'recType': rec_type, 'startTime': start_time, 'endTime': end_time } return EvizVersionClient._do_request(url, data) @staticmethod def get_cloud_video_download_url(access_token, file_id, project_id): url = 'https://open.ys7.com/api/open/cloud/v1/file/download' data = { 'accessToken': access_token, 'fileId': file_id, 'projectId': project_id } return EvizVersionClient._do_request(url, data, 'GET') @staticmethod def delete_cloud_video_download_url(access_token, file_id, project_id): url = 'https://open.ys7.com/api/open/cloud/v1/file' data = { 'accessToken': access_token, 'fileId': file_id, 'projectId': project_id } return EvizVersionClient._do_request(url, data, method='DELETE') if __name__ == '__main__': ysy_client = EvizVersionClient() camera_sn = 'E57377023' token = ysy_client.get_access_token(camera_sn) print(ysy_client.get_live_address(token, camera_sn))