#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ """ import json import time import datetime from datetime import timezone import requests import uuid import logging from django.views.generic import TemplateView from django.http import JsonResponse import http.client from urllib.parse import urlencode from object.ResObject import ResObject import subprocess # from gevent.pool import Pool from model.models import UserModel, UidRtspModel, SwitchModel from object.ResponseObject import ResponseObject from object.tkObject import tkObject from service.CommonService import CommonService from model.models import AlexaAuthModel from object.RedisObject import RedisObject from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from azoauth.config import * class deviceStatus(TemplateView): @method_decorator(csrf_exempt) def dispatch(self, *args, **kwargs): return super(deviceStatus, self).dispatch(*args, **kwargs) def get(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.GET, request, operation) def post(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.POST, request, operation) def validation(self, request_dict, request, operation): response = ResponseObject() if operation == 'saveAccessToken': return self.saveAccessToken(request_dict) elif operation == 'addOrUpdateV2': return self.addOrUpdateV2(request_dict) elif operation == 'addOrUpdateSwitch': return self.addOrUpdateSwitch(request_dict) elif operation == 'delete': return self.delete(request_dict) elif operation == 'stateReport': return self.StateReport(request_dict) elif operation == 'changeReportSwitch': return self.changeReportSwitch(request_dict) elif operation == 'deleteSwitch': return self.deleteSwitch(request_dict) elif operation == 'objectDetectionSensorChangeReport': return self.objectDetectionSensorChangeReport(request_dict) def saveAccessToken(self, request_dict): token = request_dict.get("token", '') alexa_region = request_dict.get("region", '') skill_name = request_dict.get("skill_name", '') access_token = request_dict.get("access_token", '') refresh_token = request_dict.get("refresh_token", '') logger = logging.getLogger('django') logger.info('认证登录,参数: {}'.format(request_dict)) try: alex_auth_qs = AlexaAuthModel.objects.filter(token=token, skill_name=skill_name) nowTime = int(time.time()) if not alex_auth_qs.exists(): AlexaAuthModel.objects.create( token=token, addTime=nowTime, updTime=nowTime, alexa_region=alexa_region, access_token=access_token, refresh_token=refresh_token, expiresTime=nowTime + 3200, skill_name=skill_name ) else: alex_auth_qs.update( token=token, updTime=nowTime, alexa_region=alexa_region, access_token=access_token, refresh_token=refresh_token, expiresTime=nowTime + 3200, ) # 更新业务服务器的token user_qs = UserModel.objects.filter(access_token=token).values('userID') if user_qs.exists(): user_id = user_qs[0]['userID'] if alexa_region == 'US': base_url = SERVER_PREFIX # SERVER_PREFIX, SERVER_PREFIX_TEST else: base_url = SERVER_PREFIX_EU url = '{}/alexaApi/appToApp/updateToken'.format(base_url) data = { 'user_id': user_id, 'access_token': access_token, 'refresh_token': refresh_token } r = requests.post(url=url, data=data, timeout=10) logger.info('请求更新token状态码: {}'.format(r.status_code)) return JsonResponse({'code': 200, 'msg': 'success'}) except Exception as e: logger.info('授权登录接口异常: error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e))) return JsonResponse({'code': 500, 'msg': 'error'}) # 向alexa事件网关发送更新设备操作V2接口 def addOrUpdateV2(self, request_dict): logger = logging.getLogger('django') logger.info('--------添加/更新设备信息V2--------') region = request_dict.get("region", 'EN') data_list = request_dict.get("data_list", '') logger.info('data_list: {}'.format(data_list)) data_list = json.loads(data_list) # 多通道设备才传 channel 键值对 if not data_list: return JsonResponse({'code': 101, 'msg': 'Parameter error'}) try: UID = data_list[0]['UID'] userID = data_list[0]['userID'] password = data_list[0]['password'] password = CommonService().decode_pwd(password) # 获取alexa授权信息 alexAuth = AlexaAuthModel.objects.filter(userID=userID). \ values('expiresTime', 'access_token', 'refresh_token', 'alexa_region', 'skill_name') if not alexAuth.exists(): logger.info('UID为 {} 的用户不存在'.format(UID)) return JsonResponse({'code': 102, 'msg': 'not found user'}) skill_name = alexAuth[0]['skill_name'] expiresTime = alexAuth[0]['expiresTime'] access_token = alexAuth[0]['access_token'] refresh_token = alexAuth[0]['refresh_token'] alexa_region = alexAuth[0]['alexa_region'] if alexa_region not in ALEXA_EVENT_API.keys(): logger.info('alexa区域信息错误,alexa_region: {}'.format(alexa_region)) return JsonResponse({'code': 102, 'msg': 'alexa_region error'}) # 更新alexa token now_time = int(time.time()) if now_time > expiresTime: logger.info(refresh_token) res = self.getRefreshToken(refresh_token, skill_name) logger.info(res) if 'error' not in res: alexAuth.update( updTime=now_time, expiresTime=now_time + 3000, access_token=res['access_token'], refresh_token=res['refresh_token'], ) access_token = res['access_token'] else: logger.info('get refresh_token fail') return JsonResponse({'code': 102, 'msg': 'get refresh_token fail'}) # 添加rtsp记录 channel = len(data_list) # 列表的元素个数即通道数量 rtsp_url = tkObject(rank=1).encrypt(data=UID) uid_rtsp_qs = UidRtspModel.objects.filter(uid__contains=UID) if not uid_rtsp_qs.exists(): # 创建UidRtsp数据 if channel == 1: # 单通道设备 UidRtspModel.objects.create(user_id=userID, uid=UID, nick=data_list[0]['uid_nick'], region=region, rtsp_url=rtsp_url, password=password, addTime=now_time, updTime=now_time) else: # 多通道设备 bulk = [] for data in data_list: uid = UID + '_' + str(data['channel']) # 多通道设备: uid_通道号 uidRtsp = UidRtspModel(user_id=userID, uid=uid, nick=data['uid_nick'], region=region, rtsp_url=rtsp_url, password=password, addTime=now_time, updTime=now_time) bulk.append(uidRtsp) UidRtspModel.objects.bulk_create(bulk) else: # 更新UidRtsp数据 count = len(uid_rtsp_qs) if count != channel: return JsonResponse({'code': 103, 'msg': '通道数不匹配'}) if count == 1: uid_rtsp_qs.update(nick=data_list[0]['uid_nick'], region=region, password=password, user_id=userID) else: # 多通道设备 for data in data_list: uid = UID + '_' + str(data['channel']) # 多通道设备: uid_通道号 UidRtspModel.objects.filter(uid=uid).update(nick=data['uid_nick'], region=region, user_id=userID, password=password) api_uri = ALEXA_EVENT_API[alexa_region] messageId = str(uuid.uuid4()).strip() bearer_access_token = "Bearer {access_token}".format(access_token=access_token) headers = {"content-type": "application/json", "Authorization": bearer_access_token} endpoints = self.append_endpoint(data_list, channel, skill_name) payload_json = { "event": { "header": { "namespace": "Alexa.Discovery", "name": "AddOrUpdateReport", "payloadVersion": "3", "messageId": messageId, }, "payload": { "endpoints": endpoints, "scope": { "type": "BearerToken", "token": 'sdf', }, }, } } logger.info('--------Alexa AddOrUpdateReport请求: url:{},data:{}--------'.format(api_uri, payload_json)) response = requests.post(api_uri, json=payload_json, headers=headers) logger.info('--------Alexa AddOrUpdateReport响应: {}--------'.format(response)) return JsonResponse({'res': 'success'}) except Exception as e: logger.info('--------添加/更新设备信息V2异常--------:error_line:{}, error_msg:{}'. format(e.__traceback__.tb_lineno, repr(e))) # 向alexa事件网关发送更新设备操作接口 def addOrUpdateSwitch(self, request_dict): logger = logging.getLogger('django') logger.info('--------添加/更新插座信息--------') region = request_dict.get("region", 'US') nick_name = request_dict.get("nick_name", '') serial_number = request_dict.get("serial_number", '') user_id = request_dict.get("user_id", '') logger.info('region: {}, nick_name: {}, serial_number: {}, user_id: {}'.format(region, nick_name, serial_number, user_id)) skill_name = 'loocam' if not all([nick_name, serial_number, user_id]): return JsonResponse({'code': 101, 'msg': 'Parameter error'}) try: # 获取alexa授权信息 alexAuth = AlexaAuthModel.objects.filter(userID=user_id, skill_name=skill_name). \ values('expiresTime', 'access_token', 'refresh_token', 'alexa_region') if not alexAuth.exists(): logger.info('serial_number {} 的用户不存在'.format(serial_number)) return JsonResponse({'code': 102, 'msg': 'not found user'}) expiresTime = alexAuth[0]['expiresTime'] access_token = alexAuth[0]['access_token'] refresh_token = alexAuth[0]['refresh_token'] alexa_region = alexAuth[0]['alexa_region'] if alexa_region not in ALEXA_EVENT_API.keys(): logger.info('alexa区域信息错误,alexa_region: {}'.format(alexa_region)) return JsonResponse({'code': 102, 'msg': 'alexa_region error'}) # 更新alexa token now_time = int(time.time()) if now_time > expiresTime: logger.info(refresh_token) res = self.getRefreshToken(refresh_token, skill_name) logger.info(res) if 'error' not in res: alexAuth.update( updTime=now_time, expiresTime=now_time + 3000, access_token=res['access_token'], refresh_token=res['refresh_token'], ) access_token = res['access_token'] else: logger.info('get refresh_token fail') return JsonResponse({'code': 102, 'msg': 'get refresh_token fail'}) # 添加rtsp记录 switch_qs = SwitchModel.objects.filter(serial_number=serial_number, userID=user_id) if switch_qs.exists(): switch_qs.update(nick=nick_name, updTime=now_time) else: data = { 'nick': nick_name, 'serial_number': serial_number, 'userID': user_id, 'region': region, 'addTime': now_time, 'updTime': now_time, } SwitchModel.objects.create(**data) api_uri = ALEXA_EVENT_API[alexa_region] messageId = str(uuid.uuid4()).strip() bearer_access_token = "Bearer {access_token}".format(access_token=access_token) headers = {"content-type": "application/json", "Authorization": bearer_access_token} endpoints = [{ "endpointId": serial_number, "manufacturerName": skill_name, "friendlyName": nick_name, "description": 'Plug connected via {}'.format(skill_name), "displayCategories": ['SMARTPLUG'], "cookie": {}, "capabilities": [ { "type": 'AlexaInterface', "interface": 'Alexa.PowerController', "version": '3', "properties": { "supported": [ { "name": "powerState" } ], "proactivelyReported": True, }, }, { "type": "AlexaInterface", "interface": "Alexa.EndpointHealth", "version": "3.1", "properties": { "supported": [{ "name": "connectivity" } ], "proactivelyReported": True, } }, { "type": 'AlexaInterface', "interface": 'Alexa', "version": '3' } ] }] payload_json = { "event": { "header": { "namespace": "Alexa.Discovery", "name": "AddOrUpdateReport", "payloadVersion": "3", "messageId": messageId, }, "payload": { "endpoints": endpoints, "scope": { "type": "BearerToken", "token": 'sdf', }, }, } } response = requests.post(api_uri, json=payload_json, headers=headers) logger.info('--------Alexa AddOrUpdateReport响应: {}--------'.format(response)) return JsonResponse({'res': 'success'}) except Exception as e: logger.info('--------添加/更新插座信息异常--------:error_line:{}, error_msg:{}'. format(e.__traceback__.tb_lineno, repr(e))) return JsonResponse({'res': 'error'}) # 向alexa事件网关发送插座状态操作接口 def StateReport(self, request_dict): logger = logging.getLogger('django') logger.info('--------alexa智能插座状态上报--------') serial_number = request_dict.get("serial_number", None) access_token = request_dict.get("access_token", None) logger.info('请求参数: {}'.format(request_dict)) response = ResObject() if not all([serial_number, access_token]): return response.json(10, res={'msg': 'Parameter error'}) user_qs = UserModel.objects.filter(access_token=access_token) if not user_qs.exists(): return response.json(10, res={'msg': '用户数据不存在'}) user_id = user_qs.first().userID switch_qs = SwitchModel.objects.filter(serial_number=serial_number).values('region') if not switch_qs.exists(): return response.json(10, res={'msg': '设备不存在'}) try: region = switch_qs[0]['region'] if region == 'EU': domain_name = SERVER_PREFIX_EU elif region == 'CN': domain_name = SERVER_PREFIX_TEST else: domain_name = SERVER_PREFIX auth_request_url = '{}/api/loocam/open/socket/getSocketState'.format(domain_name) requests_data = {'serial_number': serial_number, 'alexa_user_id': user_id} res = requests.post(url=auth_request_url, data=requests_data) res_json = res.json() logger.info('{}正式服务器响应: {}'.format(region, res_json)) if res_json['result_code'] != 0: return response.json(10, res={'msg': 'error'}) power_state = res_json['result']['power_state'] if power_state: res = {'state': 'ON'} else: res = {'state': 'OFF'} return response.json(0, res=res) except Exception as e: logger.info('--------上传插座状态异常--------:error_line:{}, error_msg:{}'. format(e.__traceback__.tb_lineno, repr(e))) return response.json(10, res={'msg': '上报状态异常'}) def append_endpoint(self, data_list, channel, skill_name): # 组织 endpoints 数据 endpoints = [] for data in data_list: endpointId = data['UID'] if channel == 1 else data['UID'] + '_' + str(data['channel']) endpoint = { "endpointId": endpointId, "manufacturerName": skill_name, "modelName": "P1425-LE", "friendlyName": data['uid_nick'], "description": "Camera connected via {}".format(skill_name), "displayCategories": ["CAMERA"], "capabilities": [ { "type": "AlexaInterface", "interface": "Alexa.CameraStreamController", "version": "3", "cameraStreamConfigurations": [ { "protocols": ["RTSP"], "resolutions": [{"width": 1280, "height": 720}], "authorizationTypes": ["NONE"], "videoCodecs": ["H264"], "audioCodecs": ["ACC"], } ], } ], } endpoints.append(endpoint) return endpoints # 向alexa事件网关发送删除设备操作 def delete(self, request_dict): UID = request_dict.get("UID", '') userID = request_dict.get("userID", '') logger = logging.getLogger('django') logger.info('--------删除设备--------') logger.info('userID: {}, UID: {}'.format(userID, UID)) if not all([UID, userID]): return JsonResponse({'code': 111, 'msg': 'fail'}) try: alexAuth = AlexaAuthModel.objects.filter(userID=userID). \ values('expiresTime', 'refresh_token', 'access_token', 'alexa_region', 'skill_name') if not alexAuth.exists(): logger.info('UID为 {} 的用户不存在'.format(UID)) return JsonResponse({'code': 102, 'msg': 'not found user'}) skill_name = alexAuth[0]['skill_name'] expiresTime = alexAuth[0]['expiresTime'] refresh_token = alexAuth[0]['refresh_token'] access_token = alexAuth[0]['access_token'] alexa_region = alexAuth[0]['alexa_region'] if alexa_region not in ALEXA_EVENT_API.keys(): logger.info('alexa区域信息错误,alexa_region: {}'.format(alexa_region)) return JsonResponse({'code': 102, 'msg': 'alexa_region error'}) now_time = int(time.time()) if now_time > expiresTime: res = self.getRefreshToken(refresh_token, skill_name) if 'error' not in res: alexAuth.update( access_token=res['access_token'], refresh_token=res['refresh_token'], expiresTime=now_time + 3000, updTime=now_time, ) access_token = res['access_token'] else: return JsonResponse({'code': 102, 'msg': 'get refresh_token fail'}) uidRtsp_qs = UidRtspModel.objects.filter(uid__contains=UID).values('uid') if not uidRtsp_qs.exists(): return JsonResponse({'code': 103, 'msg': '不存在uidRtsp数据'}) endpoints = [] for uidRtsp in uidRtsp_qs: endpointId = {"endpointId": uidRtsp['uid']} endpoints.append(endpointId) headers = { "Authorization": "Bearer " + access_token, "Content-Type": "application/json;charset=UTF-8", "Cache-Control": "no-cache" } payload = { "event": { "header": { "namespace": "Alexa.Discovery", "name": "DeleteReport", "messageId": str(uuid.uuid4()), "payloadVersion": "3" }, "payload": { "endpoints": endpoints, "scope": { "type": "BearerToken", "token": access_token } } } } api_uri = ALEXA_EVENT_API[alexa_region] response = requests.post(api_uri, json=payload, headers=headers) logger.info('--------Alexa DeleteReport响应: {}--------'.format(response)) return JsonResponse({'res': 'success'}) except Exception as e: logger.info('--------删除设备异常--------:error_line:{}, error_msg:{}'. format(e.__traceback__.tb_lineno, repr(e))) def changeReportSwitch(self, request_dict): # 通过app或设备打开/关闭插座电源时主动报告状态 serial_number = request_dict.get('serial_number') value = request_dict.get('value') online = request_dict.get('online') cause_type = request_dict.get('cause_type', 'PHYSICAL_INTERACTION') logger = logging.getLogger('django') logger.info('插座ChangeReport参数,' 'serial_number:{}, value:{}, cause_type:{}, online:{}'. format(serial_number, value, cause_type, online)) if not all([serial_number, value, cause_type]): return JsonResponse({'code': 111, 'msg': '参数异常'}) skill_name = 'loocam' try: switch_qs = SwitchModel.objects.filter(serial_number=serial_number) if not switch_qs.exists(): return JsonResponse({'code': 103, 'msg': '不存在socket数据'}) userID = switch_qs[0].userID alexAuth = AlexaAuthModel.objects.filter(userID=userID, skill_name=skill_name). \ values('expiresTime', 'refresh_token', 'access_token', 'alexa_region') if not alexAuth.exists(): logger.info('序列号为 {} 的用户不存在'.format(serial_number)) return JsonResponse({'code': 102, 'msg': 'not found user'}) expiresTime = alexAuth[0]['expiresTime'] refresh_token = alexAuth[0]['refresh_token'] access_token = alexAuth[0]['access_token'] alexa_region = alexAuth[0]['alexa_region'] if alexa_region not in ALEXA_EVENT_API.keys(): logger.info('alexa区域信息错误,alexa_region: {}'.format(alexa_region)) return JsonResponse({'code': 102, 'msg': 'alexa_region error'}) now_time = int(time.time()) if now_time > expiresTime: res = self.getRefreshToken(refresh_token, skill_name) if 'error' not in res: alexAuth.update( access_token=res['access_token'], refresh_token=res['refresh_token'], expiresTime=now_time + 3000, updTime=now_time, ) access_token = res['access_token'] else: return JsonResponse({'code': 102, 'msg': 'get refresh_token fail'}) headers = { "Authorization": "Bearer " + access_token, "Content-Type": "application/json;charset=UTF-8", "Cache-Control": "no-cache" } # 转换时间格式 iso_string = datetime.datetime.now(timezone.utc).isoformat() time_of_sample = iso_string.split('.')[0] + 'Z' properties = [ { "namespace": "Alexa.PowerController", "name": "powerState", "value": value, "timeOfSample": time_of_sample, "uncertaintyInMilliseconds": 0 } ] if online is not None: endpoint_health_value = 'OK' if online == '1' else 'UNREACHABLE' properties.append( { "namespace": "Alexa.EndpointHealth", "name": "connectivity", "value": { "value": endpoint_health_value }, "timeOfSample": time_of_sample, "uncertaintyInMilliseconds": 0 } ) logger.info('插座ChangeReport properties:{}'.format(properties)) payload = { "event": { "header": { "namespace": "Alexa", "name": "ChangeReport", "messageId": str(uuid.uuid4()), "payloadVersion": "3" }, "endpoint": { "scope": { "type": "BearerToken", "token": access_token }, "endpointId": serial_number }, "payload": { "change": { "cause": { "type": cause_type }, "properties": properties } } } } api_uri = ALEXA_EVENT_API[alexa_region] response = requests.post(api_uri, json=payload, headers=headers) logger.info('--------Alexa ChangeReport响应: {}--------'.format(response)) return JsonResponse({'res': 'success'}) except Exception as e: logger.info('--------插座ChangeReport异常--------:error_line:{}, error_msg:{}'. format(e.__traceback__.tb_lineno, repr(e))) return JsonResponse({'res': 'error'}) # 向alexa事件网关发送删除插座操作 def deleteSwitch(self, request_dict): serial_number = request_dict.get("serial_number", '') logger = logging.getLogger('django') logger.info('--------删除插座--------') logger.info('serial_number: {}'.format(serial_number)) if not all([serial_number]): return JsonResponse({'code': 111, 'msg': '参数异常'}) skill_name = 'loocam' try: switch_qs = SwitchModel.objects.filter(serial_number=serial_number) if not switch_qs.exists(): return JsonResponse({'code': 103, 'msg': '不存在socket数据'}) userID = switch_qs[0].userID alexAuth = AlexaAuthModel.objects.filter(userID=userID, skill_name=skill_name). \ values('expiresTime', 'refresh_token', 'access_token', 'alexa_region') if not alexAuth.exists(): logger.info('序列号为 {} 的用户不存在'.format(serial_number)) return JsonResponse({'code': 102, 'msg': 'not found user'}) expiresTime = alexAuth[0]['expiresTime'] refresh_token = alexAuth[0]['refresh_token'] access_token = alexAuth[0]['access_token'] alexa_region = alexAuth[0]['alexa_region'] if alexa_region not in ALEXA_EVENT_API.keys(): logger.info('alexa区域信息错误,alexa_region: {}'.format(alexa_region)) return JsonResponse({'code': 102, 'msg': 'alexa_region error'}) now_time = int(time.time()) if now_time > expiresTime: res = self.getRefreshToken(refresh_token, skill_name) if 'error' not in res: alexAuth.update( access_token=res['access_token'], refresh_token=res['refresh_token'], expiresTime=now_time + 3000, updTime=now_time, ) access_token = res['access_token'] else: return JsonResponse({'code': 102, 'msg': 'get refresh_token fail'}) endpoints = [] for switch in switch_qs: endpointId = {"endpointId": switch.serial_number} endpoints.append(endpointId) switch_qs.delete() headers = { "Authorization": "Bearer " + access_token, "Content-Type": "application/json;charset=UTF-8", "Cache-Control": "no-cache" } payload = { "event": { "header": { "namespace": "Alexa.Discovery", "name": "DeleteReport", "messageId": str(uuid.uuid4()), "payloadVersion": "3" }, "payload": { "endpoints": endpoints, "scope": { "type": "BearerToken", "token": access_token } } } } api_uri = ALEXA_EVENT_API[alexa_region] response = requests.post(api_uri, json=payload, headers=headers) logger.info('--------Alexa DeleteReport响应: {}--------'.format(response)) return JsonResponse({'res': 'success'}) except Exception as e: logger.info('--------删除设备异常--------:error_line:{}, error_msg:{}'. format(e.__traceback__.tb_lineno, repr(e))) return JsonResponse({'res': 'error'}) def getRefreshToken(self, refresh_token, skill_name): # 请求更新token logger = logging.getLogger('django') logger.info('--------{}请求更新token--------'.format(skill_name)) if skill_name not in CLIENT_CONFIG.keys(): # skill_name = 'Anlapus' # 应对UL测试,后期改回 logger.info('--------技能名称错误,skill_name: {}--------'.format(skill_name)) return JsonResponse({'code': 111, 'msg': 'skill_name error'}) payload = { 'grant_type': 'refresh_token', 'refresh_token': refresh_token, 'client_id': CLIENT_CONFIG[skill_name]['client_id'], 'client_secret': CLIENT_CONFIG[skill_name]['client_secret'], } auth_request_url = 'https://api.amazon.com/auth/o2/token' headers = { 'content-type': "application/x-www-form-urlencoded", 'cache-control': "no-cache" } try: res = requests.post(auth_request_url, data=payload, headers=headers, timeout=5) res.raise_for_status() request_json = res.json() logger.info('--------请求更新token响应{}--------'.format(request_json)) return request_json except requests.exceptions.RequestException as e: logger.error(f'刷新token请求异常: {e}') return {'error': str(e)} def objectDetectionSensorChangeReport(self, request_dict): logger = logging.getLogger('django') logger.info('--------发送ChangeReport事件--------') # 获取请求参数 uid = request_dict.get('uid') # 客户端传入的唯一设备标识 object_classes = request_dict.get('object_classes', []) cause_type = request_dict.get('cause_type', 'PHYSICAL_INTERACTION') skill_name = request_dict.get('skill_name', 'Anlapus') # 默认技能名称 logger.info(f'uid: {uid}, object_classes: {object_classes}') if not uid: return JsonResponse({'code': 111, 'msg': '缺少必要参数: uid'}) try: # 1. 通过UID查询设备信息 device = UidRtspModel.objects.filter(uid=uid).first() if not device: logger.error(f'未找到设备信息: uid={uid}') return JsonResponse({'code': 404, 'msg': '设备未注册'}) user_id = device.user_id endpoint_id = device.uid # 使用设备UID作为endpoint_id # 2. 通过用户ID获取Alexa授权信息 alexa_auth = AlexaAuthModel.objects.filter( userID=user_id, skill_name=skill_name ).first() if not alexa_auth: logger.error(f'未找到Alexa授权信息: user_id={user_id}, skill={skill_name}') return JsonResponse({'code': 403, 'msg': '未授权Alexa服务'}) # 3. 检查并刷新access token now_time = int(time.time()) if now_time > alexa_auth.expiresTime: res = self.getRefreshToken(alexa_auth.refresh_token, skill_name) if 'error' in res: logger.error('刷新Token失败') return JsonResponse({'code': 401, 'msg': 'Token刷新失败'}) # 更新数据库中的token alexa_auth.access_token = res['access_token'] alexa_auth.refresh_token = res['refresh_token'] alexa_auth.expiresTime = now_time + 3000 alexa_auth.updTime = now_time alexa_auth.save() current_access_token = res['access_token'] else: current_access_token = alexa_auth.access_token # 4. 构造时间戳 time_of_sample = datetime.datetime.now(timezone.utc).isoformat() time_of_sample = time_of_sample.split('.')[0] + 'Z' # 5. 构造ChangeReport事件 properties = [] if object_classes: properties.append({ "namespace": "Alexa.SmartVision.ObjectDetectionSensor", "name": "objectDetectionClasses", "value": object_classes, "timeOfSample": time_of_sample, "uncertaintyInMilliseconds": 0 }) payload = { "event": { "header": { "namespace": "Alexa", "name": "ChangeReport", "messageId": str(uuid.uuid4()), "payloadVersion": "3" }, "endpoint": { "scope": { "type": "BearerToken", "token": current_access_token }, "endpointId": endpoint_id }, "payload": { "change": { "cause": { "type": cause_type }, "properties": properties } } }, "context": { "properties": [{ "namespace": "Alexa.EndpointHealth", "name": "connectivity", "value": {"value": "OK"}, "timeOfSample": time_of_sample, "uncertaintyInMilliseconds": 60000 }] } } # 6. 发送到Alexa事件网关 api_uri = ALEXA_EVENT_API.get(alexa_auth.alexa_region) if not api_uri: logger.error(f'无效的区域配置: {alexa_auth.alexa_region}') return JsonResponse({'code': 400, 'msg': '区域配置错误'}) headers = { "Authorization": f"Bearer {current_access_token}", "Content-Type": "application/json" } response = requests.post(api_uri, json=payload, headers=headers, timeout=10) logger.info(f'Alexa响应: {response.status_code}, {response.text}') if response.status_code == 202: return JsonResponse({'code': 200, 'msg': '事件发送成功'}) else: logger.error(f'Alexa返回错误: {response.status_code}, {response.text}') return JsonResponse({ 'code': 500, 'msg': 'Alexa服务错误', 'alexa_status': response.status_code, 'alexa_response': response.text }) except Exception as e: logger.exception(f'处理ChangeReport异常: {str(e)}') return JsonResponse({ 'code': 500, 'msg': f'服务器错误: {str(e)}' })