gatewayController.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. # -*- coding: utf-8 -*-
  2. """
  3. @Author : Rocky
  4. @Time : 2022/5/9 10:51
  5. @File :gatewayController.py
  6. """
  7. import datetime
  8. import threading
  9. import time
  10. import pytz
  11. import requests
  12. from django.views.generic.base import View
  13. from AnsjerPush.Config.gatewaySensorConfig import SENSOR_TYPE, EVENT_TYPE, DEVICE_TYPE, SMART_SOCKET_TOPIC
  14. from AnsjerPush.config import LOGGER, XM_PUSH_CHANNEL_ID
  15. from Model.models import SensorRecord, GatewaySubDevice, GatewayPush, Device_Info, SceneLog, SmartScene, CountryModel, \
  16. EffectiveTime
  17. from Object.ResponseObject import ResponseObject
  18. from Service.CommonService import CommonService
  19. from AnsjerPush.config import DETECT_PUSH_DOMAIN
  20. from Service.EquipmentInfoService import EquipmentInfoService
  21. from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
  22. from Service.PushService import PushObject
  23. class GatewayView(View):
  24. def get(self, request, *args, **kwargs):
  25. request.encoding = 'utf-8'
  26. operation = kwargs.get('operation')
  27. return self.validation(request.GET, operation)
  28. def post(self, request, *args, **kwargs):
  29. request.encoding = 'utf-8'
  30. operation = kwargs.get('operation')
  31. return self.validation(request.POST, operation)
  32. def validation(self, request_dict, operation):
  33. response = ResponseObject()
  34. if operation == 'gatewayPush': # 网关推送
  35. return self.gateway_push(request_dict, response)
  36. elif operation == 'sceneLogPush': # 场景日志推送
  37. return self.scene_log_push(request_dict, response)
  38. elif operation == 'socketPush': # 插座推送
  39. return self.socket_msg_push(request_dict, response)
  40. else:
  41. return response.json(414)
  42. @classmethod
  43. def gateway_push(cls, request_dict, response):
  44. """
  45. 网关推送
  46. @param request_dict: 请求参数
  47. @request_dict serial_number: 序列号
  48. @request_dict ieee_addr: 长地址
  49. @request_dict sensor_type: 传感器类型
  50. @request_dict event_type: 事件类型
  51. @request_dict defense: 防御状态,0:撤防,1:防御
  52. @request_dict sensor_status: 拆动状态,拆动时传参
  53. @param response: 响应对象
  54. @return: response
  55. """
  56. serial_number = request_dict.get('serial_number', None)
  57. ieee_addr = request_dict.get('ieee_addr', None)
  58. sensor_type = int(request_dict.get('sensor_type', None))
  59. event_type = int(request_dict.get('event_type', None))
  60. defense = int(request_dict.get('defense', None))
  61. LOGGER.info('---调用网关推送接口--- request_dict:{}'.format(request_dict))
  62. if not all([serial_number, ieee_addr, sensor_type, event_type]):
  63. return response.json(444)
  64. num = None
  65. n_time = int(time.time())
  66. try:
  67. # 获取温湿度
  68. if sensor_type == SENSOR_TYPE['tem_hum_sensor'] and (
  69. event_type == EVENT_TYPE['temperature'] or event_type == EVENT_TYPE['humidity']):
  70. num = request_dict.get('num')
  71. if num is None:
  72. return response.json(444)
  73. num = int(num) / 100
  74. # 查询子设备表id
  75. gateway_sub_device_qs = GatewaySubDevice.objects.filter(device__serial_number=serial_number,
  76. device_type=sensor_type, ieee_addr=ieee_addr). \
  77. values('id', 'nickname', 'device__userID__region_country')
  78. if not gateway_sub_device_qs.exists():
  79. return response.json(173)
  80. gateway_sub_device_id = gateway_sub_device_qs[0]['id']
  81. # 多线程执行场景任务
  82. task_kwargs = {
  83. 'num': num,
  84. 'n_time': n_time,
  85. 'event_type': event_type,
  86. 'gateway_sub_device_id': gateway_sub_device_id
  87. }
  88. execute_task_thread = threading.Thread(target=cls.execute_scene_tasks, kwargs=task_kwargs)
  89. execute_task_thread.start()
  90. country_id = gateway_sub_device_qs[0]['device__userID__region_country']
  91. lang = cls.confirm_lang(country_id)
  92. alarm = cls.get_alarm(lang, event_type)
  93. nickname = gateway_sub_device_qs[0]['nickname']
  94. sensor_record_dict = {
  95. 'gateway_sub_device_id': gateway_sub_device_id,
  96. 'alarm': alarm,
  97. 'event_type': event_type,
  98. 'created_time': n_time,
  99. }
  100. # 温湿度上报不推送
  101. if num is not None:
  102. sensor_record_dict['alarm'] = str(num)
  103. SensorRecord.objects.create(**sensor_record_dict)
  104. return response.json(0)
  105. SensorRecord.objects.create(**sensor_record_dict)
  106. # 门磁被拆动/拆动恢复,修改拆动状态
  107. if event_type == 2156:
  108. gateway_sub_device_qs.update(is_tampered=1)
  109. elif event_type == 2152:
  110. gateway_sub_device_qs.update(is_tampered=0)
  111. # 撤防状态不推送
  112. if defense == 0:
  113. return response.json(0)
  114. device_info_qs = Device_Info.objects.filter(serial_number=serial_number).values('userID_id')
  115. if not device_info_qs.exists():
  116. return response.json(173)
  117. equipment_info_list = []
  118. equipment_info_model = EquipmentInfoService.randoms_choice_equipment_info()
  119. # 推送表存储数据
  120. equipment_info_kwargs = {
  121. 'device_uid': serial_number,
  122. 'device_nick_name': nickname,
  123. 'event_type': event_type,
  124. 'event_time': n_time,
  125. 'add_time': n_time,
  126. 'alarm': alarm
  127. }
  128. for device_info in device_info_qs:
  129. user_id = device_info['userID_id']
  130. equipment_info_kwargs['device_user_id'] = user_id
  131. equipment_info_list.append(equipment_info_model(**equipment_info_kwargs))
  132. # 开启异步推送
  133. push_kwargs = {
  134. 'user_id': user_id,
  135. 'n_time': n_time,
  136. 'event_type': event_type,
  137. 'nickname': nickname,
  138. 'alarm': alarm,
  139. }
  140. push_thread = threading.Thread(
  141. target=cls.gateway_push_msg,
  142. kwargs=push_kwargs)
  143. push_thread.start()
  144. if equipment_info_list:
  145. equipment_info_model.objects.bulk_create(equipment_info_list)
  146. return response.json(0)
  147. except Exception as e:
  148. LOGGER.info('---网关推送接口异常--- {}'.format(repr(e)))
  149. return response.json(500, 'error_ine:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  150. @staticmethod
  151. def confirm_lang(country_id):
  152. """
  153. 根据country_id确定语言
  154. @param country_id: 国家id
  155. @return lang: 语言
  156. """
  157. country_qs = CountryModel.objects.filter(id=country_id).values('country_code')
  158. if not country_qs.exists():
  159. lang = 'NA'
  160. else:
  161. lang = country_qs[0]['country_code']
  162. return lang
  163. @staticmethod
  164. def get_alarm(lang, event_type):
  165. """
  166. 根据语言和事件类型确定警报内容
  167. @param lang: 语言
  168. @param event_type: 事件类型
  169. @return alarm: 警报内容
  170. """
  171. alarm = ''
  172. if lang == 'CN':
  173. # 门磁
  174. if event_type == 2150:
  175. alarm = '门磁开'
  176. elif event_type == 2151:
  177. alarm = '门磁关'
  178. elif event_type == 2156:
  179. alarm = '被拆动'
  180. elif event_type == 2152:
  181. alarm = '拆动恢复'
  182. # 智能按钮
  183. elif event_type == 2160:
  184. alarm = '紧急按钮按下'
  185. elif event_type == 2161:
  186. alarm = '单击'
  187. elif event_type == 2162:
  188. alarm = '双击'
  189. elif event_type == 2163:
  190. alarm = '长按'
  191. # 水浸
  192. elif event_type == 2170:
  193. alarm = '水浸触发'
  194. elif event_type == 2171:
  195. alarm = '水浸恢复'
  196. # 烟雾
  197. elif event_type == 2180:
  198. alarm = '烟雾触发'
  199. elif event_type == 2181:
  200. alarm = '烟雾恢复'
  201. # 人体红外
  202. elif event_type == 2190:
  203. alarm = '有人移动'
  204. elif event_type == 2191:
  205. alarm = '无人移动'
  206. # 低电量
  207. elif event_type in (2153, 2164, 2172, 2182, 2193):
  208. alarm = '低电量'
  209. else:
  210. # 门磁
  211. if event_type == 2150:
  212. alarm = 'Door magnetic opening'
  213. elif event_type == 2151:
  214. alarm = 'Door magnetic closing'
  215. elif event_type == 2156:
  216. alarm = 'Be dismantled'
  217. elif event_type == 2152:
  218. alarm = 'Dismantling recovery'
  219. # 智能按钮
  220. elif event_type == 2160:
  221. alarm = 'Emergency button pressed'
  222. elif event_type == 2161:
  223. alarm = 'Single click'
  224. elif event_type == 2162:
  225. alarm = 'Double click'
  226. elif event_type == 2163:
  227. alarm = 'Long press'
  228. # 水浸
  229. elif event_type == 2170:
  230. alarm = 'Water immersion trigger'
  231. elif event_type == 2171:
  232. alarm = 'Water immersion recovery'
  233. # 烟雾
  234. elif event_type == 2180:
  235. alarm = 'Smoke triggering'
  236. elif event_type == 2181:
  237. alarm = 'Smoke recovery'
  238. # 人体红外
  239. elif event_type == 2190:
  240. alarm = 'Someone moving'
  241. elif event_type == 2191:
  242. alarm = 'Unmanned movement'
  243. # 低电量
  244. elif event_type in (2153, 2164, 2172, 2182, 2193):
  245. alarm = 'LOW BATTERY'
  246. return alarm
  247. @classmethod
  248. def scene_log_push(cls, request_dict, response):
  249. """
  250. 网关智能场景日志推送
  251. @param request_dict: 请求参数
  252. @request_dict sceneId: 场景id
  253. @request_dict status: 状态
  254. @param response: 响应对象
  255. @return: response
  256. """
  257. scene_id = request_dict.get('sceneId', None)
  258. status = request_dict.get('status', None)
  259. LOGGER.info('---场景日志推送接口--- request_dict:{}'.format(request_dict))
  260. if not all([scene_id, status]):
  261. return response.json(444)
  262. smart_scene_qs = SmartScene.objects.filter(id=scene_id).values('scene_name', 'conditions', 'tasks', 'device_id',
  263. 'sub_device_id', 'user_id', 'scene_data')
  264. if not smart_scene_qs.exists():
  265. return response.json(173)
  266. scene_name = smart_scene_qs[0]['scene_name']
  267. tasks = smart_scene_qs[0]['tasks']
  268. device_id = smart_scene_qs[0]['device_id']
  269. sub_device_id = smart_scene_qs[0]['sub_device_id']
  270. n_time = int(time.time())
  271. user_id = smart_scene_qs[0]['user_id']
  272. scene_data = smart_scene_qs[0]['scene_data']
  273. if sub_device_id:
  274. gateway_sub_device_qs = GatewaySubDevice.objects.filter(id=sub_device_id).values('nickname')
  275. nickname = gateway_sub_device_qs[0]['nickname'] if gateway_sub_device_qs.exists() else ''
  276. else:
  277. device_qs = Device_Info.objects.filter(id=device_id).values('NickName')
  278. nickname = device_qs[0]['NickName'] if device_qs.exists() else ''
  279. log_dict = {
  280. 'scene_id': scene_id,
  281. 'scene_name': scene_name,
  282. 'tasks': tasks,
  283. 'status': status,
  284. 'device_id': device_id,
  285. 'sub_device_id': sub_device_id,
  286. 'created_time': n_time,
  287. }
  288. tasks = eval(tasks)
  289. try:
  290. SceneLog.objects.create(**log_dict)
  291. # 如果是一次性场景,关闭场景
  292. if scene_data:
  293. scene_data_dict = eval(scene_data)
  294. condition = scene_data_dict.get('condition')
  295. if condition:
  296. time_type = condition.get('time')
  297. if time_type == 'date':
  298. smart_scene_qs.update(is_enable=False)
  299. # 推送日志
  300. gateway_push_qs = GatewayPush.objects.filter(user_id=user_id, logout=False). \
  301. values('user_id', 'app_bundle_id', 'app_type', 'push_type', 'token_val', 'm_code', 'lang', 'm_code',
  302. 'tz')
  303. if not gateway_push_qs.exists():
  304. return response.json(174)
  305. # 开启异步推送
  306. push_kwargs = {
  307. 'tasks': tasks,
  308. 'n_time': n_time,
  309. 'nickname': nickname,
  310. 'gateway_push_qs': gateway_push_qs
  311. }
  312. push_thread = threading.Thread(
  313. target=cls.scene_msg_push,
  314. kwargs=push_kwargs)
  315. push_thread.start()
  316. return response.json(0)
  317. except Exception as e:
  318. LOGGER.info('---场景日志推送接口异常--- {}'.format(repr(e)))
  319. return response.json(500, 'error_ine:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  320. @classmethod
  321. def gateway_push_msg(cls, **push_kwargs):
  322. user_id = push_kwargs['user_id']
  323. # 查询推送配置数据
  324. gateway_push_qs = GatewayPush.objects.filter(user_id=user_id, logout=False). \
  325. values('user_id', 'app_bundle_id', 'app_type', 'push_type', 'token_val', 'm_code', 'lang', 'm_code',
  326. 'tz')
  327. if gateway_push_qs.exists():
  328. n_time = push_kwargs['n_time']
  329. event_type = push_kwargs['event_type']
  330. nickname = push_kwargs['nickname']
  331. alarm = push_kwargs['alarm']
  332. kwargs = {
  333. 'n_time': n_time,
  334. 'event_type': event_type,
  335. 'nickname': nickname,
  336. }
  337. # 推送到每台登录账号的手机
  338. for gateway_push in gateway_push_qs:
  339. app_bundle_id = gateway_push['app_bundle_id']
  340. push_type = gateway_push['push_type']
  341. token_val = gateway_push['token_val']
  342. lang = gateway_push['lang']
  343. tz = gateway_push['tz'] if gateway_push['tz'] else 0
  344. # 获取推送所需数据
  345. msg_title = PushObject.get_msg_title(nickname)
  346. msg_text = PushObject.get_gateway_msg_text(n_time, tz, lang, alarm)
  347. kwargs['msg_title'] = msg_title
  348. kwargs['msg_text'] = msg_text
  349. kwargs['app_bundle_id'] = app_bundle_id
  350. kwargs['token_val'] = token_val
  351. try:
  352. # 推送消息
  353. cls.push_msg(push_type, **kwargs)
  354. except Exception as e:
  355. LOGGER.info('网关推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  356. continue
  357. @classmethod
  358. def scene_msg_push(cls, **push_kwargs):
  359. tasks = push_kwargs['tasks']
  360. n_time = push_kwargs['n_time']
  361. nickname = push_kwargs['nickname']
  362. gateway_push_qs = push_kwargs['gateway_push_qs']
  363. for task in tasks:
  364. event_type = task['event_type']
  365. if event_type == '1001':
  366. kwargs = {
  367. 'n_time': n_time,
  368. 'event_type': event_type,
  369. 'nickname': nickname,
  370. }
  371. event_info = task['value']
  372. # 推送到每台登录账号的手机
  373. for gateway_push in gateway_push_qs:
  374. app_bundle_id = gateway_push['app_bundle_id']
  375. push_type = gateway_push['push_type']
  376. token_val = gateway_push['token_val']
  377. kwargs['msg_title'] = PushObject.get_msg_title(nickname)
  378. kwargs['msg_text'] = event_info
  379. kwargs['app_bundle_id'] = app_bundle_id
  380. kwargs['token_val'] = token_val
  381. try:
  382. # 推送消息
  383. cls.push_msg(push_type, **kwargs)
  384. except Exception as e:
  385. LOGGER.info(
  386. '场景日志推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  387. continue
  388. @staticmethod
  389. def push_msg(push_type, **kwargs):
  390. """
  391. 发送推送消息
  392. @param push_type: 推送类型
  393. @param kwargs: 推送参数
  394. @return: None
  395. """
  396. if push_type == 0: # ios apns
  397. PushObject.ios_apns_push(**kwargs)
  398. elif push_type == 1: # android gcm
  399. PushObject.android_fcm_push(**kwargs)
  400. elif push_type == 2: # android 极光推送
  401. PushObject.android_jpush(**kwargs)
  402. elif push_type == 3:
  403. huawei_push_object = HuaweiPushObject()
  404. huawei_push_object.send_push_notify_message(**kwargs)
  405. elif push_type == 4: # android 小米推送
  406. channel_id = XM_PUSH_CHANNEL_ID['device_reminder']
  407. PushObject.android_xmpush(channel_id=channel_id, **kwargs)
  408. elif push_type == 5: # android vivo推送
  409. PushObject.android_vivopush(**kwargs)
  410. elif push_type == 6: # android oppo推送
  411. channel_id = 'DEVICE_REMINDER'
  412. PushObject.android_oppopush(channel_id=channel_id, **kwargs)
  413. elif push_type == 7: # android 魅族推送
  414. PushObject.android_meizupush(**kwargs)
  415. @classmethod
  416. def socket_msg_push(cls, request_dict, response):
  417. """
  418. 智能插座开关状态推送
  419. """
  420. try:
  421. serial_number = request_dict.get('serialNumber', None)
  422. device_time = request_dict.get('deviceTime', None)
  423. status = request_dict.get('status', None)
  424. if not all([serial_number, status, device_time]):
  425. return response.json(444)
  426. status = int(status)
  427. now_time = int(device_time) if device_time else int(time.time())
  428. # 获取主用户设备id
  429. log_dict = {
  430. 'status': status,
  431. 'device_id': serial_number,
  432. 'created_time': now_time,
  433. }
  434. SceneLog.objects.create(**log_dict)
  435. LOGGER.info('成功接收并保存,插座序列号{},状态:{}'.format(serial_number, status))
  436. return response.json(0)
  437. except Exception as e:
  438. LOGGER.info('插座开关日志推送接口异常, error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  439. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  440. @classmethod
  441. def execute_scene_tasks(cls, gateway_sub_device_id, event_type, num, n_time):
  442. """
  443. 执行场景任务
  444. @param gateway_sub_device_id: 子设备id
  445. @param event_type: 事件类型
  446. @param num: 温湿度值
  447. @param n_time: 当前时间
  448. @return:
  449. """
  450. smart_scene_qs = SmartScene.objects.filter(sub_device_id=gateway_sub_device_id, is_enable=True). \
  451. values('id', 'scene_data', 'tz', 'is_all_day', 'effective_time_id')
  452. if smart_scene_qs.exists():
  453. for smart_scene in smart_scene_qs:
  454. if smart_scene['scene_data']:
  455. scene_data_dict = eval(smart_scene['scene_data'])
  456. condition = scene_data_dict.get('condition')
  457. if condition:
  458. # None 或 event_type
  459. scene_data_event_type = condition.get('event_type')
  460. # 触发事件类型跟条件事件类型一致,且任务列表不为空
  461. task_list = scene_data_dict.get('task_list')
  462. if event_type == scene_data_event_type and task_list:
  463. # 判断温湿度是否在条件设置的范围
  464. if num is not None:
  465. condition_value = condition.get('value')
  466. space_index = condition_value.index(' ')
  467. symbol, value = condition_value[:space_index], float(condition_value[space_index + 1:])
  468. # 温湿度不在设定范围,不执行
  469. if not ((symbol == '≥' and num >= value) or (symbol == '≤' and num <= value)):
  470. continue
  471. execute_task = cls.judge_execute_task(smart_scene, n_time)
  472. # 执行任务
  473. if execute_task:
  474. scene_id = 0
  475. task_list_len = len(task_list)
  476. for index, task in enumerate(task_list):
  477. # 最后一个任务上报日志
  478. if index == task_list_len - 1:
  479. scene_id = smart_scene['id']
  480. kwargs = {
  481. 'device_type': task['device_type'],
  482. 'event_type': task['event_type'],
  483. 'serial_number': task['serial_number'],
  484. 'delay_time': task['delay_time'],
  485. 'scene_id': scene_id
  486. }
  487. pub_mqtt_thread = threading.Thread(target=cls.pub_mqtt, kwargs=kwargs)
  488. pub_mqtt_thread.start()
  489. @classmethod
  490. def judge_execute_task(cls, smart_scene, n_time):
  491. """
  492. 判断是否执行任务
  493. @param smart_scene: 场景数据
  494. @param n_time: 当前时间
  495. @return execute_task: bool
  496. """
  497. execute_task = False
  498. # 判断时间是否在执行时间范围内
  499. if smart_scene['is_all_day'] == 1:
  500. # 全天必执行
  501. execute_task = True
  502. elif smart_scene['is_all_day'] == 2:
  503. # 非全天,判断当前时间是否在设置的时间范围
  504. tz = smart_scene['tz']
  505. time_minute, week = cls.get_time_info(n_time, tz)
  506. effective_time_id = smart_scene['effective_time_id']
  507. effective_time_qs = EffectiveTime.objects.filter(id=effective_time_id). \
  508. values('start_time', 'end_time', 'repeat')
  509. if effective_time_qs.exists():
  510. start_time = effective_time_qs[0]['start_time']
  511. end_time = effective_time_qs[0]['end_time']
  512. repeat = effective_time_qs[0]['repeat']
  513. time_frame_dict, time_frame_next_day_dict = cls.get_time_frame_dict(
  514. start_time, end_time, repeat)
  515. # 判断当前时间是否在设置的时间范围
  516. if time_frame_dict.get(week):
  517. start_time = time_frame_dict[week]['start_time']
  518. end_time = time_frame_dict[week]['end_time']
  519. if start_time <= time_minute <= end_time:
  520. execute_task = True
  521. if time_frame_next_day_dict.get(week):
  522. start_time = time_frame_next_day_dict[week]['start_time']
  523. end_time = time_frame_next_day_dict[week]['end_time']
  524. if start_time <= time_minute <= end_time:
  525. execute_task = True
  526. return execute_task
  527. @staticmethod
  528. def get_time_info(timestamp, timezone_offset):
  529. """
  530. 根据时间戳和时区获取时间(时分转为分钟数)和星期
  531. @param timestamp: 时间戳
  532. @param timezone_offset: 时区
  533. @return: time_minute, week
  534. """
  535. # 计算时区偏移量(以分钟为单位)
  536. timezone_offset_minutes = int(timezone_offset * 60)
  537. timezone = pytz.FixedOffset(timezone_offset_minutes)
  538. # 将时间戳转换为datetime对象,并应用时区
  539. dt_object = datetime.datetime.fromtimestamp(timestamp, tz=timezone)
  540. # 获取时分,并转为分钟数
  541. hour = dt_object.hour
  542. minute = dt_object.minute
  543. time_minute = hour * 60 + minute
  544. # 获取星期(0表示星期一,6表示星期日)
  545. week = str(dt_object.weekday())
  546. return time_minute, week
  547. @staticmethod
  548. def get_time_frame_dict(start_time, end_time, repeat):
  549. """
  550. 获取时间范围字典
  551. @param start_time: 开始时间
  552. @param end_time: 结束时间
  553. @param repeat: 星期周期的十进制数,如127 -> 0,1,2,3,4,5,6
  554. @return: time_frame_dict, time_frame_next_day_dict
  555. """
  556. # 十进制转为7位的二进制并倒序
  557. bin_str = bin(repeat)[2:].zfill(7)[::-1]
  558. # 生成星期周期列表
  559. week_list = []
  560. for i, bit in enumerate(bin_str):
  561. if bit == '1':
  562. week_list.append(i)
  563. # 生成时间范围字典
  564. time_frame_dict = {}
  565. time_frame_next_day_dict = {}
  566. # 非隔天
  567. if end_time > start_time:
  568. for week in week_list:
  569. time_frame_dict[str(week)] = {
  570. 'start_time': start_time,
  571. 'end_time': end_time
  572. }
  573. # 隔天
  574. else:
  575. # time_frame_dict记录当天时间范围,time_frame_next_day_dict记录溢出到第二天的时间范围
  576. for week in week_list:
  577. time_frame_dict[str(week)] = {
  578. 'start_time': start_time,
  579. 'end_time': 1439 # 23:59
  580. }
  581. week += 1
  582. if week == 7:
  583. week = 0
  584. time_frame_next_day_dict[str(week)] = {
  585. 'start_time': 0, # 00:00
  586. 'end_time': end_time
  587. }
  588. return time_frame_dict, time_frame_next_day_dict
  589. @staticmethod
  590. def pub_mqtt(device_type, event_type, serial_number, delay_time, scene_id=0):
  591. """
  592. 发布mqtt消息
  593. @param device_type: 设备类型
  594. @param event_type: 事件类型
  595. @param serial_number: 序列号
  596. @param delay_time: 延迟时间
  597. @param scene_id: 场景id
  598. @return:
  599. """
  600. if device_type == DEVICE_TYPE['socket']:
  601. topic_name = SMART_SOCKET_TOPIC.format(serial_number)
  602. status = 1 if event_type == EVENT_TYPE['socket_power_on'] else 0
  603. msg = {
  604. 'type': 1,
  605. 'data': {'deviceSwitch': status}
  606. }
  607. if delay_time:
  608. time.sleep(delay_time)
  609. CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
  610. # 没有设备任务时,最后一个任务上报场景日志
  611. if scene_id:
  612. data = {
  613. 'sceneId': scene_id,
  614. 'status': 1
  615. }
  616. url = DETECT_PUSH_DOMAIN + 'gatewayService/sceneLogPush'
  617. requests.post(url=url, data=data, timeout=8)