IotCoreController.py 18 KB

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