SmartSocketController.py 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. # -*- encoding: utf-8 -*-
  2. """
  3. @File : SmartSocketController.py
  4. @Time : 2023/3/17 11:52
  5. @Author : stephen
  6. @Email : zhangdongming@asj6.wecom.work
  7. @Software: PyCharm
  8. """
  9. import calendar
  10. import datetime
  11. import logging
  12. import time
  13. import requests
  14. from dateutil.parser import parse
  15. from django.db import transaction, connections
  16. from django.db.models import Sum, Count
  17. from django.http import QueryDict
  18. from django.views import View
  19. from Model.models import SocketInfo, SocketSchedule, Device_Info, SocketPowerStatistics, SceneLog, FamilyRoomDevice
  20. from Object.AWS.AWSIoTDataPlaneUtil import AWSIoTDataPlaneService
  21. from Object.ResponseObject import ResponseObject
  22. from Object.utils import LocalDateTimeUtil
  23. from Service.CommonService import CommonService
  24. from Ansjer.config import CONFIG_INFO, AWS_IOT_SES_ACCESS_CHINA_ID, AWS_IOT_SES_ACCESS_CHINA_SECRET, \
  25. AWS_IOT_SES_ACCESS_CHINA_REGION, AWS_IOT_SES_ACCESS_FOREIGN_ID, AWS_IOT_SES_ACCESS_FOREIGN_SECRET, \
  26. AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA, AWS_IOT_SES_ACCESS_FOREIGN_REGION_EUROPE
  27. LOGGER = logging.getLogger('info')
  28. SOCKET_TOPIC_NAME = 'loocam/smart-socket/{}' # 插座发布消息主题(因设备当前版本只能订阅一个主题)
  29. class SmartSocketView(View):
  30. def get(self, request, *args, **kwargs):
  31. request.encoding = 'utf-8'
  32. operation = kwargs.get('operation')
  33. return self.validation(request.GET, request, operation)
  34. def post(self, request, *args, **kwargs):
  35. request.encoding = 'utf-8'
  36. operation = kwargs.get('operation')
  37. return self.validation(request.POST, request, operation)
  38. def delete(self, request, *args, **kwargs):
  39. request.encoding = 'utf-8'
  40. operation = kwargs.get('operation')
  41. delete = QueryDict(request.body)
  42. if not delete:
  43. delete = request.GET
  44. return self.validation(delete, request, operation)
  45. def put(self, request, *args, **kwargs):
  46. request.encoding = 'utf-8'
  47. operation = kwargs.get('operation')
  48. put = QueryDict(request.body)
  49. return self.validation(put, request, operation)
  50. def validation(self, request_dict, request, operation):
  51. ResponseObject('cn')
  52. if operation == 'savePowerStatistics': # 保存电量上报统计
  53. return self.save_power_statistics(request_dict, ResponseObject('cn'))
  54. elif operation == 'reset': # 设备复位
  55. return self.socket_reset(request_dict, ResponseObject('cn'))
  56. elif operation == 'alexa-socket-switch': # 新增alexa智能开关
  57. return self.alexa_socket_switch(request_dict, ResponseObject('cn'))
  58. elif operation == 'getSocketState': # 获取alexa智能开关状态
  59. return self.get_socket_state(request_dict, ResponseObject('cn'))
  60. elif operation == 'socketPush': # 插座推送
  61. return self.socket_msg_push(request_dict, ResponseObject('cn'))
  62. token_code, user_id, response = CommonService \
  63. .verify_token_get_user_id(request_dict, request)
  64. if token_code != 0:
  65. return response.json(token_code)
  66. if operation == 'saveSwitch': # 添加插座开关
  67. return self.save_switch(request_dict, response)
  68. elif operation == 'saveCountDown': # 添加插座倒计时
  69. return self.save_count_down(request_dict, response)
  70. elif operation == 'saveSchedule': # 添加插座排程
  71. return self.save_socket_schedule(request_dict, response)
  72. elif operation == 'get-all-scene': # 统计智能插座电量
  73. return self.get_all_scene(request_dict, response)
  74. elif operation == 'get-socket-schedule': # 智能插座排程记录查询
  75. return self.get_socket_schedule(request_dict, response)
  76. elif operation == 'get-log': # 智能插座开关日志记录查询
  77. return self.get_log(request_dict, response)
  78. elif operation == 'del-socket-schedule': # 批量刪除排程
  79. return self.del_socket_schedule(request_dict, response, user_id)
  80. elif operation == 'get-unit-scene': # 查詢設備每日/月用電量
  81. return self.get_unit_scene(request_dict, response)
  82. elif operation == 'get-schedule-data': # 查询插座记录日期
  83. return self.get_schedule_data(request_dict, response)
  84. return response.json(404)
  85. @classmethod
  86. def socket_reset(cls, request_dict, response):
  87. """
  88. 智能插座复位删除数据
  89. """
  90. try:
  91. with transaction.atomic():
  92. serial_number = request_dict.get('serialNumber', None)
  93. status = request_dict.get('status', None) # 设备当前状态0:关, 1:开, 2:离线
  94. if not all([serial_number, status]):
  95. return response.json(444, {'error': 'serialNumber and status'})
  96. socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number, type_switch=0)
  97. if not socket_info_qs.exists():
  98. return response.json(173)
  99. device_id = socket_info_qs.first().device_id
  100. if socket_info_qs.first().status == 1: # 设备电源开时 恢复为关闭状态
  101. socket_info_qs.update(status=0, updated_time=int(time.time()))
  102. # 删除插座电量统计
  103. SocketPowerStatistics.objects.filter(device_id=device_id).delete()
  104. # 删除插座排程
  105. SocketSchedule.objects.filter(device_id=device_id).delete()
  106. # 删除插座开关日志
  107. SceneLog.objects.filter(device_id=serial_number).delete()
  108. # 离线状态下只删除设备电量, 排程和开关日志
  109. if int(status) != 2:
  110. # 删除插座倒计时
  111. SocketInfo.objects.filter(device_id=device_id).delete()
  112. # 删除设备管理家庭接口
  113. FamilyRoomDevice.objects.filter(device_id=device_id).delete()
  114. # 删除设备
  115. Device_Info.objects.filter(id=device_id).delete()
  116. # alexa删除插座
  117. cls.delete_alexa_socket(serial_number)
  118. LOGGER.info('智能插座{}设备已复位'.format(serial_number))
  119. return response.json(0)
  120. except Exception as e:
  121. LOGGER.info('插座复位删除数据异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  122. return response.json(177)
  123. @classmethod
  124. def save_power_statistics(cls, request_dict, response):
  125. """
  126. 保存设备上报电量统计
  127. """
  128. try:
  129. serial_number = request_dict.get('serialNumber', None)
  130. watt = request_dict.get('electricity', 0.00) # 功率
  131. power = request_dict.get('power', 0.00) # 负载功率
  132. # 在线时长秒
  133. accumulated_time = request_dict.get('accumulatedTime', None)
  134. device_time = request_dict.get('deviceTime', None)
  135. if not all([serial_number, watt, power, accumulated_time, device_time]):
  136. return response.json(444)
  137. watt = float(watt)
  138. power = float(power)
  139. accumulated_time = int(accumulated_time)
  140. # 判断上报数据是否为负数
  141. number_list = [watt, power, accumulated_time]
  142. number = CommonService.negative_number_judgment(number_list)
  143. if not number:
  144. if watt < 0:
  145. watt = 0
  146. if power < 0:
  147. power = 0
  148. if accumulated_time < 0:
  149. accumulated_time = 0
  150. now_time = int(time.time())
  151. LOGGER.info('{}上报电量统计data:{}'.format(serial_number, request_dict))
  152. start_time, end_time = LocalDateTimeUtil.get_today_date(True)
  153. # 查询当前序列号当天是否有上传过电量统计
  154. power_qs = SocketPowerStatistics.objects.filter(serial_number=serial_number,
  155. created_time__gt=start_time,
  156. created_time__lte=end_time)
  157. data = {
  158. 'power': power,
  159. 'updated_time': now_time,
  160. 'watt': watt
  161. }
  162. if not power_qs.exists(): # 添加插座上报电量统计
  163. socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number).values('device_id')
  164. if not socket_info_qs.exists():
  165. return response.json(173)
  166. data['device_id'] = socket_info_qs[0]['device_id']
  167. data['created_time'] = now_time
  168. data['serial_number'] = serial_number
  169. data['electricity'] = cls.calculated_power(watt, accumulated_time)
  170. data['accumulated_time'] = accumulated_time
  171. SocketPowerStatistics.objects.create(**data)
  172. return response.json(0)
  173. power_vo = power_qs.first()
  174. # 累加在线时间目前是以分钟为单位
  175. data['accumulated_time'] = power_vo.accumulated_time + accumulated_time
  176. # kwh 千瓦时
  177. kilowatt_hour = cls.calculated_power(watt, accumulated_time)
  178. data['electricity'] = kilowatt_hour + float(power_vo.electricity)
  179. # 所消耗累计功率
  180. data['watt'] = float(power_vo.watt) + watt
  181. # 更新当天电量统计
  182. power_qs.update(**data)
  183. return response.json(0)
  184. except Exception as e:
  185. LOGGER.info('智能插座电量存库异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  186. return response.json(177)
  187. @staticmethod
  188. def calculated_power(watt, minute):
  189. """
  190. 通过每分钟所消耗的功率(瓦)得到千瓦时kwh
  191. """
  192. if watt == 0 or watt < 0.1 or minute == 0:
  193. return 0.00
  194. kilowatt_hour = watt / 1000
  195. LOGGER.info('瓦计算得到千瓦时结果{}'.format(kilowatt_hour))
  196. return kilowatt_hour
  197. @staticmethod
  198. def get_serial_number_by_device_id(deviceId):
  199. """
  200. 根据设备ID获取序列号
  201. """
  202. device_info = Device_Info.objects.get(id=deviceId)
  203. return device_info.serial_number
  204. @classmethod
  205. def save_switch(cls, request_dict, response):
  206. """
  207. 添加开关
  208. """
  209. device_id = request_dict.get('deviceId', None)
  210. status = request_dict.get('status', None)
  211. if not all([device_id, status]):
  212. return response.json(444)
  213. serial_number = cls.get_serial_number_by_device_id(device_id)
  214. # 保存数据库并下发MQTT消息到插座设备
  215. result = cls.save_socket_switch(device_id, serial_number, int(status))
  216. if not result:
  217. return response.json(177)
  218. return response.json(0)
  219. @staticmethod
  220. def save_socket_switch(device_id, serial_number, status, type_switch=0):
  221. """
  222. 保存插座开关信息
  223. @param device_id: 设备ID
  224. @param serial_number: 序列号
  225. @param status: 状态 0关,1开
  226. @param type_switch: 0:总开关,1倒计时开关
  227. @return: True | False
  228. """
  229. if not device_id:
  230. return False
  231. socket_info_qs = SocketInfo.objects.filter(device_id=device_id, type_switch=type_switch)
  232. LOGGER.info('{}进入插座电源开关OR倒计时,类型:{}'.format(serial_number, type_switch))
  233. now_time = int(time.time())
  234. try:
  235. with transaction.atomic():
  236. # 创建插座开关信息
  237. if not socket_info_qs.exists():
  238. socket_dict = {"device_id": device_id,
  239. "serial_number": serial_number,
  240. "status": status,
  241. "type_switch": type_switch,
  242. "created_time": now_time,
  243. "updated_time": now_time,
  244. "online": True}
  245. SocketInfo.objects.create(**socket_dict)
  246. return True
  247. if socket_info_qs.first().status != status:
  248. socket_info_qs.update(status=status, updated_time=now_time)
  249. # 主题名称
  250. topic_name = SOCKET_TOPIC_NAME.format(serial_number)
  251. # 发布消息内容
  252. msg = {'type': 1, 'data': {'deviceSwitch': status}}
  253. result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
  254. LOGGER.info('{}智能插座开关设置发布MQTT消息结果{}'.format(serial_number, result))
  255. return True
  256. except Exception as e:
  257. LOGGER.info('智能插座异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  258. return False
  259. @classmethod
  260. def save_count_down(cls, request_dict, response):
  261. """
  262. 添加倒计时
  263. """
  264. device_id = request_dict.get('deviceId', None)
  265. status = request_dict.get('status', None)
  266. start = request_dict.get('start', None)
  267. count_down_time = request_dict.get('countDownTime', None)
  268. if not all([device_id, status, count_down_time]):
  269. return response.json(444)
  270. serial_number = cls.get_serial_number_by_device_id(device_id)
  271. # 保存数据库并下发MQTT消息到插座设备
  272. result = cls.save_socket_count_down(device_id, serial_number, int(status), int(start), int(count_down_time))
  273. if not result:
  274. return response.json(177)
  275. return response.json(0)
  276. @staticmethod
  277. def save_socket_count_down(device_id, serial_number, status, start, count_down_time, type_switch=1):
  278. """
  279. 保存插座倒计时信息
  280. @param count_down_time: 倒计时时间戳
  281. @param start: 是否启动倒计时 0:关闭,1:开始
  282. @param device_id: 设备ID
  283. @param serial_number: 序列号
  284. @param status: 倒计时电源状态 0关,1开
  285. @param type_switch: 0:总开关,1倒计时开关
  286. @return:
  287. """
  288. if not device_id:
  289. return False
  290. socket_info_qs = SocketInfo.objects.filter(device_id=device_id, type_switch=type_switch)
  291. now_time = int(time.time())
  292. try:
  293. with transaction.atomic():
  294. # 创建插座倒计时信息
  295. if not socket_info_qs.exists():
  296. socket_dict = {"device_id": device_id,
  297. "serial_number": serial_number,
  298. "status": status,
  299. "type_switch": type_switch,
  300. "created_time": now_time,
  301. "updated_time": now_time,
  302. "online": True,
  303. "start": True if start == 1 else False,
  304. "count_down_time": count_down_time}
  305. socket_info_qs = SocketInfo.objects.create(**socket_dict)
  306. count_down_id = socket_info_qs.id
  307. else:
  308. socket_info_qs.update(status=status, count_down_time=count_down_time,
  309. updated_time=now_time)
  310. count_down_id = socket_info_qs.first().id
  311. # 主题名称
  312. topic_name = SOCKET_TOPIC_NAME.format(serial_number)
  313. # 发布消息内容
  314. msg = {'type': 2,
  315. 'data': {'powerType': status,
  316. 'countDownId': count_down_id,
  317. 'time': count_down_time,
  318. 'start': start}}
  319. result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
  320. LOGGER.info('智能插座倒计时发布MQTT消息结果{}'.format(result))
  321. return True
  322. except Exception as e:
  323. LOGGER.info('智能插座异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  324. return False
  325. @classmethod
  326. def save_socket_schedule(cls, request_dict, response):
  327. """
  328. 插座添加排程
  329. """
  330. try:
  331. device_id = request_dict.get('deviceId', None)
  332. task_type = request_dict.get('timeType', None)
  333. start_time = request_dict.get('startTime', None)
  334. end_time = request_dict.get('endTime', 0)
  335. repeat = request_dict.get('repeat', None)
  336. task_id = request_dict.get('taskId', None)
  337. device_switch = request_dict.get('deviceSwitch', None)
  338. task_switch = request_dict.get('taskSwitch', None)
  339. if not all([task_type, start_time, end_time, repeat, device_switch, task_switch]):
  340. return response.json(444)
  341. device_switch = int(device_switch)
  342. task_switch = int(task_switch)
  343. now_time = int(time.time())
  344. task_type = int(task_type)
  345. end_time = int(end_time) if task_type == 2 else 0
  346. data = {'time_type': task_type, 'start_time': int(start_time), 'repeat': int(repeat),
  347. 'switch_status': True if device_switch == 1 else False,
  348. 'task_status': True if task_switch == 1 else False}
  349. serial_number = cls.get_serial_number_by_device_id(device_id)
  350. if task_id: # 修改排程
  351. task_id = int(task_id)
  352. socket_schedule_qs = SocketSchedule.objects.filter(id=task_id)
  353. if not socket_schedule_qs.exists():
  354. return response.json(174)
  355. if end_time:
  356. data['end_time'] = end_time
  357. data['updated_time'] = now_time
  358. socket_schedule_qs.update(**data)
  359. else:
  360. # 查询是否已设置过当前排程
  361. socket_s_qs = SocketSchedule.objects.filter(device_id=device_id,
  362. start_time=int(start_time),
  363. end_time=end_time,
  364. time_type=task_type)
  365. if socket_s_qs.exists():
  366. return response.json(174)
  367. schedule_count = SocketSchedule.objects.filter(device_id=device_id).count()
  368. if schedule_count >= 20:
  369. return response.json(10061)
  370. # 添加排程
  371. data['device_id'] = device_id
  372. data['end_time'] = end_time
  373. data['serial_number'] = serial_number
  374. data['updated_time'] = now_time
  375. data['created_time'] = now_time
  376. socket_schedule = SocketSchedule.objects.create(**data)
  377. task_id = socket_schedule.id
  378. # 将排程任务下发给设备
  379. cls.send_socket_schedule(serial_number, task_id, task_type, int(start_time),
  380. end_time, int(repeat), device_switch,
  381. task_switch)
  382. return response.json(0)
  383. except Exception as e:
  384. LOGGER.info('智能插座异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  385. return False
  386. @staticmethod
  387. def send_socket_schedule(serial_number, task_id, time_type, start_time, end_time, repeat, device_switch,
  388. task_switch):
  389. """
  390. 排程下发设备
  391. @param serial_number: 序列号
  392. @param task_id: 当前排程任务id
  393. @param time_type: 任务类型 0:设定时间,1:设定时间段
  394. @param start_time: 开启时间
  395. @param end_time: 结束时间
  396. @param repeat: 重复日期
  397. @param device_switch: 任务执行后期望设备状态,0:关闭,1:开启
  398. @param task_switch: 任务执行状态 0:不执行,1:执行
  399. @return: True | False
  400. """
  401. msg = {
  402. 'type': 3,
  403. 'data': {'taskId': task_id, 'timeType': time_type,
  404. 'startTime': start_time, 'endTime': end_time,
  405. 'repeat': repeat,
  406. 'deviceSwitch': device_switch,
  407. 'taskSwitch': task_switch}
  408. }
  409. # 主题名称
  410. topic_name = SOCKET_TOPIC_NAME.format(serial_number)
  411. result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
  412. LOGGER.info('智能插座{}排程任务发布MQTT消息结果{}'.format(serial_number, result))
  413. return result
  414. # 以下是查询智能插座接口
  415. @staticmethod
  416. def get_all_scene(request_dict, response):
  417. """
  418. 统计智能插座电量
  419. @request_dict serialNumber: 序列号
  420. @request_dict unit: 时间单位
  421. @param request_dict: 请求数据
  422. @param response: 响应
  423. @return: response
  424. """
  425. serial_number = request_dict.get('serialNumber', None)
  426. # 确定是否会传值
  427. if not all([serial_number]):
  428. return response.json(444, {'error param': 'serialNumber'})
  429. all_socket_power_qs = SocketPowerStatistics.objects. \
  430. filter(serial_number=serial_number).values('electricity', 'accumulated_time', 'power', 'created_time') \
  431. .order_by('-created_time')
  432. if not all_socket_power_qs.exists():
  433. return response.json(0, {})
  434. try:
  435. data = {}
  436. # 设备累计电量
  437. all_electricity = all_socket_power_qs.aggregate(total=Sum('electricity'))
  438. data['electricityAll'] = round(all_electricity['total'], 1)
  439. # 本月电费
  440. nowTime = int(time.time())
  441. nowTime = CommonService.timestamp_to_str(nowTime)
  442. year, month = str(nowTime).split('-')[0], str(nowTime).split('-')[1]
  443. end = calendar.monthrange(int(year), int(month))[1]
  444. startTime_now = parse('%s-%s-01 00:00:00' % (year, month))
  445. endTime_now = parse('%s-%s-%s 23:59:59' % (year, month, end))
  446. startTime_now = CommonService.str_to_timestamp(str(startTime_now))
  447. endTime_now = CommonService.str_to_timestamp(str(endTime_now))
  448. electricity = all_socket_power_qs.filter(created_time__gte=startTime_now,
  449. created_time__lt=endTime_now).aggregate(
  450. total=Sum('electricity'))
  451. if electricity['total'] is not None:
  452. data['electricityMonth'] = round(electricity['total'], 1)
  453. else:
  454. data['electricityMonth'] = 0
  455. # 获取当前日期
  456. nowTime = int(time.time())
  457. today = datetime.date.today()
  458. # 今天开始时间
  459. today_start_time = int(time.mktime(time.strptime(str(today), '%Y-%m-%d')))
  460. today_socket_power_qs = all_socket_power_qs.filter(created_time__gte=today_start_time,
  461. created_time__lt=nowTime).values('electricity',
  462. 'accumulated_time',
  463. 'power',
  464. 'created_time')
  465. # 当天使用电量
  466. data['electricityToday'] = round(today_socket_power_qs[0]['electricity'],
  467. 1) if today_socket_power_qs.exists() else 0
  468. # 当天累计时长
  469. data['accumulated_time'] = today_socket_power_qs[0][
  470. 'accumulated_time'] if today_socket_power_qs.exists() else 0
  471. # 当前功率
  472. data['power'] = round(today_socket_power_qs[0]['power'], 1) if today_socket_power_qs.exists() else 0
  473. # 昨天使用电量
  474. yesterday = today - datetime.timedelta(days=1)
  475. # 昨天开始时间戳
  476. yesterday_start_time = int(time.mktime(time.strptime(str(yesterday), '%Y-%m-%d')))
  477. # 昨天结束时间戳
  478. yesterday_end_time = int(time.mktime(time.strptime(str(today), '%Y-%m-%d'))) - 1
  479. socket_qs = all_socket_power_qs.filter(created_time__gte=yesterday_start_time,
  480. created_time__lt=yesterday_end_time).values('electricity')
  481. if socket_qs.exists():
  482. data['electricityYesterday'] = round(socket_qs[0]['electricity'], 1)
  483. else:
  484. data['electricityYesterday'] = 0
  485. return response.json(0, data)
  486. except Exception as e:
  487. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  488. @staticmethod
  489. def get_socket_schedule(request_dict, response):
  490. """
  491. 智能插座排程记录查询
  492. @param request_dict: 请求参数
  493. @request_dict page: 页数
  494. @request_dict size: 条数
  495. @request_dict serialNumber: 设备序列号
  496. @param response: 响应对象
  497. @return: response
  498. """
  499. page = request_dict.get('pageNo', None)
  500. size = request_dict.get('pageSize', None)
  501. serial_number = request_dict.get('serialNumber', None)
  502. if not all([page, size, serial_number]):
  503. return response.json(444)
  504. page, size = int(page), int(size)
  505. socket_schedule_qs = SocketSchedule.objects.filter(serial_number=serial_number).values('switch_status',
  506. 'start_time',
  507. 'end_time',
  508. 'repeat',
  509. 'task_status',
  510. 'time_type',
  511. 'created_time',
  512. 'updated_time',
  513. 'device_id',
  514. 'id').order_by(
  515. '-created_time')[(page - 1) * size:page * size]
  516. if not socket_schedule_qs.exists():
  517. return response.json(0, [])
  518. try:
  519. schedule_list = []
  520. for socket_schedule in socket_schedule_qs:
  521. schedule_list.append({
  522. 'taskId': socket_schedule['id'],
  523. 'deviceID': socket_schedule['device_id'],
  524. 'serialNumber': serial_number,
  525. 'timeType': socket_schedule['time_type'],
  526. 'startTime': socket_schedule['start_time'],
  527. 'endTime': socket_schedule['end_time'],
  528. 'switchStatus': socket_schedule['switch_status'],
  529. 'taskStatus': socket_schedule['task_status'],
  530. # 进制
  531. 'repeat': socket_schedule['repeat'],
  532. })
  533. return response.json(0, schedule_list)
  534. except Exception as e:
  535. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  536. @staticmethod
  537. def get_log(request_dict, response):
  538. """
  539. 智能插座开关日志记录查询
  540. @param request_dict: 请求参数
  541. @request_dict page: 页数
  542. @request_dict size: 条数
  543. @request_dict serialNumber: 设备序列号
  544. @request_dict startTime: 开始时间
  545. @request_dict endTime: 结束时间
  546. @param response: 响应对象
  547. @return: response
  548. # 日誌擦護用序列號查詢
  549. """
  550. page = request_dict.get('page', None)
  551. size = request_dict.get('size', None)
  552. serial_number = request_dict.get('serialNumber', None)
  553. startTime = request_dict.get('startTime', None)
  554. endTime = request_dict.get('endTime', None)
  555. if not all([page, size, serial_number]):
  556. return response.json(444, 'errno: page or size or serial_number')
  557. page, size = int(page), int(size)
  558. try:
  559. sql_query = """
  560. SELECT DISTINCT
  561. tasks,
  562. status,
  563. created_time
  564. FROM scene_log
  565. WHERE device_id = %s {}
  566. ORDER BY created_time DESC,id DESC
  567. LIMIT %s, %s;
  568. """
  569. params = [serial_number]
  570. if startTime is not None and endTime is not None:
  571. sql_query = sql_query.format(
  572. "AND created_time >= %s AND created_time < %s ")
  573. params.extend([startTime, endTime])
  574. else:
  575. sql_query = sql_query.format(" ")
  576. params.extend([(page - 1) * size, size])
  577. cursor = connections['mysql02'].cursor()
  578. cursor.execute(sql_query, params)
  579. results = cursor.fetchall()
  580. # 关闭游标
  581. cursor.close()
  582. connections.close_all()
  583. log_list = []
  584. for result in results:
  585. data = {
  586. 'serialNumber': serial_number,
  587. 'tasks': result[0] if result[0] else '',
  588. 'status': result[1],
  589. 'createdTime': result[2],
  590. }
  591. log_list.append(data)
  592. return response.json(0, log_list)
  593. except Exception as e:
  594. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  595. @classmethod
  596. def splittings_time(cls, startTime, endTime, unit):
  597. """
  598. 根據時間單位分割時間
  599. """
  600. diction = {}
  601. time_list = []
  602. # 开始时间
  603. startTime = CommonService.timestamp_to_str(int(startTime))
  604. endTime = CommonService.timestamp_to_str(int(endTime))
  605. startYear, startMonth, startDay = \
  606. str(startTime).split('-')[0], str(startTime).split('-')[1], str(startTime).split('-')[2]
  607. # 结束时间
  608. endYear, endMonth, endDay = str(endTime).split('-')[0], str(endTime).split('-')[1], str(endTime).split('-')[
  609. 2]
  610. if unit == 'week' or unit == 'month':
  611. startTime = parse('%s-%s-%s' % (startYear, startMonth, startDay))
  612. endTime = parse('%s-%s-%s' % (endYear, endMonth, endDay))
  613. time_list = CommonService.cutting_time(startTime, endTime, time_unit='day')
  614. elif unit == 'year':
  615. startYear, startMonth = int(startTime.split('-')[0]), int(startTime.split('-')[1])
  616. endYear, endMonth = int(endTime.split('-')[0]), int(endTime.split('-')[1])
  617. # 获取下个月的第一天
  618. if startMonth == 12:
  619. startYear += 1
  620. startMonth = 1
  621. else:
  622. startMonth += 1
  623. # 计算(开始月,结束月)
  624. startTime = parse('%s-%s-01 00:00:00' % (str(startYear), str(startMonth)))
  625. # 获取上个月最后一天
  626. if endMonth == 1:
  627. endYear -= 1
  628. endMonth = 12
  629. else:
  630. endMonth -= 1
  631. endDay = calendar.monthrange(endYear, endMonth)[1]
  632. endTime = parse('%s-%s-%s 23:59:59' % (str(endYear), str(endMonth), endDay))
  633. time_list = CommonService.cutting_time(startTime, endTime, time_unit='month')
  634. # 开始月的时间区间
  635. startMonth_time = CommonService.str_to_timestamp(str(startTime))
  636. # 结束月的时间区间
  637. endMonth_time = CommonService.str_to_timestamp(str(endTime))
  638. diction['startMonth_time'] = startMonth_time
  639. diction['endMonth_time'] = endMonth_time
  640. diction['time_list'] = time_list
  641. return diction
  642. @staticmethod
  643. def del_socket_schedule(request_dict, response, user_id):
  644. """
  645. 批量刪除排程
  646. @param request_dict: 请求参数
  647. @param user_id: 用戶user_id
  648. @request_dict ids: 排程id
  649. @request_dict serialNumber: 设备序列号
  650. @param response: 响应对象
  651. @return: response
  652. """
  653. try:
  654. with transaction.atomic():
  655. ids = request_dict.get('ids', None)
  656. serial_number = request_dict.get('serialNumber', None)
  657. if not all({ids, serial_number}):
  658. return response.json(444, {'error param': 'id or serialNumber'})
  659. device_info_qs = Device_Info.objects.filter(userID_id=user_id, serial_number=serial_number)
  660. if not device_info_qs.exists():
  661. return response.json(173)
  662. socket_schedule_qs = SocketSchedule.objects.filter(id__in=ids.split(','))
  663. if not socket_schedule_qs.exists():
  664. return response.json(173)
  665. # 发布MQTT消息通知设备删除排程任务
  666. # for val in socket_schedule_qs:
  667. # if val.task_status:
  668. # switch_status = 1 if val.switch_status else 0
  669. # result = SmartSocketView.send_socket_schedule(val.serial_number, val.id, val.time_type,
  670. # val.start_time, val.end_time,
  671. # val.repeat, switch_status, 0)
  672. # LOGGER.info('删除排程发布结果:{}'.format(result))
  673. socket_schedule_qs.delete()
  674. return response.json(0)
  675. except Exception as e:
  676. LOGGER.info('插座排程删除数据异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  677. return response.json(177)
  678. @classmethod
  679. def get_unit_scene(cls, request_dict, response):
  680. """
  681. 查詢設備每日/月用電量
  682. @request_dict serialNumber: 设备序列号
  683. @request_dict startTime: 开始时间
  684. @request_dict endTime: 结束时间
  685. @param request_dict: 请求参数
  686. @param response: 响应对象
  687. @return: response
  688. """
  689. serial_number = request_dict.get('serialNumber', None)
  690. unit = request_dict.get('unit', None)
  691. startTime = request_dict.get('startTime', None)
  692. endTime = request_dict.get('endTime', None)
  693. if not all([unit, startTime, endTime, serial_number]):
  694. return response.json(500, {'errno': 'unit or startTime or endTime or serialNumber'})
  695. try:
  696. socket_power_qs = SocketPowerStatistics.objects.filter(serial_number=serial_number). \
  697. values('electricity', 'accumulated_time', 'power', 'created_time')
  698. if not socket_power_qs.exists():
  699. return response.json(0, {})
  700. # 时间和功耗
  701. data = {}
  702. new_list = []
  703. socket_qs = socket_power_qs.filter(created_time__gte=startTime, created_time__lt=endTime).aggregate(
  704. electricity=Sum('electricity'), accumulatedTime=Sum('accumulated_time'))
  705. data['electricityTimeAll'] = round(socket_qs['electricity'], 2) if socket_qs[
  706. 'electricity'] else 0
  707. data['accumulatedTimeAll'] = socket_qs['accumulatedTime'] if socket_qs['accumulatedTime'] else 0
  708. # 分割时间
  709. diction = cls.splittings_time(startTime, endTime, unit)
  710. if unit == 'year':
  711. # 开始月
  712. socket_qs = socket_power_qs.filter(created_time__gte=startTime,
  713. created_time__lt=diction['startMonth_time']).aggregate(
  714. electricity=Sum('electricity'), accumulatedTime=Sum('accumulated_time'))
  715. electricity = socket_qs['electricity'] if socket_qs[
  716. 'electricity'] else 0
  717. # 標記月日
  718. subscript = cls.get_subscript(unit, startTime)
  719. new_list.append({
  720. 'subscript': subscript,
  721. 'time': int(startTime),
  722. 'electricity': round(electricity, 2)
  723. })
  724. # 查询天月
  725. for item in diction['time_list']:
  726. socket_qs = socket_power_qs.filter(created_time__gte=item[0],
  727. created_time__lt=item[1]).aggregate(
  728. electricity=Sum('electricity'))
  729. electricity = socket_qs['electricity'] if socket_qs[
  730. 'electricity'] else 0
  731. # 標記月日
  732. subscript = cls.get_subscript(unit, item[0])
  733. new_list.append({
  734. 'subscript': subscript,
  735. 'time': item[0],
  736. 'electricity': round(electricity, 2)
  737. })
  738. if unit == 'year':
  739. # 结束月
  740. socket_qs = socket_power_qs.filter(created_time__gte=diction['endMonth_time'],
  741. created_time__lt=endTime).aggregate(
  742. electricity=Sum('electricity'))
  743. electricity = socket_qs['electricity'] if socket_qs[
  744. 'electricity'] else 0
  745. # 標記月日
  746. subscript = cls.get_subscript(unit, endTime)
  747. new_list.append({
  748. 'subscript': subscript,
  749. 'time': int(endTime),
  750. 'electricity': round(electricity, 2)
  751. })
  752. # 降序排序
  753. # new_list.sort(key=lambda k: k["time"], reverse=True)
  754. data['week_or_month_or_year'] = new_list
  755. return response.json(0, data)
  756. except Exception as e:
  757. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  758. @classmethod
  759. def get_subscript(cls, unit, time_stamp):
  760. """
  761. 標記月日
  762. @param unit: 时间单位
  763. @param time_stamp: 时间戳
  764. @return: subscript
  765. """
  766. time_tuple = time.localtime(int(time_stamp)) # 把时间戳转换成时间元祖
  767. time_tuple = time.strftime('%Y-%m-%d-%w', time_tuple) # 把时间元祖转换成格式化好的时间
  768. if unit == 'week' or unit == 'year':
  769. if unit == 'week':
  770. subscript = int(str(time_tuple).split('-')[3])
  771. return subscript
  772. else:
  773. Year, Month, Day = int(time_tuple.split('-')[0]), int(time_tuple.split('-')[1]), int(
  774. time_tuple.split('-')[2])
  775. subscript = datetime.date(Year, Month, Day).month
  776. subscript -= 1
  777. return subscript
  778. else:
  779. Year, Month, Day = int(time_tuple.split('-')[0]), int(time_tuple.split('-')[1]), int(
  780. time_tuple.split('-')[2])
  781. subscript = datetime.date(Year, Month, Day).day
  782. subscript -= 1
  783. return subscript
  784. @staticmethod
  785. def get_schedule_data(request_dict, response):
  786. """
  787. 查询插座日志记录日期
  788. @request_dict serialNumber: 设备序列号
  789. @param request_dict: 请求参数
  790. @param response: 响应对象
  791. @return: response
  792. """
  793. serial_number = request_dict.get('serialNumber', None)
  794. if not serial_number:
  795. return response.json(444, {'error': 'serialNumber'})
  796. try:
  797. socket_schedule_qs = SceneLog.objects.extra(
  798. select={'date': "FROM_UNIXTIME(created_time,'%%Y-%%m-%%d')"}).values('date').filter(
  799. device_id=serial_number).annotate(count=Count('created_time')).order_by('-date')[:31]
  800. schedule_date_list = []
  801. for socket_schedule in socket_schedule_qs:
  802. schedule_date_list.append({
  803. 'timestamp': CommonService.str_to_timestamp(socket_schedule['date'], '%Y-%m-%d'),
  804. 'count': socket_schedule['count'],
  805. 'format': socket_schedule['date']
  806. })
  807. return response.json(0, schedule_date_list)
  808. except Exception as e:
  809. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  810. @classmethod
  811. def alexa_socket_switch(cls, request_dict, response):
  812. """
  813. alexa智能开关
  814. @request_dict serialNumber: 设备序列号
  815. @request_dict status: 开关状态
  816. @param request_dict: 请求参数
  817. @param response: 响应对象
  818. @return: response
  819. """
  820. serialNumber = request_dict.get('serial_number', None)
  821. status = request_dict.get('power_controller', None)
  822. if not all([serialNumber, status]):
  823. return response.json(444)
  824. # 同步数据库并下发MQTT消息到插座设备
  825. try:
  826. socket_info_qs = SocketInfo.objects.get(serial_number=serialNumber, type_switch=0)
  827. device_id = socket_info_qs.device_id
  828. # 判断设备是否在线
  829. thing_name = 'LC_' + serialNumber
  830. if 'test' == CONFIG_INFO or CONFIG_INFO == 'cn':
  831. iot_data_plane = AWSIoTDataPlaneService(AWS_IOT_SES_ACCESS_CHINA_ID,
  832. AWS_IOT_SES_ACCESS_CHINA_SECRET,
  833. AWS_IOT_SES_ACCESS_CHINA_REGION)
  834. elif 'us' == CONFIG_INFO:
  835. iot_data_plane = AWSIoTDataPlaneService(AWS_IOT_SES_ACCESS_FOREIGN_ID,
  836. AWS_IOT_SES_ACCESS_FOREIGN_SECRET,
  837. AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA)
  838. else:
  839. iot_data_plane = AWSIoTDataPlaneService(AWS_IOT_SES_ACCESS_FOREIGN_ID,
  840. AWS_IOT_SES_ACCESS_FOREIGN_SECRET,
  841. AWS_IOT_SES_ACCESS_FOREIGN_REGION_EUROPE)
  842. res = iot_data_plane.get_thing_shadow(thing_name)
  843. if not res:
  844. return response.json(177)
  845. if not res['state']['reported']['online']:
  846. return response.json(177)
  847. result = cls.save_socket_switch(device_id, serialNumber, int(status))
  848. if not result:
  849. return response.json(177)
  850. return response.json(0)
  851. except Exception as e:
  852. print(e)
  853. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  854. @classmethod
  855. def get_socket_state(cls, request_dict, response):
  856. """
  857. 获取alexa智能开关状态
  858. @request_dict serialNumber: 设备序列号
  859. @request_dict status: 开关状态
  860. @param request_dict: 请求参数
  861. @param response: 响应对象
  862. @return: response
  863. """
  864. serial_number = request_dict.get('serial_number', None)
  865. if not all([serial_number]):
  866. return response.json(444)
  867. # 同步数据库并下发MQTT消息到插座设备
  868. try:
  869. socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number).values('status')
  870. if socket_info_qs.exists():
  871. res = {
  872. 'power_state': socket_info_qs[0]['status']
  873. }
  874. return response.json(0, res)
  875. return response.json(173)
  876. except Exception as e:
  877. print(e)
  878. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  879. @classmethod
  880. def delete_alexa_socket(cls, serial_number):
  881. url = 'https://www.zositech.xyz/deviceStatus/deleteSwitch'
  882. data = {
  883. 'serial_number': serial_number
  884. }
  885. try:
  886. requests.post(url=url, data=data, timeout=5)
  887. except Exception as e:
  888. print(repr(e))
  889. @classmethod
  890. def socket_msg_push(cls, request_dict, response):
  891. """
  892. 智能插座开关状态推送
  893. """
  894. try:
  895. serial_number = request_dict.get('serialNumber', None)
  896. device_time = request_dict.get('deviceTime', None)
  897. status = request_dict.get('status', None)
  898. if not all([serial_number, status, device_time]):
  899. return response.json(444)
  900. status = int(status)
  901. now_time = int(device_time) if device_time else int(time.time())
  902. # 获取主用户设备id
  903. log_dict = {
  904. 'status': status,
  905. 'device_id': serial_number,
  906. 'created_time': now_time,
  907. }
  908. SceneLog.objects.create(**log_dict)
  909. LOGGER.info('成功接收并保存,插座序列号{},状态:{}'.format(serial_number, status))
  910. return response.json(0)
  911. except Exception as e:
  912. print(repr(e))
  913. LOGGER.info('---插座开关日志推送接口异常--- {}'.format(repr(e)))
  914. return response.json(500, repr(e))
  915. @classmethod
  916. def update_socket(cls, serial_number, device_name, user_id):
  917. url = 'https://www.zositech.xyz/deviceStatus/addOrUpdateSwitch'
  918. try:
  919. region_id = CommonService.confirm_region_id()
  920. data = {
  921. 'nick_name': device_name,
  922. 'serial_number': serial_number,
  923. 'user_id': user_id,
  924. }
  925. if region_id == 3:
  926. data['region'] = 'US'
  927. elif region_id == 4:
  928. data['region'] = 'EU'
  929. elif region_id == 5:
  930. data['region'] = 'CN'
  931. requests.post(url=url, data=data, timeout=5)
  932. except Exception as e:
  933. print(repr(e))