IotCoreController.py 19 KB

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