|
@@ -58,6 +58,8 @@ class deviceStatus(TemplateView):
|
|
|
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", '')
|
|
@@ -739,7 +741,148 @@ class deviceStatus(TemplateView):
|
|
|
'content-type': "application/x-www-form-urlencoded",
|
|
|
'cache-control': "no-cache"
|
|
|
}
|
|
|
- res = requests.post(auth_request_url, payload, headers)
|
|
|
- request_json = res.json()
|
|
|
- logger.info('--------请求更新token响应{}--------'.format(request_json))
|
|
|
- return request_json
|
|
|
+ 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)}'
|
|
|
+ })
|
|
|
+
|