# -*- coding: utf-8 -*- # !/usr/bin/env python import logging import uuid import simplejson as json from typing import TYPE_CHECKING from apilib.utils_json import JsonResponse from apps.web.agent.models import Agent from apps.web.api.exceptions import ApiParameterError, ApiAuthDeviceException, ApiNoDeviceException, ApiDeviceTypeError from apps.web.api.utils import api_call, api_exception_response, api_ok_response, api_error_response, \ api_ok_response_v2, deprecated_api_error_response from apps.web.constant import DeviceCmdCode, ErrorCode, MQTT_TIMEOUT from apps.web.core.db import paginate from apps.web.core.exceptions import ServiceException from apps.web.core.helpers import ActionDeviceBuilder from apps.web.core.networking import MessageSender from apps.web.dealer.models import Dealer from apps.web.dealer.utils import get_devices_by_dealer from apps.web.device.models import Device from apps.web.api.models import APIStartDeviceRecord from apps.web.utils import error_tolerate if TYPE_CHECKING: from django.core.handlers.wsgi import WSGIRequest from apps.web.device.models import DeviceDict logger = logging.getLogger(__name__) @api_call(logger = logger, nil = api_exception_response()) def deviceOnlineStatus(request, dealer): # type: (WSGIRequest, Dealer)->JsonResponse payload = json.loads(request.body) if request.body else {} if not payload: raise ApiParameterError(errmsg = u'传递为空或payload格式有误,请传JSON格式') if 'deviceCode' not in payload: raise ApiParameterError(errmsg = u'必须传递deviceCode参数') device = Device.get_dev_by_logicalCode(payload['deviceCode']) if not device.is_authorized_to_dealer(str(dealer.id)): raise ApiAuthDeviceException() result = MessageSender.send(device = device, cmd = DeviceCmdCode.GET_DEVINFO, payload = {'IMEI': device['devNo']}) if result['rst'] != 0: return api_ok_response(payload = {'online': 0, 'signal': 0}) else: return api_ok_response(payload = {'online': 1, 'signal': int(result.get('signal', 0))}) @api_call(logger = logger, nil = api_exception_response()) def apiStartDevice(request, dealer): # type: (WSGIRequest, Dealer)->JsonResponse payload = json.loads(request.body) if request.body else {} if not payload: raise ApiParameterError(errmsg = u'传递为空或payload格式有误,请传JSON格式') if 'deviceCode' not in payload: raise ApiParameterError(errmsg = u'必须传递deviceCode参数') device = Device.get_dev_by_logicalCode(payload['deviceCode']) # type: DeviceDict if not device: raise ApiNoDeviceException() if not device.is_authorized_to_dealer(str(dealer.id)): raise ApiAuthDeviceException() # 整理额外参数 attachParas = payload.get('attachParas', {}) if 'channel' in payload: channel = payload.get('channel', 'unknown') else: channel = attachParas.pop('channel', '') if 'extOrderNo' in payload: orderNo = payload.get('extOrderNo') else: orderNo = attachParas.pop('extOrderNo', str(uuid.uuid4())) attachParas['extOrderNo'] = orderNo if 'userId' in payload: user_id = payload.get('userId', '') else: user_id = attachParas.pop('userId', '') if 'createTime' in payload: create_time = payload.get('createTime') else: create_time = attachParas.pop('createTime', '') if 'coins' not in payload and 'package' not in payload: raise ApiParameterError(errmsg = u'coins和package参数不能同时为空') if 'coins' in payload: package = { 'coins': payload['coins'] } else: if isinstance(payload['package'], str): package = device['washConfig'].get(payload['package']) elif isinstance(payload['package'], dict): package = payload['package'] else: raise ApiParameterError(errmsg = u'package必须为字符串或者json字典') notify_url = payload.get('notify_url', '') record = APIStartDeviceRecord(orderNo = orderNo, deviceCode = payload['deviceCode'], userId = user_id, createTime = create_time, package = package, notifyUrl = notify_url, apiConf = getattr(dealer, 'api_conf'), channel = channel, devNo = device['devNo'], ownerId = device['ownerId'], attachParas = attachParas) record.save() err_code = ErrorCode.EXCEPTION err_msg = u'系统错误' try: if 'coins' in payload: # 兼容以前代码, 走的是同步接口 result = MessageSender.net_pay(device = device, coins = int(package['coins']), timeout = MQTT_TIMEOUT.START_DEVICE) else: smartBox = ActionDeviceBuilder.create_action_device(device) result = smartBox.start_from_api(record = record) if result and 'rst' in result: # 这个是同步接口 err_code = result.get('rst') err_msg = str(result.get('desc', '')) return api_ok_response(payload = {'status': err_code, 'desc': err_msg}) else: # 异步接口没有值返回 return api_ok_response(payload = {'status': 0, 'desc': u'成功下发启动设备命令'}) except ServiceException as e: logger.error('start device({}) failed error(code={},msg={})'.format(device['devNo'], e.result.get('result'), e.result.get('description'))) err_code = e.result.get('result') err_msg = str(e.result.get('description')) return api_ok_response(payload = {'status': err_code, 'desc': err_msg}) except Exception as e: logger.exception('start device({}) failed error={}'.format(device['devNo'], e)) err_code = ErrorCode.EXCEPTION err_msg = str(e) return api_error_response(errcode = ErrorCode.API_EXCEPTION, errmsg = u'系统错误') finally: try: record.errCode = err_code record.errMsg = err_msg record.save() except Exception as e: logger.exception(e) @api_call(logger = logger, nil = api_exception_response()) def apiGetDevicePackageList(request, dealer): # type: (WSGIRequest, Dealer)->JsonResponse payload = json.loads(request.body) if request.body else {} if not payload: raise ApiParameterError(errmsg = u'传递为空或payload格式有误,请传JSON格式') if 'deviceCode' not in payload: raise ApiParameterError(errmsg = u'必须传递deviceCode参数') device = Device.get_dev_by_logicalCode(payload['deviceCode']) if not device.is_authorized_to_dealer(str(dealer.id)): raise ApiAuthDeviceException() # 返回errcode, errmsg, packages. 其他为兼容 response_dict = { 'errcode': ErrorCode.SUCCESS, 'errmsg': 'SUCCESS', 'result': 1, 'payload': device['washConfig'], 'packages': device['washConfig'], 'description': '' } return JsonResponse(response_dict) @api_call(logger = logger, nil = api_exception_response()) def order(request, dealer): # type: (WSGIRequest, Dealer)->JsonResponse return api_ok_response(payload = {}, description = u'成功查询订单') @api_call(logger = logger, nil = api_exception_response()) def getDeviceListByDealer(request, dealer): # type:(WSGIRequest, Dealer)->JsonResponse pageIndex = 1 pageSize = 10 payload = json.loads(request.body) if request.body else {} if not payload: pageIndex = int(request.GET.get('pageIndex', 1)) pageSize = int(request.GET.get('pageSize', 10)) else: pageIndex = int(payload.get('pageIndex', 1)) pageSize = int(payload.get('pageSize', 10)) def public_only(device): return {k: v for k, v in device.iteritems() if k not in Device.protected_fields()} devices = [public_only(_) for _ in get_devices_by_dealer(dealer)] return api_ok_response(payload = {'devices': paginate(devices, pageIndex, pageSize)}) @api_call(logger = logger, nil = api_exception_response()) def apiGetChargerInfo(request, dealer): # type: (WSGIRequest)->JsonResponse payload = json.loads(request.body) if request.body else {} if not payload: raise ApiParameterError(errmsg = u'传递为空或payload格式有误,请传JSON格式') if 'deviceCode' not in payload: raise ApiParameterError(errmsg = u'必须传递deviceCode参数') device = Device.get_dev_by_logicalCode(payload['deviceCode']) if device is None: raise ApiNoDeviceException() if not device.is_authorized_to_dealer(str(dealer.id)): raise ApiAuthDeviceException() data = {} ctrInfo = Device.get_dev_control_cache(device['devNo']) if ctrInfo.has_key('elecValue'): data.update({'elec': ctrInfo['elecValue']}) else: try: box = ActionDeviceBuilder.create_action_device(device) valueDict = box.get_port_elec_from_dev() data.update({'elec': valueDict}) except Exception, e: data.update({'elec': {}}) if ctrInfo.has_key('temperatureValue'): data.update({'temperature': ctrInfo['temperatureValue']}) else: try: box = ActionDeviceBuilder.create_action_device(device) valueDict = box.get_port_temperature_from_dev() data.update({'temperature': valueDict}) except Exception, e: data.update({'temperature': {}}) if ctrInfo.has_key('isYangan'): data.update({'smokeDetected': ctrInfo['isYangan']}) else: data.update({'smokeDetected': False}) return api_ok_response_v2(**data) @api_call(logger = logger, nil = api_exception_response()) def apiGetDevStatus(request, dealer): # type: (WSGIRequest)->JsonResponse payload = json.loads(request.body) if request.body else {} if not payload: raise ApiParameterError(errmsg = u'传递为空或payload格式有误,请传JSON格式') if 'deviceCode' not in payload: raise ApiParameterError(errmsg = u'必须传递deviceCode参数') device = Device.get_dev_by_logicalCode(payload['deviceCode']) # type: DeviceDict if device is None: raise ApiNoDeviceException() if not device.is_authorized_to_dealer(str(dealer.id)): raise ApiAuthDeviceException() data = {'status': device.status} try: box = device.deviceAdapter portStatus = box.get_port_status_from_dev() if portStatus is not None: data.update({'portStatus': portStatus}) return api_ok_response(payload = data) except NotImplementedError: raise ApiDeviceTypeError(errmsg = u'未找到端口查询函数,请注意是否注册了正确的设备类型') @api_call(logger = logger, nil = api_exception_response()) def apiStopDevice(request, dealer): # type: (WSGIRequest)->JsonResponse payload = json.loads(request.body) if request.body else {} if not payload: raise ApiParameterError(errmsg = u'传递为空或payload格式有误,请传JSON格式') if 'deviceCode' not in payload: raise ApiParameterError(errmsg = u'必须传递deviceCode参数') device = Device.get_dev_by_logicalCode(payload['deviceCode']) # type: DeviceDict if device is None: raise ApiNoDeviceException() if not device.is_authorized_to_dealer(str(dealer.id)): raise ApiAuthDeviceException() box = ActionDeviceBuilder.create_action_device(device) oper_result = box.stop() Device.invalid_device_control_cache(device['devNo']) return api_ok_response(payload = {'status': oper_result['rst']}) @error_tolerate(logger = logger, nil = deprecated_api_error_response(description = u'系统错误')) def apiGetPackage(request): # type: (WSGIRequest)->JsonResponse sign = request.POST.get('sign', None) channel = request.POST.get('channel', None) logicalCode = request.POST.get('deviceCode', None) if None in [sign, channel, logicalCode]: return JsonResponse({'code': '00000001', 'msg': u'缺少必须的参数'}) # 检查签名是否存在 if not sign: return JsonResponse({'code': '00000002', 'msg': u'签名不能为空'}) try: agent = Agent.objects.get(agentSign = sign) except Exception, e: logger.exception('get agent error=%s' % e) return JsonResponse({'code': '00000003', 'msg': u'错误的签名'}) dev = Device.get_dev_by_logicalCode(logicalCode) dealer = Dealer.objects.get(id = dev['ownerId']) if str(agent.id) != dealer['agentId']: return JsonResponse({'code': '00000005', 'msg': u'设备编号和商户ID不匹配'}) if dev is None: return JsonResponse({'code': '00000004', 'msg': u'未找到对应的设备'}) return JsonResponse({'code': '00000000', 'msg': '', 'data': dev['washConfig']})