IotCoreController.py 18 KB

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