IotCoreController.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import logging
  4. import os
  5. import hashlib
  6. import json
  7. import time
  8. import uuid
  9. import boto3
  10. import requests
  11. from django.views import View
  12. from Ansjer.config import BASE_DIR, AWS_IOT_GETS3_PULL_CHINA_ID, AWS_IOT_GETS3_PULL_CHINA_SECRET, \
  13. AWS_IOT_GETS3_PULL_FOREIGN_ID, AWS_IOT_GETS3_PULL_FOREIGN_SECRET, AWS_ARN, AWS_IOT_SES_ACCESS_CHINA_REGION, \
  14. AWS_IOT_SES_ACCESS_FOREIGN_REGION_ASIA, AWS_IOT_SES_ACCESS_FOREIGN_REGION_EUROPE, \
  15. AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA, SERVER_TYPE
  16. from base64 import b64encode, encodebytes
  17. from Controller.DeviceConfirmRegion import Device_Region
  18. from Model.models import Device_User, Device_Info, iotdeviceInfoModel, UIDCompanySerialModel, \
  19. SerialNumberModel
  20. from Object.IOTCore.IotObject import IOTClient
  21. from Object.ResponseObject import ResponseObject
  22. from Object.TokenObject import TokenObject
  23. from Service.CommonService import CommonService
  24. import OpenSSL.crypto as ct
  25. class IotCoreView(View):
  26. def get(self, request, *args, **kwargs):
  27. request.encoding = 'utf-8'
  28. request_dict = request.GET
  29. operation = kwargs.get('operation', None)
  30. return self.validate(operation, request_dict, request)
  31. def post(self, request, *args, **kwargs):
  32. request.encoding = 'utf-8'
  33. request_dict = request.POST
  34. operation = kwargs.get('operation', None)
  35. return self.validate(operation, request_dict, request)
  36. def validate(self, operation, request_dict, request):
  37. response = ResponseObject()
  38. lang = request_dict.get('lang', 'en')
  39. response.lang = lang
  40. if operation == 'createKeysAndCertificate':
  41. return self.create_keys_and_certificate(request_dict, response, request)
  42. elif operation == 'requestPublishMessage':
  43. return self.request_publish_message(request_dict, response, request)
  44. elif operation == 'getS3PullKey':
  45. return self.get_s3_pull_key(request_dict, response, request)
  46. elif operation == 'thingRegroup':
  47. return self.thing_regroup(request_dict, response, request)
  48. elif operation == 'pcGetIotInfo':
  49. return self.pcGetIotInfo(request_dict, response)
  50. else:
  51. token = TokenObject(request_dict.get('token', None))
  52. if token.code != 0:
  53. return response.json(token.code)
  54. response.lang = token.lang
  55. if operation == 'clearIotCerm':
  56. return self.clear_Iot_Cerm(request_dict, response)
  57. elif operation == 'getIotInfo':
  58. return self.getIotInfo(request_dict, response)
  59. else:
  60. return response.json(404)
  61. # CVM注册 :正使用
  62. def create_keys_and_certificate(self, request_dict, response, request):
  63. uid = request_dict.get('uid', '')
  64. token = request_dict.get('token', None)
  65. uid_code = request_dict.get('uid_code', None)
  66. language = request_dict.get('language', None)
  67. time_stamp = request_dict.get('time_stamp', None)
  68. device_version = request_dict.get('device_version', None).replace('.', '_') # 物品组命名不能包含'.'
  69. if not all([token, time_stamp, device_version, language]):
  70. return response.json(444, {'param': 'token, uid_code, time_stamp, device_version, language'})
  71. # 时间戳token校验
  72. if not CommonService.check_time_stamp_token(token, time_stamp):
  73. return response.json(13)
  74. if not uid:
  75. # 使用序列号
  76. serial_number = request_dict.get('serial_number', None)
  77. serial_number_code = request_dict.get('serial_number_code', None)
  78. if not all([serial_number, serial_number_code]):
  79. return response.json(444, {'param': 'serial_number, serial_number_code'})
  80. # 序列号编码解码校验
  81. serial_number_code = CommonService.decode_data(serial_number_code)
  82. if serial_number != serial_number_code:
  83. return response.json(404)
  84. serial = serial_number[0:6]
  85. try:
  86. SerialNumberModel.objects.get(serial_number=serial)
  87. except:
  88. return response.json(444)
  89. ThingNameSuffix = serial_number # 物品名后缀
  90. iotdeviceInfo_qs = iotdeviceInfoModel.objects.filter(serial_number=serial)
  91. else:
  92. # 使用uid
  93. # uid编码解码校验
  94. uid_code = CommonService.decode_data(uid_code)
  95. if uid != uid_code:
  96. return response.json(404)
  97. serial = '' # iot_deviceInfo表写入serial_number为''
  98. ThingNameSuffix = uid # 物品名后缀
  99. iotdeviceInfo_qs = iotdeviceInfoModel.objects.filter(uid=uid)
  100. # 判断设备是否已注册证书
  101. if not iotdeviceInfo_qs.exists():
  102. thingGroup = device_version + '_' + language
  103. # 设备模拟国外环境测试
  104. # if SERVER_TYPE == 'Ansjer.us_config.formal_settings': # 国外正式配置使用固定ip进行测试
  105. # ip = '67.220.90.13'
  106. # else:
  107. # ip = CommonService.get_ip_address(request)
  108. ip = CommonService.get_ip_address(request)
  109. region_id = Device_Region().get_device_region(ip)
  110. logger = logging.getLogger('info')
  111. logger.info('---设备注册到IoT Core接口--- ip:{},region_id:{}'.format(ip, region_id))
  112. iotClient = IOTClient(region_id)
  113. res = iotClient.register_to_iot_core(ThingNameSuffix, thingGroup, response)
  114. token_iot_number = hashlib.md5((str(uuid.uuid1()) + str(int(time.time()))).encode('utf-8')).hexdigest()
  115. iotdeviceInfoModel.objects.create(uid=uid,
  116. serial_number=serial,
  117. endpoint=res[0]['endpoint'],
  118. certificate_id=res[0]['certificateId'],
  119. certificate_pem=res[0]['certificatePem'],
  120. public_key=res[0]['publicKey'],
  121. private_key=res[0]['privateKey'],
  122. thing_name=res[1]['ThingName'],
  123. thing_groups=res[1]['thingGroupName'],
  124. token_iot_number=token_iot_number
  125. )
  126. res = {
  127. 'certificateId': res[0]['certificateId'],
  128. 'certificatePem': res[0]['certificatePem'],
  129. 'publicKey': res[0]['publicKey'],
  130. 'privateKey': res[0]['privateKey'],
  131. 'endpoint': res[0]['endpoint']
  132. }
  133. return response.json(0, {'res': res})
  134. else:
  135. iot = iotdeviceInfo_qs[0]
  136. res = {
  137. 'certificateId': iot.certificate_id,
  138. 'certificatePem': iot.certificate_pem,
  139. 'publicKey': iot.public_key,
  140. 'privateKey': iot.private_key,
  141. 'endpoint': iot.endpoint
  142. }
  143. # print('此设备已注册证书')
  144. return response.json(0, {'res': res})
  145. def thing_regroup(self, request_dict, response, request):
  146. # 物品重新分组
  147. uid = request_dict.get('uid', '')
  148. token = request_dict.get('token', None)
  149. language = request_dict.get('language', None)
  150. time_stamp = request_dict.get('time_stamp', None)
  151. device_version = request_dict.get('device_version', None)
  152. if not all([token, language, time_stamp, device_version]):
  153. return response.json(444, {'param: token, language, time_stamp, device_version'})
  154. # 时间戳token校验
  155. if not CommonService.check_time_stamp_token(token, time_stamp):
  156. return response.json(13)
  157. ip = CommonService.get_ip_address(request)
  158. region_id = Device_Region().get_device_region(ip)
  159. iotClient = IOTClient(region_id)
  160. ThingNameSuffix = uid
  161. if not uid:
  162. # 使用序列号
  163. serial_number = request_dict.get('serial_number', None)
  164. if not serial_number:
  165. return response.json(444)
  166. ThingNameSuffix = serial_number
  167. thingName = 'Ansjer_Device_' + ThingNameSuffix
  168. new_thingGroupName = (device_version + '_' + language).replace('.', '_') # 物品组命名不能包含'.'
  169. try:
  170. # 获取旧物品组
  171. list_groups_res = iotClient.client.list_thing_groups_for_thing(thingName=thingName, maxResults=1)
  172. old_thingGroupName = list_groups_res['thingGroups'][0]['groupName']
  173. # 没有新物品组则创建
  174. list_thing_groups_res = iotClient.client.list_thing_groups(namePrefixFilter=new_thingGroupName
  175. , maxResults=1, recursive=False)
  176. if not list_thing_groups_res['thingGroups']:
  177. attributes = {
  178. "update_time": "0"
  179. }
  180. thingGroupProperties = {
  181. "thingGroupDescription": "OTA",
  182. "attributePayload": {
  183. "attributes": attributes,
  184. "merge": False # 更新时覆盖掉而不是合并
  185. }
  186. }
  187. iotClient.client.create_thing_group(thingGroupName=new_thingGroupName
  188. , thingGroupProperties=thingGroupProperties)
  189. iotClient.client.update_thing_groups_for_thing(thingName=thingName
  190. , thingGroupsToAdd=[new_thingGroupName]
  191. , thingGroupsToRemove=[old_thingGroupName])
  192. # 更新设备版本信息
  193. if uid:
  194. Device_Info.objects.filter(UID=uid).update(version=device_version)
  195. else:
  196. Device_Info.objects.filter(serial_number=serial_number).update(version=device_version)
  197. return response.json(0)
  198. except Exception as e:
  199. print(e)
  200. return response.json(500, repr(e))
  201. def clear_Iot_Cerm(self, userID, request_dict, response):
  202. serial_number = request_dict.get('serial_number', None)
  203. if serial_number:
  204. iot = iotdeviceInfoModel.objects.filter(thing_name="Ansjer_Device_" + serial_number)
  205. if iot.exists():
  206. iot.delete()
  207. return response.json(0)
  208. else:
  209. return response.json(444)
  210. def request_publish_message(self, request_dict, response, request):
  211. # Alexa请求IoT Core下发MQTT消息通知设备开始或停止推流,或唤醒设备
  212. UID = request_dict.get('UID', None)
  213. MSG = request_dict.get('MSG', None)
  214. if not all([UID, MSG]):
  215. return response.json(444)
  216. try:
  217. # 获取设备的物品名后缀
  218. device_info_qs = Device_Info.objects.filter(UID=UID).values('UID', 'serial_number')
  219. if not device_info_qs.exists():
  220. return response.json(10043)
  221. uid = device_info_qs[0]['UID']
  222. serial_number = device_info_qs[0]['serial_number']
  223. # 如果device_info表的serial_number不为空,物品名为'Ansjer_Device_序列号'
  224. thing_name_suffix = serial_number if serial_number != '' else uid
  225. # 获取数据组织将要请求的url
  226. iot = iotdeviceInfoModel.objects.filter(thing_name__contains=thing_name_suffix).values('thing_name',
  227. 'endpoint',
  228. 'token_iot_number')
  229. if not iot.exists():
  230. return response.json(10043)
  231. thing_name = iot[0]['thing_name'][14:] # IoT core上的物品名: Ansjer_Device_ + 序列号+企业编码/uid
  232. endpoint = iot[0]['endpoint']
  233. Token = iot[0]['token_iot_number']
  234. # Token = '297a601b3925e04daab5a60280650e09'
  235. topic_suffix = '_power_topic' if 'Turn' in MSG else '_rtsp_topic'
  236. topic_name = thing_name + topic_suffix # MQTT主题
  237. # api doc: https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/http.html
  238. # url: https://IoT_data_endpoint/topics/url_encoded_topic_name?qos=1
  239. # post请求url来发布MQTT消息
  240. url = 'https://{}/topics/{}'.format(endpoint, topic_name)
  241. authorizer_name = 'Ansjer_Iot_Auth'
  242. signature = CommonService.rsa_sign(Token) # Token签名
  243. headers = {'x-amz-customauthorizer-name': authorizer_name, 'Token': Token,
  244. 'x-amz-customauthorizer-signature': signature}
  245. params = {'command': MSG}
  246. r = requests.post(url=url, headers=headers, json=params, timeout=2)
  247. if r.status_code == 200:
  248. res = r.json()
  249. if res['message'] == 'OK':
  250. return response.json(0)
  251. return response.json(10044)
  252. else:
  253. # print('发布失败')
  254. return response.json(10044)
  255. except Exception as e:
  256. # print(e)
  257. return response.json(500, repr(e))
  258. def get_s3_pull_key(self, request_dict, response, request):
  259. # 通用发布主题通知
  260. UID = request_dict.get('UID', None)
  261. if not all([UID]):
  262. return response.json(444)
  263. try:
  264. # 获取检查uid的序列号,如果没有序列号,不使用MQTT下发消息
  265. device_info_qs = Device_Info.objects.filter(UID=UID).values('UID', 'serial_number')
  266. if not device_info_qs.exists():
  267. return response.json(10043)
  268. uid = device_info_qs[0]['UID']
  269. serial_number = device_info_qs[0]['serial_number']
  270. # 如果device_info表的serial_number不为空,物品名为'Ansjer_Device_序列号'
  271. thing_name_suffix = serial_number if serial_number != '' else uid
  272. # 获取数据组织将要请求的url
  273. iot = iotdeviceInfoModel.objects.filter(thing_name__contains=thing_name_suffix).values('thing_name',
  274. 'endpoint',
  275. 'token_iot_number')
  276. if not iot.exists():
  277. return response.json(10043)
  278. endpoint = iot[0]['endpoint']
  279. MSG = self.get_s3_key_return_msg(endpoint)
  280. return response.json(0,MSG)
  281. except Exception as e:
  282. # print(e)
  283. return response.json(500, repr(e))
  284. def get_s3_key_return_msg(self,endpoint):
  285. MSG = {}
  286. if 'cn-northwest-1' in endpoint :
  287. key = AWS_IOT_GETS3_PULL_CHINA_ID
  288. secret = AWS_IOT_GETS3_PULL_CHINA_SECRET
  289. arn = AWS_ARN[0]
  290. region_name = AWS_IOT_SES_ACCESS_CHINA_REGION
  291. else:
  292. key = AWS_IOT_GETS3_PULL_FOREIGN_ID
  293. secret = AWS_IOT_GETS3_PULL_FOREIGN_SECRET
  294. arn = AWS_ARN[1]
  295. if 'ap-southeast-1' in endpoint :
  296. region_name = AWS_IOT_SES_ACCESS_FOREIGN_REGION_ASIA
  297. if 'eu-west-1' in endpoint :
  298. region_name = AWS_IOT_SES_ACCESS_FOREIGN_REGION_EUROPE
  299. if 'us-east-1' in endpoint :
  300. region_name = AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA
  301. MSG['AccessKeyId'] = key
  302. MSG['AccessKeySecret'] = secret
  303. MSG['bucket_name'] = 'asj-log'
  304. MSG['arn'] = arn
  305. MSG['region_name'] = region_name
  306. return MSG
  307. def getIotInfo(self, request_dict, response):
  308. # 获取IoT数据
  309. serial_number = request_dict.get('serial_number', None)
  310. uid = request_dict.get('uid', None)
  311. if not uid and not serial_number:
  312. return response.json(444)
  313. try:
  314. if serial_number:
  315. serial_number = serial_number[0:6]
  316. iot_info_qs = iotdeviceInfoModel.objects.filter(serial_number=serial_number).\
  317. values('endpoint', 'token_iot_number')
  318. else:
  319. iot_info_qs = iotdeviceInfoModel.objects.filter(uid=uid).\
  320. values('endpoint', 'token_iot_number')
  321. if not iot_info_qs.exists():
  322. return response.json(173)
  323. endpoint = iot_info_qs[0]['endpoint']
  324. token_iot_number = iot_info_qs[0]['token_iot_number']
  325. res = {'endpoint': endpoint, 'token_iot_number': token_iot_number}
  326. return response.json(0, res)
  327. except Exception as e:
  328. return response.json(500, repr(e))
  329. def pcGetIotInfo(self, request_dict, response):
  330. # PC工具获取IoT数据
  331. serial_number = request_dict.get('serial_number', None)
  332. uid = request_dict.get('uid', None)
  333. if not uid and not serial_number:
  334. return response.json(444)
  335. try:
  336. if serial_number:
  337. serial_number = serial_number[0:6]
  338. iot_info_qs = iotdeviceInfoModel.objects.filter(serial_number=serial_number).\
  339. values('endpoint', 'token_iot_number')
  340. else:
  341. iot_info_qs = iotdeviceInfoModel.objects.filter(uid=uid).\
  342. values('endpoint', 'token_iot_number')
  343. if not iot_info_qs.exists():
  344. return response.json(173)
  345. endpoint = iot_info_qs[0]['endpoint']
  346. token_iot_number = iot_info_qs[0]['token_iot_number']
  347. res = {'endpoint': endpoint, 'token_iot_number': token_iot_number}
  348. return response.json(0, res)
  349. except Exception as e:
  350. return response.json(500, repr(e))