IotCoreController.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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. # 设备注册到aws iot core
  62. @staticmethod
  63. def create_keys_and_certificate(request_dict, response, request):
  64. logger = logging.getLogger('info')
  65. logger.info('设备注册到aws iot core请求参数:{}'.format(request_dict))
  66. token = request_dict.get('token', None)
  67. language = request_dict.get('language', None)
  68. time_stamp = request_dict.get('time_stamp', None)
  69. device_version = request_dict.get('device_version', None).replace('.', '_') # 物品组命名不能包含'.'
  70. if not all([token, language, time_stamp, device_version]):
  71. return response.json(444, {'param': 'token, language, time_stamp, device_version'})
  72. try:
  73. # 时间戳token校验
  74. no_rtc = request_dict.get('no_rtc', None)
  75. if no_rtc:
  76. if not CommonService.check_time_stamp_token_without_distance(token, time_stamp):
  77. return response.json(13)
  78. else:
  79. if not CommonService.check_time_stamp_token(token, time_stamp):
  80. return response.json(13)
  81. uid = request_dict.get('uid', '')
  82. uid_code = request_dict.get('uid_code', None)
  83. companyMark = '11A'
  84. if not uid: # 传序列号
  85. serial_number = request_dict.get('serial_number', None)
  86. serial_number_code = request_dict.get('serial_number_code', None)
  87. if not all([serial_number, serial_number_code]):
  88. return response.json(444, {'param': 'serial_number, serial_number_code'})
  89. # 序列号编码解码校验
  90. serial_number_code = CommonService.decode_data(serial_number_code)
  91. if serial_number != serial_number_code:
  92. return response.json(404)
  93. serial = serial_number[0:6]
  94. companyMark = serial_number[-3:]
  95. try:
  96. SerialNumberModel.objects.get(serial_number=serial)
  97. except:
  98. return response.json(444)
  99. ThingNameSuffix = serial_number # 物品名后缀
  100. iot_device_info_qs = iotdeviceInfoModel.objects.filter(serial_number=serial)
  101. else: # 传uid
  102. # uid编码解码校验
  103. uid_code = CommonService.decode_data(uid_code)
  104. if uid != uid_code:
  105. return response.json(404)
  106. serial = '' # iot_deviceInfo表写入serial_number为''
  107. ThingNameSuffix = uid # 物品名后缀
  108. iot_device_info_qs = iotdeviceInfoModel.objects.filter(uid=uid)
  109. # 判断设备是否已注册过
  110. if iot_device_info_qs.exists():
  111. iot = iot_device_info_qs[0]
  112. res = {
  113. 'certificateId': iot.certificate_id,
  114. 'certificatePem': iot.certificate_pem,
  115. 'publicKey': iot.public_key,
  116. 'privateKey': iot.private_key,
  117. 'endpoint': iot.endpoint
  118. }
  119. return response.json(0, {'res': res})
  120. else:
  121. # 根据地区id确定使用的boto3 client
  122. region_id = request_dict.get('region_id', None)
  123. if region_id:
  124. region_id = int(region_id)
  125. else:
  126. ip = CommonService.get_ip_address(request)
  127. region_id = Device_Region().get_device_region(ip)
  128. iotClient = IOTClient(region_id)
  129. # 拼接物品名
  130. if companyMark == '11A':
  131. ThingName = 'Ansjer_Device_' + ThingNameSuffix
  132. elif companyMark == '11L':
  133. ThingName = 'Loocam_Device_' + ThingNameSuffix
  134. else:
  135. ThingName = ThingNameSuffix
  136. thingGroup = device_version + '_' + language
  137. res = iotClient.register_to_iot_core(ThingName, thingGroup, response)
  138. token_iot_number = hashlib.md5((str(uuid.uuid1()) + str(int(time.time()))).encode('utf-8')).hexdigest()
  139. iotdeviceInfoModel.objects.create(uid=uid,
  140. serial_number=serial,
  141. endpoint=res[0]['endpoint'],
  142. certificate_id=res[0]['certificateId'],
  143. certificate_pem=res[0]['certificatePem'],
  144. public_key=res[0]['publicKey'],
  145. private_key=res[0]['privateKey'],
  146. thing_name=res[1]['ThingName'],
  147. thing_groups=res[1]['thingGroupName'],
  148. token_iot_number=token_iot_number
  149. )
  150. res = {
  151. 'certificateId': res[0]['certificateId'],
  152. 'certificatePem': res[0]['certificatePem'],
  153. 'publicKey': res[0]['publicKey'],
  154. 'privateKey': res[0]['privateKey'],
  155. 'endpoint': res[0]['endpoint']
  156. }
  157. return response.json(0, {'res': res})
  158. except Exception as e:
  159. print(e)
  160. return response.json(500, repr(e))
  161. def thing_regroup(self, request_dict, response, request):
  162. # 物品重新分组
  163. uid = request_dict.get('uid', '')
  164. token = request_dict.get('token', None)
  165. language = request_dict.get('language', None)
  166. time_stamp = request_dict.get('time_stamp', None)
  167. device_version = request_dict.get('device_version', None)
  168. if not all([token, language, time_stamp, device_version]):
  169. return response.json(444, {'param: token, language, time_stamp, device_version'})
  170. # 时间戳token校验
  171. if not CommonService.check_time_stamp_token(token, time_stamp):
  172. return response.json(13)
  173. ip = CommonService.get_ip_address(request)
  174. region_id = Device_Region().get_device_region(ip)
  175. iotClient = IOTClient(region_id)
  176. ThingNameSuffix = uid
  177. if not uid:
  178. # 使用序列号
  179. serial_number = request_dict.get('serial_number', None)
  180. if not serial_number:
  181. return response.json(444)
  182. ThingNameSuffix = serial_number
  183. thingName = 'Ansjer_Device_' + ThingNameSuffix
  184. new_thingGroupName = (device_version + '_' + language).replace('.', '_') # 物品组命名不能包含'.'
  185. try:
  186. # 获取旧物品组
  187. list_groups_res = iotClient.client.list_thing_groups_for_thing(thingName=thingName, maxResults=1)
  188. old_thingGroupName = list_groups_res['thingGroups'][0]['groupName']
  189. # 没有新物品组则创建
  190. list_thing_groups_res = iotClient.client.list_thing_groups(namePrefixFilter=new_thingGroupName
  191. , maxResults=1, recursive=False)
  192. if not list_thing_groups_res['thingGroups']:
  193. attributes = {
  194. "update_time": "0"
  195. }
  196. thingGroupProperties = {
  197. "thingGroupDescription": "OTA",
  198. "attributePayload": {
  199. "attributes": attributes,
  200. "merge": False # 更新时覆盖掉而不是合并
  201. }
  202. }
  203. iotClient.client.create_thing_group(thingGroupName=new_thingGroupName
  204. , thingGroupProperties=thingGroupProperties)
  205. iotClient.client.update_thing_groups_for_thing(thingName=thingName
  206. , thingGroupsToAdd=[new_thingGroupName]
  207. , thingGroupsToRemove=[old_thingGroupName])
  208. # 更新设备版本信息
  209. if uid:
  210. Device_Info.objects.filter(UID=uid).update(version=device_version)
  211. else:
  212. Device_Info.objects.filter(serial_number=serial_number).update(version=device_version)
  213. return response.json(0)
  214. except Exception as e:
  215. print(e)
  216. return response.json(500, repr(e))
  217. def clear_Iot_Cerm(self, userID, request_dict, response):
  218. serial_number = request_dict.get('serial_number', None)
  219. if serial_number:
  220. iot = iotdeviceInfoModel.objects.filter(thing_name="Ansjer_Device_" + serial_number)
  221. if iot.exists():
  222. iot.delete()
  223. return response.json(0)
  224. else:
  225. return response.json(444)
  226. def request_publish_message(self, request_dict, response, request):
  227. # Alexa请求IoT Core下发MQTT消息通知设备开始或停止推流,或唤醒设备
  228. UID = request_dict.get('UID', None)
  229. MSG = request_dict.get('MSG', None)
  230. if not all([UID, MSG]):
  231. return response.json(444)
  232. try:
  233. # 获取设备的物品名后缀
  234. device_info_qs = Device_Info.objects.filter(UID=UID).values('UID', 'serial_number')
  235. if not device_info_qs.exists():
  236. return response.json(10043)
  237. uid = device_info_qs[0]['UID']
  238. serial_number = device_info_qs[0]['serial_number']
  239. # 如果device_info表的serial_number不为空,物品名为'Ansjer_Device_序列号'
  240. thing_name_suffix = serial_number if serial_number != '' else uid
  241. # 获取数据组织将要请求的url
  242. iot = iotdeviceInfoModel.objects.filter(thing_name__contains=thing_name_suffix).values('thing_name',
  243. 'endpoint',
  244. 'token_iot_number')
  245. if not iot.exists():
  246. return response.json(10043)
  247. thing_name = iot[0]['thing_name'][14:] # IoT core上的物品名: Ansjer_Device_ + 序列号+企业编码/uid
  248. endpoint = iot[0]['endpoint']
  249. Token = iot[0]['token_iot_number']
  250. # Token = '297a601b3925e04daab5a60280650e09'
  251. topic_suffix = '_power_topic' if 'Turn' in MSG else '_rtsp_topic'
  252. topic_name = thing_name + topic_suffix # MQTT主题
  253. # api doc: https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/http.html
  254. # url: https://IoT_data_endpoint/topics/url_encoded_topic_name?qos=1
  255. # post请求url来发布MQTT消息
  256. url = 'https://{}/topics/{}'.format(endpoint, topic_name)
  257. authorizer_name = 'Ansjer_Iot_Auth'
  258. signature = CommonService.rsa_sign(Token) # Token签名
  259. headers = {'x-amz-customauthorizer-name': authorizer_name, 'Token': Token,
  260. 'x-amz-customauthorizer-signature': signature}
  261. params = {'command': MSG}
  262. r = requests.post(url=url, headers=headers, json=params, timeout=2)
  263. if r.status_code == 200:
  264. res = r.json()
  265. if res['message'] == 'OK':
  266. return response.json(0)
  267. return response.json(10044)
  268. else:
  269. # print('发布失败')
  270. return response.json(10044)
  271. except Exception as e:
  272. # print(e)
  273. return response.json(500, repr(e))
  274. def get_s3_pull_key(self, request_dict, response, request):
  275. # 通用发布主题通知
  276. UID = request_dict.get('UID', None)
  277. if not all([UID]):
  278. return response.json(444)
  279. try:
  280. # 获取检查uid的序列号,如果没有序列号,不使用MQTT下发消息
  281. device_info_qs = Device_Info.objects.filter(UID=UID).values('UID', 'serial_number')
  282. if not device_info_qs.exists():
  283. return response.json(10043)
  284. uid = device_info_qs[0]['UID']
  285. serial_number = device_info_qs[0]['serial_number']
  286. # 如果device_info表的serial_number不为空,物品名为'Ansjer_Device_序列号'
  287. thing_name_suffix = serial_number if serial_number != '' else uid
  288. # 获取数据组织将要请求的url
  289. iot = iotdeviceInfoModel.objects.filter(thing_name__contains=thing_name_suffix).values('thing_name',
  290. 'endpoint',
  291. 'token_iot_number')
  292. if not iot.exists():
  293. return response.json(10043)
  294. endpoint = iot[0]['endpoint']
  295. MSG = self.get_s3_key_return_msg(endpoint)
  296. return response.json(0,MSG)
  297. except Exception as e:
  298. # print(e)
  299. return response.json(500, repr(e))
  300. def get_s3_key_return_msg(self,endpoint):
  301. MSG = {}
  302. if 'cn-northwest-1' in endpoint :
  303. key = AWS_IOT_GETS3_PULL_CHINA_ID
  304. secret = AWS_IOT_GETS3_PULL_CHINA_SECRET
  305. arn = AWS_ARN[0]
  306. region_name = AWS_IOT_SES_ACCESS_CHINA_REGION
  307. else:
  308. key = AWS_IOT_GETS3_PULL_FOREIGN_ID
  309. secret = AWS_IOT_GETS3_PULL_FOREIGN_SECRET
  310. arn = AWS_ARN[1]
  311. if 'ap-southeast-1' in endpoint :
  312. region_name = AWS_IOT_SES_ACCESS_FOREIGN_REGION_ASIA
  313. if 'eu-west-1' in endpoint :
  314. region_name = AWS_IOT_SES_ACCESS_FOREIGN_REGION_EUROPE
  315. if 'us-east-1' in endpoint :
  316. region_name = AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA
  317. MSG['AccessKeyId'] = key
  318. MSG['AccessKeySecret'] = secret
  319. MSG['bucket_name'] = 'asj-log'
  320. MSG['arn'] = arn
  321. MSG['region_name'] = region_name
  322. return MSG
  323. def getIotInfo(self, request_dict, response):
  324. # 获取IoT数据
  325. serial_number = request_dict.get('serial_number', None)
  326. uid = request_dict.get('uid', None)
  327. if not uid and not serial_number:
  328. return response.json(444)
  329. try:
  330. if serial_number:
  331. serial_number = serial_number[0:6]
  332. iot_info_qs = iotdeviceInfoModel.objects.filter(serial_number=serial_number).\
  333. values('endpoint', 'token_iot_number')
  334. else:
  335. iot_info_qs = iotdeviceInfoModel.objects.filter(uid=uid).\
  336. values('endpoint', 'token_iot_number')
  337. if not iot_info_qs.exists():
  338. return response.json(173)
  339. endpoint = iot_info_qs[0]['endpoint']
  340. token_iot_number = iot_info_qs[0]['token_iot_number']
  341. res = {'endpoint': endpoint, 'token_iot_number': token_iot_number}
  342. return response.json(0, res)
  343. except Exception as e:
  344. return response.json(500, repr(e))
  345. def pcGetIotInfo(self, request_dict, response):
  346. # PC工具获取IoT数据
  347. serial_number = request_dict.get('serial_number', None)
  348. uid = request_dict.get('uid', None)
  349. if not uid and not serial_number:
  350. return response.json(444)
  351. try:
  352. if serial_number:
  353. serial_number = serial_number[0:6]
  354. iot_info_qs = iotdeviceInfoModel.objects.filter(serial_number=serial_number).\
  355. values('endpoint', 'token_iot_number')
  356. else:
  357. iot_info_qs = iotdeviceInfoModel.objects.filter(uid=uid).\
  358. values('endpoint', 'token_iot_number')
  359. if not iot_info_qs.exists():
  360. return response.json(173)
  361. endpoint = iot_info_qs[0]['endpoint']
  362. token_iot_number = iot_info_qs[0]['token_iot_number']
  363. res = {'endpoint': endpoint, 'token_iot_number': token_iot_number}
  364. return response.json(0, res)
  365. except Exception as e:
  366. return response.json(500, repr(e))