|
@@ -0,0 +1,886 @@
|
|
|
+# -*- encoding: utf-8 -*-
|
|
|
+"""
|
|
|
+@File : SmartSocketController.py
|
|
|
+@Time : 2023/3/17 11:52
|
|
|
+@Author : stephen
|
|
|
+@Email : zhangdongming@asj6.wecom.work
|
|
|
+@Software: PyCharm
|
|
|
+"""
|
|
|
+import calendar
|
|
|
+import datetime
|
|
|
+import logging
|
|
|
+import time
|
|
|
+from decimal import Decimal
|
|
|
+
|
|
|
+import requests
|
|
|
+from dateutil.parser import parse
|
|
|
+from django.db import transaction
|
|
|
+from django.db.models import Sum, Count
|
|
|
+from django.http import QueryDict
|
|
|
+from django.views import View
|
|
|
+
|
|
|
+from Model.models import SocketInfo, SocketSchedule, Device_Info, SocketPowerStatistics, SceneLog, FamilyRoomDevice, \
|
|
|
+ CountryModel
|
|
|
+from Object.ResponseObject import ResponseObject
|
|
|
+from Object.utils import LocalDateTimeUtil
|
|
|
+from Service.CommonService import CommonService
|
|
|
+
|
|
|
+LOGGER = logging.getLogger('info')
|
|
|
+SOCKET_TOPIC_NAME = 'loocam/smart-socket/{}' # 插座发布消息主题(因设备当前版本只能订阅一个主题)
|
|
|
+
|
|
|
+
|
|
|
+class SmartSocketView(View):
|
|
|
+ def get(self, request, *args, **kwargs):
|
|
|
+ request.encoding = 'utf-8'
|
|
|
+ operation = kwargs.get('operation')
|
|
|
+ return self.validation(request.GET, request, operation)
|
|
|
+
|
|
|
+ def post(self, request, *args, **kwargs):
|
|
|
+ request.encoding = 'utf-8'
|
|
|
+ operation = kwargs.get('operation')
|
|
|
+ return self.validation(request.POST, request, operation)
|
|
|
+
|
|
|
+ def delete(self, request, *args, **kwargs):
|
|
|
+ request.encoding = 'utf-8'
|
|
|
+ operation = kwargs.get('operation')
|
|
|
+ delete = QueryDict(request.body)
|
|
|
+ if not delete:
|
|
|
+ delete = request.GET
|
|
|
+ return self.validation(delete, request, operation)
|
|
|
+
|
|
|
+ def put(self, request, *args, **kwargs):
|
|
|
+ request.encoding = 'utf-8'
|
|
|
+ operation = kwargs.get('operation')
|
|
|
+ put = QueryDict(request.body)
|
|
|
+ return self.validation(put, request, operation)
|
|
|
+
|
|
|
+ def validation(self, request_dict, request, operation):
|
|
|
+ ResponseObject('cn')
|
|
|
+ if operation == 'savePowerStatistics': # 保存电量上报统计
|
|
|
+ return self.save_power_statistics(request_dict, ResponseObject('cn'))
|
|
|
+ elif operation == 'reset': # 设备复位
|
|
|
+ return self.socket_reset(request_dict, ResponseObject('cn'))
|
|
|
+ elif operation == 'alexa-socket-switch': # 新增alexa智能开关
|
|
|
+ return self.alexa_socket_switch(request_dict, ResponseObject('cn'))
|
|
|
+ elif operation == 'getSocketState': # 获取alexa智能开关状态
|
|
|
+ return self.get_socket_state(request_dict, ResponseObject('cn'))
|
|
|
+ token_code, user_id, response = CommonService \
|
|
|
+ .verify_token_get_user_id(request_dict, request)
|
|
|
+ if token_code != 0:
|
|
|
+ return response.json(token_code)
|
|
|
+ if operation == 'saveSwitch': # 添加插座开关
|
|
|
+ return self.save_switch(request_dict, response)
|
|
|
+ elif operation == 'saveCountDown': # 添加插座倒计时
|
|
|
+ return self.save_count_down(request_dict, response)
|
|
|
+ elif operation == 'saveSchedule': # 添加插座排程
|
|
|
+ return self.save_socket_schedule(request_dict, response)
|
|
|
+ elif operation == 'get-all-scene': # 统计智能插座电量
|
|
|
+ return self.get_all_scene(request_dict, response)
|
|
|
+ elif operation == 'get-socket-schedule': # 智能插座排程记录查询
|
|
|
+ return self.get_socket_schedule(request_dict, response)
|
|
|
+ elif operation == 'get-log': # 智能插座开关日志记录查询
|
|
|
+ return self.get_log(request_dict, response)
|
|
|
+ elif operation == 'del-socket-schedule': # 批量刪除排程
|
|
|
+ return self.del_socket_schedule(request_dict, response, user_id)
|
|
|
+ elif operation == 'get-unit-scene': # 查詢設備每日/月用電量
|
|
|
+ return self.get_unit_scene(request_dict, response)
|
|
|
+ elif operation == 'get-schedule-data': # 查询插座记录日期
|
|
|
+ return self.get_schedule_data(request_dict, response)
|
|
|
+ return response.json(404)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def socket_reset(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 智能插座复位删除数据
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ with transaction.atomic():
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ if not serial_number:
|
|
|
+ return response.json(444)
|
|
|
+ socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number, type_switch=0)
|
|
|
+ if not socket_info_qs.exists():
|
|
|
+ return response.json(173)
|
|
|
+ device_id = socket_info_qs.first().device_id
|
|
|
+ if socket_info_qs.first().status == 1: # 设备电源开时 恢复为关闭状态
|
|
|
+ socket_info_qs.update(status=0, updated_time=int(time.time()))
|
|
|
+ # 删除插座倒计时
|
|
|
+ SocketInfo.objects.filter(device_id=device_id).delete()
|
|
|
+ # 删除插座电量统计
|
|
|
+ SocketPowerStatistics.objects.filter(device_id=device_id).delete()
|
|
|
+ # 删除插座排程
|
|
|
+ SocketSchedule.objects.filter(device_id=device_id).delete()
|
|
|
+ # 删除插座开关日志
|
|
|
+ SceneLog.objects.filter(device_id=serial_number).delete()
|
|
|
+ # 删除设备管理家庭接口
|
|
|
+ FamilyRoomDevice.objects.filter(device_id=device_id).delete()
|
|
|
+ # 删除设备
|
|
|
+ Device_Info.objects.filter(id=device_id).delete()
|
|
|
+ # alexa删除插座
|
|
|
+ cls.delete_alexa_socket(serial_number)
|
|
|
+ LOGGER.info('智能插座{}设备已复位'.format(serial_number))
|
|
|
+ return response.json(0)
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('插座复位删除数据异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return response.json(177)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def save_power_statistics(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 保存设备上报电量统计
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ watt = request_dict.get('electricity', 0.00) # 功率
|
|
|
+ power = request_dict.get('power', 0.00) # 负载功率
|
|
|
+ # 在线时长秒
|
|
|
+ accumulated_time = request_dict.get('accumulatedTime', None)
|
|
|
+ device_time = request_dict.get('deviceTime', None)
|
|
|
+ LOGGER.info('{}上报电量统计data:{}'.format(serial_number, request_dict))
|
|
|
+ if not all([serial_number, watt, power, accumulated_time, device_time]):
|
|
|
+ return response.json(444)
|
|
|
+ watt = float(watt)
|
|
|
+ power = float(power)
|
|
|
+ accumulated_time = int(accumulated_time)
|
|
|
+ # 判断上报数据是否为负数
|
|
|
+ if watt < 0 or power < 0 or accumulated_time < 0:
|
|
|
+ LOGGER.info('{}插座上报负值data:{}'.format(serial_number, request_dict))
|
|
|
+ return response.json(177)
|
|
|
+ now_time = int(time.time())
|
|
|
+ start_time, end_time = LocalDateTimeUtil.get_today_date(True)
|
|
|
+ # 查询当前序列号当天是否有上传过电量统计
|
|
|
+ power_qs = SocketPowerStatistics.objects.filter(serial_number=serial_number,
|
|
|
+ created_time__gt=start_time,
|
|
|
+ created_time__lte=end_time)
|
|
|
+ data = {
|
|
|
+ 'power': power,
|
|
|
+ 'updated_time': now_time,
|
|
|
+ 'watt': watt
|
|
|
+ }
|
|
|
+ if not power_qs.exists(): # 添加插座上报电量统计
|
|
|
+ socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number).values('device_id')
|
|
|
+ if not socket_info_qs.exists():
|
|
|
+ return response.json(173)
|
|
|
+ data['device_id'] = socket_info_qs[0]['device_id']
|
|
|
+ data['created_time'] = now_time
|
|
|
+ data['serial_number'] = serial_number
|
|
|
+ data['electricity'] = cls.calculated_power(watt, accumulated_time)
|
|
|
+ data['accumulated_time'] = accumulated_time
|
|
|
+ SocketPowerStatistics.objects.create(**data)
|
|
|
+ return response.json(0)
|
|
|
+ power_vo = power_qs.first()
|
|
|
+ # 累加在线时间目前是以分钟为单位
|
|
|
+ data['accumulated_time'] = power_vo.accumulated_time + accumulated_time
|
|
|
+ # kwh 千瓦时
|
|
|
+ kilowatt_hour = cls.calculated_power(watt, accumulated_time)
|
|
|
+ data['electricity'] = kilowatt_hour + float(power_vo.electricity)
|
|
|
+ # 所消耗累计功率
|
|
|
+ data['watt'] = float(power_vo.watt) + watt
|
|
|
+ # 更新当天电量统计
|
|
|
+ power_qs.update(**data)
|
|
|
+ return response.json(0)
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('智能插座电量存库异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return response.json(177)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def calculated_power(watt, minute):
|
|
|
+ """
|
|
|
+ 通过每分钟所消耗的功率(瓦)得到千瓦时kwh
|
|
|
+ """
|
|
|
+ if watt == 0 or watt < 0.1 or minute == 0:
|
|
|
+ return 0.00
|
|
|
+ hours = minute / 3600
|
|
|
+ kilowatt_hour = watt * hours / 1000
|
|
|
+ LOGGER.info('瓦计算得到千瓦时结果{}'.format(kilowatt_hour))
|
|
|
+ return kilowatt_hour
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_serial_number_by_device_id(deviceId):
|
|
|
+ """
|
|
|
+ 根据设备ID获取序列号
|
|
|
+ """
|
|
|
+ device_info = Device_Info.objects.get(id=deviceId)
|
|
|
+ return device_info.serial_number
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def save_switch(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 添加开关
|
|
|
+ """
|
|
|
+ device_id = request_dict.get('deviceId', None)
|
|
|
+ status = request_dict.get('status', None)
|
|
|
+ if not all([device_id, status]):
|
|
|
+ return response.json(444)
|
|
|
+ serial_number = cls.get_serial_number_by_device_id(device_id)
|
|
|
+ # 保存数据库并下发MQTT消息到插座设备
|
|
|
+ result = cls.save_socket_switch(device_id, serial_number, int(status))
|
|
|
+ if not result:
|
|
|
+ return response.json(177)
|
|
|
+ return response.json(0)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def save_socket_switch(device_id, serial_number, status, type_switch=0):
|
|
|
+ """
|
|
|
+ 保存插座开关信息
|
|
|
+ @param device_id: 设备ID
|
|
|
+ @param serial_number: 序列号
|
|
|
+ @param status: 状态 0关,1开
|
|
|
+ @param type_switch: 0:总开关,1倒计时开关
|
|
|
+ @return: True | False
|
|
|
+ """
|
|
|
+ if not device_id:
|
|
|
+ return False
|
|
|
+ socket_info_qs = SocketInfo.objects.filter(device_id=device_id, type_switch=type_switch)
|
|
|
+ LOGGER.info('{}进入插座电源开关OR倒计时,类型:{}'.format(serial_number, type_switch))
|
|
|
+ now_time = int(time.time())
|
|
|
+ try:
|
|
|
+ with transaction.atomic():
|
|
|
+ # 创建插座开关信息
|
|
|
+ if not socket_info_qs.exists():
|
|
|
+ socket_dict = {"device_id": device_id,
|
|
|
+ "serial_number": serial_number,
|
|
|
+ "status": status,
|
|
|
+ "type_switch": type_switch,
|
|
|
+ "created_time": now_time,
|
|
|
+ "updated_time": now_time,
|
|
|
+ "online": True}
|
|
|
+ SocketInfo.objects.create(**socket_dict)
|
|
|
+ return True
|
|
|
+ if socket_info_qs.first().status != status:
|
|
|
+ socket_info_qs.update(status=status, updated_time=now_time)
|
|
|
+ # 主题名称
|
|
|
+ topic_name = SOCKET_TOPIC_NAME.format(serial_number)
|
|
|
+ # 发布消息内容
|
|
|
+ msg = {'type': 1, 'data': {'deviceSwitch': status}}
|
|
|
+ result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
|
|
|
+ LOGGER.info('{}智能插座开关设置发布MQTT消息结果{}'.format(serial_number, result))
|
|
|
+ return True
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('智能插座异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return False
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def save_count_down(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 添加倒计时
|
|
|
+ """
|
|
|
+ device_id = request_dict.get('deviceId', None)
|
|
|
+ status = request_dict.get('status', None)
|
|
|
+ start = request_dict.get('start', None)
|
|
|
+ count_down_time = request_dict.get('countDownTime', None)
|
|
|
+ if not all([device_id, status, count_down_time]):
|
|
|
+ return response.json(444)
|
|
|
+ serial_number = cls.get_serial_number_by_device_id(device_id)
|
|
|
+ # 保存数据库并下发MQTT消息到插座设备
|
|
|
+ result = cls.save_socket_count_down(device_id, serial_number, int(status), int(start), int(count_down_time))
|
|
|
+ if not result:
|
|
|
+ return response.json(177)
|
|
|
+ return response.json(0)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def save_socket_count_down(device_id, serial_number, status, start, count_down_time, type_switch=1):
|
|
|
+ """
|
|
|
+ 保存插座倒计时信息
|
|
|
+ @param count_down_time: 倒计时时间戳
|
|
|
+ @param start: 是否启动倒计时 0:关闭,1:开始
|
|
|
+ @param device_id: 设备ID
|
|
|
+ @param serial_number: 序列号
|
|
|
+ @param status: 倒计时电源状态 0关,1开
|
|
|
+ @param type_switch: 0:总开关,1倒计时开关
|
|
|
+ @return:
|
|
|
+ """
|
|
|
+ if not device_id:
|
|
|
+ return False
|
|
|
+ socket_info_qs = SocketInfo.objects.filter(device_id=device_id, type_switch=type_switch)
|
|
|
+ now_time = int(time.time())
|
|
|
+ try:
|
|
|
+ with transaction.atomic():
|
|
|
+ # 创建插座倒计时信息
|
|
|
+ if not socket_info_qs.exists():
|
|
|
+ socket_dict = {"device_id": device_id,
|
|
|
+ "serial_number": serial_number,
|
|
|
+ "status": status,
|
|
|
+ "type_switch": type_switch,
|
|
|
+ "created_time": now_time,
|
|
|
+ "updated_time": now_time,
|
|
|
+ "online": True,
|
|
|
+ "start": True if start == 1 else False,
|
|
|
+ "count_down_time": count_down_time}
|
|
|
+ socket_info_qs = SocketInfo.objects.create(**socket_dict)
|
|
|
+ count_down_id = socket_info_qs.id
|
|
|
+ else:
|
|
|
+ socket_info_qs.update(status=status, count_down_time=count_down_time,
|
|
|
+ updated_time=now_time)
|
|
|
+ count_down_id = socket_info_qs.first().id
|
|
|
+ # 主题名称
|
|
|
+ topic_name = SOCKET_TOPIC_NAME.format(serial_number)
|
|
|
+ # 发布消息内容
|
|
|
+ msg = {'type': 2,
|
|
|
+ 'data': {'powerType': status,
|
|
|
+ 'countDownId': count_down_id,
|
|
|
+ 'time': count_down_time,
|
|
|
+ 'start': start}}
|
|
|
+ result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
|
|
|
+ LOGGER.info('智能插座倒计时发布MQTT消息结果{}'.format(result))
|
|
|
+ return True
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('智能插座异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return False
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def save_socket_schedule(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 插座添加排程
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ device_id = request_dict.get('deviceId', None)
|
|
|
+ task_type = request_dict.get('timeType', None)
|
|
|
+ start_time = request_dict.get('startTime', None)
|
|
|
+ end_time = request_dict.get('endTime', 0)
|
|
|
+ repeat = request_dict.get('repeat', None)
|
|
|
+ task_id = request_dict.get('taskId', None)
|
|
|
+ device_switch = request_dict.get('deviceSwitch', None)
|
|
|
+ task_switch = request_dict.get('taskSwitch', None)
|
|
|
+ if not all([task_type, start_time, end_time, repeat, device_switch, task_switch]):
|
|
|
+ return response.json(444)
|
|
|
+ device_switch = int(device_switch)
|
|
|
+ task_switch = int(task_switch)
|
|
|
+ now_time = int(time.time())
|
|
|
+ task_type = int(task_type)
|
|
|
+ end_time = int(end_time) if task_type == 2 else 0
|
|
|
+ data = {'time_type': task_type, 'start_time': int(start_time), 'repeat': int(repeat),
|
|
|
+ 'switch_status': True if device_switch == 1 else False,
|
|
|
+ 'task_status': True if task_switch == 1 else False}
|
|
|
+ serial_number = cls.get_serial_number_by_device_id(device_id)
|
|
|
+ if task_id: # 修改排程
|
|
|
+ task_id = int(task_id)
|
|
|
+ socket_schedule_qs = SocketSchedule.objects.filter(id=task_id)
|
|
|
+ if not socket_schedule_qs.exists():
|
|
|
+ return response.json(174)
|
|
|
+ if end_time:
|
|
|
+ data['end_time'] = end_time
|
|
|
+ data['updated_time'] = now_time
|
|
|
+ socket_schedule_qs.update(**data)
|
|
|
+ else:
|
|
|
+ # 查询是否已设置过当前排程
|
|
|
+ socket_s_qs = SocketSchedule.objects.filter(device_id=device_id,
|
|
|
+ start_time=int(start_time),
|
|
|
+ end_time=end_time,
|
|
|
+ time_type=task_type)
|
|
|
+ if socket_s_qs.exists():
|
|
|
+ return response.json(174)
|
|
|
+ schedule_count = SocketSchedule.objects.filter(device_id=device_id).count()
|
|
|
+ if schedule_count >= 20:
|
|
|
+ return response.json(10061)
|
|
|
+ # 添加排程
|
|
|
+ data['device_id'] = device_id
|
|
|
+ data['end_time'] = end_time
|
|
|
+ data['serial_number'] = serial_number
|
|
|
+ data['updated_time'] = now_time
|
|
|
+ data['created_time'] = now_time
|
|
|
+ socket_schedule = SocketSchedule.objects.create(**data)
|
|
|
+ task_id = socket_schedule.id
|
|
|
+ # 将排程任务下发给设备
|
|
|
+ cls.send_socket_schedule(serial_number, task_id, task_type, int(start_time),
|
|
|
+ end_time, int(repeat), device_switch,
|
|
|
+ task_switch)
|
|
|
+ return response.json(0)
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('智能插座异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return False
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def send_socket_schedule(serial_number, task_id, time_type, start_time, end_time, repeat, device_switch,
|
|
|
+ task_switch):
|
|
|
+ """
|
|
|
+ 排程下发设备
|
|
|
+ @param serial_number: 序列号
|
|
|
+ @param task_id: 当前排程任务id
|
|
|
+ @param time_type: 任务类型 0:设定时间,1:设定时间段
|
|
|
+ @param start_time: 开启时间
|
|
|
+ @param end_time: 结束时间
|
|
|
+ @param repeat: 重复日期
|
|
|
+ @param device_switch: 任务执行后期望设备状态,0:关闭,1:开启
|
|
|
+ @param task_switch: 任务执行状态 0:不执行,1:执行
|
|
|
+ @return: True | False
|
|
|
+ """
|
|
|
+ msg = {
|
|
|
+ 'type': 3,
|
|
|
+ 'data': {'taskId': task_id, 'timeType': time_type,
|
|
|
+ 'startTime': start_time, 'endTime': end_time,
|
|
|
+ 'repeat': repeat,
|
|
|
+ 'deviceSwitch': device_switch,
|
|
|
+ 'taskSwitch': task_switch}
|
|
|
+ }
|
|
|
+ # 主题名称
|
|
|
+ topic_name = SOCKET_TOPIC_NAME.format(serial_number)
|
|
|
+ result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
|
|
|
+ LOGGER.info('智能插座{}排程任务发布MQTT消息结果{}'.format(serial_number, result))
|
|
|
+ return result
|
|
|
+
|
|
|
+ # 以下是查询智能插座接口
|
|
|
+ @staticmethod
|
|
|
+ def get_all_scene(request_dict, response):
|
|
|
+ """
|
|
|
+ 统计智能插座电量
|
|
|
+ @request_dict serialNumber: 序列号
|
|
|
+ @request_dict unit: 时间单位
|
|
|
+ @param request_dict: 请求数据
|
|
|
+ @param response: 响应
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ # 确定是否会传值
|
|
|
+ if not all([serial_number]):
|
|
|
+ return response.json(444, {'error param': 'serialNumber'})
|
|
|
+ all_socket_power_qs = SocketPowerStatistics.objects.filter(serial_number=serial_number).values('electricity',
|
|
|
+ 'accumulated_time',
|
|
|
+ 'power',
|
|
|
+ 'created_time'). \
|
|
|
+ order_by('-created_time')
|
|
|
+ if not all_socket_power_qs.exists():
|
|
|
+ return response.json(0, {})
|
|
|
+ try:
|
|
|
+ data = {}
|
|
|
+ # 设备累计电量
|
|
|
+ all_electricity = all_socket_power_qs.aggregate(total=Sum('electricity'))
|
|
|
+ data['electricityAll'] = round(all_electricity['total'], 1)
|
|
|
+
|
|
|
+ # 本月电费
|
|
|
+ nowTime = int(time.time())
|
|
|
+ nowTime = CommonService.timestamp_to_str(nowTime)
|
|
|
+ year, month = str(nowTime).split('-')[0], str(nowTime).split('-')[1]
|
|
|
+ end = calendar.monthrange(int(year), int(month))[1]
|
|
|
+ startTime_now = parse('%s-%s-01 00:00:00' % (year, month))
|
|
|
+ endTime_now = parse('%s-%s-%s 23:59:59' % (year, month, end))
|
|
|
+ startTime_now = CommonService.str_to_timestamp(str(startTime_now))
|
|
|
+ endTime_now = CommonService.str_to_timestamp(str(endTime_now))
|
|
|
+ electricity = all_socket_power_qs.filter(created_time__gte=startTime_now,
|
|
|
+ created_time__lt=endTime_now).aggregate(
|
|
|
+ total=Sum('electricity'))
|
|
|
+ if electricity['total'] != None:
|
|
|
+ data['electricityMonth'] = round(electricity['total'], 1)
|
|
|
+ else:
|
|
|
+ data['electricityMonth'] = 0
|
|
|
+
|
|
|
+ # 获取当前日期
|
|
|
+ nowTime = int(time.time())
|
|
|
+ today = datetime.date.today()
|
|
|
+ # 今天开始时间
|
|
|
+ today_start_time = int(time.mktime(time.strptime(str(today), '%Y-%m-%d')))
|
|
|
+ today_socket_power_qs = all_socket_power_qs.filter(created_time__gte=today_start_time,
|
|
|
+ created_time__lt=nowTime).values('electricity',
|
|
|
+ 'accumulated_time',
|
|
|
+ 'power',
|
|
|
+ 'created_time')
|
|
|
+ # 当天使用电量
|
|
|
+ data['electricityToday'] = round(today_socket_power_qs[0]['electricity'],
|
|
|
+ 1) if today_socket_power_qs.exists() else 0
|
|
|
+ # 当天累计时长
|
|
|
+ data['accumulated_time'] = today_socket_power_qs[0][
|
|
|
+ 'accumulated_time'] if today_socket_power_qs.exists() else 0
|
|
|
+ # 当前功率
|
|
|
+ data['power'] = round(today_socket_power_qs[0]['power'], 1) if today_socket_power_qs.exists() else 0
|
|
|
+
|
|
|
+ # 昨天使用电量
|
|
|
+ yesterday = today - datetime.timedelta(days=1)
|
|
|
+ # 昨天开始时间戳
|
|
|
+ yesterday_start_time = int(time.mktime(time.strptime(str(yesterday), '%Y-%m-%d')))
|
|
|
+ # 昨天结束时间戳
|
|
|
+ yesterday_end_time = int(time.mktime(time.strptime(str(today), '%Y-%m-%d'))) - 1
|
|
|
+ socket_qs = all_socket_power_qs.filter(created_time__gte=yesterday_start_time,
|
|
|
+ created_time__lt=yesterday_end_time).values('electricity')
|
|
|
+ if socket_qs.exists():
|
|
|
+ data['electricityYesterday'] = round(socket_qs[0]['electricity'], 1)
|
|
|
+ else:
|
|
|
+ data['electricityYesterday'] = 0
|
|
|
+ return response.json(0, data)
|
|
|
+ except Exception as e:
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_socket_schedule(request_dict, response):
|
|
|
+ """
|
|
|
+ 智能插座排程记录查询
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @request_dict page: 页数
|
|
|
+ @request_dict size: 条数
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ page = request_dict.get('pageNo', None)
|
|
|
+ size = request_dict.get('pageSize', None)
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+
|
|
|
+ if not all([page, size, serial_number]):
|
|
|
+ return response.json(444)
|
|
|
+ page, size = int(page), int(size)
|
|
|
+ socket_schedule_qs = SocketSchedule.objects.filter(serial_number=serial_number).values('switch_status',
|
|
|
+ 'start_time',
|
|
|
+ 'end_time',
|
|
|
+ 'repeat',
|
|
|
+ 'task_status',
|
|
|
+ 'time_type',
|
|
|
+ 'created_time',
|
|
|
+ 'updated_time',
|
|
|
+ 'device_id',
|
|
|
+ 'id').order_by(
|
|
|
+ '-created_time')[(page - 1) * size:page * size]
|
|
|
+ if not socket_schedule_qs.exists():
|
|
|
+ return response.json(0, [])
|
|
|
+ try:
|
|
|
+ schedule_list = []
|
|
|
+ for socket_schedule in socket_schedule_qs:
|
|
|
+ schedule_list.append({
|
|
|
+ 'taskId': socket_schedule['id'],
|
|
|
+ 'deviceID': socket_schedule['device_id'],
|
|
|
+ 'serialNumber': serial_number,
|
|
|
+ 'timeType': socket_schedule['time_type'],
|
|
|
+ 'startTime': socket_schedule['start_time'],
|
|
|
+ 'endTime': socket_schedule['end_time'],
|
|
|
+ 'switchStatus': socket_schedule['switch_status'],
|
|
|
+ 'taskStatus': socket_schedule['task_status'],
|
|
|
+ # 进制
|
|
|
+ 'repeat': socket_schedule['repeat'],
|
|
|
+ })
|
|
|
+ return response.json(0, schedule_list)
|
|
|
+ except Exception as e:
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_log(request_dict, response):
|
|
|
+ """
|
|
|
+ 智能插座开关日志记录查询
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @request_dict page: 页数
|
|
|
+ @request_dict size: 条数
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @request_dict startTime: 开始时间
|
|
|
+ @request_dict endTime: 结束时间
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ # 日誌擦護用序列號查詢
|
|
|
+ """
|
|
|
+ page = request_dict.get('page', None)
|
|
|
+ size = request_dict.get('size', None)
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ startTime = request_dict.get('startTime', None)
|
|
|
+ endTime = request_dict.get('endTime', None)
|
|
|
+ if not all([page, size, serial_number]):
|
|
|
+ return response.json(444, {'errno: page or size or serial_number'})
|
|
|
+ page, size = int(page), int(size)
|
|
|
+
|
|
|
+ try:
|
|
|
+ if startTime is None and endTime is None:
|
|
|
+ scene_log_qs = SceneLog.objects.filter(device_id=serial_number).values('tasks', 'status',
|
|
|
+ 'created_time').order_by(
|
|
|
+ '-created_time')[(page - 1) * size:page * size]
|
|
|
+ if not scene_log_qs.exists():
|
|
|
+ return response.json(0, [])
|
|
|
+ else:
|
|
|
+ scene_log_qs = SceneLog.objects.filter(device_id=serial_number, created_time__gte=startTime,
|
|
|
+ created_time__lt=endTime).values('tasks', 'status',
|
|
|
+ 'created_time').order_by(
|
|
|
+ '-created_time')[(page - 1) * size:page * size]
|
|
|
+ if not scene_log_qs.exists():
|
|
|
+ return response.json(0, [])
|
|
|
+ log_list = []
|
|
|
+ for scene_log in scene_log_qs:
|
|
|
+ data = {
|
|
|
+ 'serialNumber': serial_number,
|
|
|
+ 'tasks': scene_log['tasks'] if scene_log['tasks'] else '',
|
|
|
+ 'status': scene_log['status'],
|
|
|
+ 'createdTime': scene_log['created_time'],
|
|
|
+ }
|
|
|
+ log_list.append(data)
|
|
|
+ return response.json(0, log_list)
|
|
|
+ except Exception as e:
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def splittings_time(cls, startTime, endTime, unit):
|
|
|
+ """
|
|
|
+ 根據時間單位分割時間
|
|
|
+ """
|
|
|
+ diction = {}
|
|
|
+ time_list = []
|
|
|
+ # 开始时间
|
|
|
+ startTime = CommonService.timestamp_to_str(int(startTime))
|
|
|
+ endTime = CommonService.timestamp_to_str(int(endTime))
|
|
|
+ startYear, startMonth, startDay = \
|
|
|
+ str(startTime).split('-')[0], str(startTime).split('-')[1], str(startTime).split('-')[2]
|
|
|
+ # 结束时间
|
|
|
+ endYear, endMonth, endDay = str(endTime).split('-')[0], str(endTime).split('-')[1], str(endTime).split('-')[
|
|
|
+ 2]
|
|
|
+ if unit == 'week' or unit == 'month':
|
|
|
+ startTime = parse('%s-%s-%s' % (startYear, startMonth, startDay))
|
|
|
+ endTime = parse('%s-%s-%s' % (endYear, endMonth, endDay))
|
|
|
+ time_list = CommonService.cutting_time(startTime, endTime, time_unit='day')
|
|
|
+ elif unit == 'year':
|
|
|
+ startYear, startMonth = int(startTime.split('-')[0]), int(startTime.split('-')[1])
|
|
|
+ endYear, endMonth = int(endTime.split('-')[0]), int(endTime.split('-')[1])
|
|
|
+ # 获取下个月的第一天
|
|
|
+ if startMonth == 12:
|
|
|
+ startYear += 1
|
|
|
+ startMonth = 1
|
|
|
+ else:
|
|
|
+ startMonth += 1
|
|
|
+ # 计算(开始月,结束月)
|
|
|
+ startTime = parse('%s-%s-01 00:00:00' % (str(startYear), str(startMonth)))
|
|
|
+ # 获取上个月最后一天
|
|
|
+ if endMonth == 1:
|
|
|
+ endYear -= 1
|
|
|
+ endMonth = 12
|
|
|
+ else:
|
|
|
+ endMonth -= 1
|
|
|
+ endDay = calendar.monthrange(endYear, endMonth)[1]
|
|
|
+ endTime = parse('%s-%s-%s 23:59:59' % (str(endYear), str(endMonth), endDay))
|
|
|
+ time_list = CommonService.cutting_time(startTime, endTime, time_unit='month')
|
|
|
+ # 开始月的时间区间
|
|
|
+ startMonth_time = CommonService.str_to_timestamp(str(startTime))
|
|
|
+ # 结束月的时间区间
|
|
|
+ endMonth_time = CommonService.str_to_timestamp(str(endTime))
|
|
|
+ diction['startMonth_time'] = startMonth_time
|
|
|
+ diction['endMonth_time'] = endMonth_time
|
|
|
+ diction['time_list'] = time_list
|
|
|
+ return diction
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def del_socket_schedule(request_dict, response, user_id):
|
|
|
+ """
|
|
|
+ 批量刪除排程
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @param user_id: 用戶user_id
|
|
|
+ @request_dict ids: 排程id
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ with transaction.atomic():
|
|
|
+ ids = request_dict.get('ids', None)
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ if not all({ids, serial_number}):
|
|
|
+ return response.json(444, {'error param': 'id or serialNumber'})
|
|
|
+ device_info_qs = Device_Info.objects.filter(userID_id=user_id, serial_number=serial_number)
|
|
|
+ if not device_info_qs.exists():
|
|
|
+ return response.json(173)
|
|
|
+ socket_schedule_qs = SocketSchedule.objects.filter(id__in=ids.split(','))
|
|
|
+ if not socket_schedule_qs.exists():
|
|
|
+ return response.json(173)
|
|
|
+ # 发布MQTT消息通知设备删除排程任务
|
|
|
+ # for val in socket_schedule_qs:
|
|
|
+ # if val.task_status:
|
|
|
+ # switch_status = 1 if val.switch_status else 0
|
|
|
+ # result = SmartSocketView.send_socket_schedule(val.serial_number, val.id, val.time_type,
|
|
|
+ # val.start_time, val.end_time,
|
|
|
+ # val.repeat, switch_status, 0)
|
|
|
+ # LOGGER.info('删除排程发布结果:{}'.format(result))
|
|
|
+ socket_schedule_qs.delete()
|
|
|
+ return response.json(0)
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.info('插座排程删除数据异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return response.json(177)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_unit_scene(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 查詢設備每日/月用電量
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @request_dict startTime: 开始时间
|
|
|
+ @request_dict endTime: 结束时间
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ unit = request_dict.get('unit', None)
|
|
|
+ startTime = request_dict.get('startTime', None)
|
|
|
+ endTime = request_dict.get('endTime', None)
|
|
|
+ if not all([unit, startTime, endTime, serial_number]):
|
|
|
+ return response.json(500, {'errno': 'unit or startTime or endTime or serialNumber'})
|
|
|
+ try:
|
|
|
+ socket_power_qs = SocketPowerStatistics.objects.filter(serial_number=serial_number). \
|
|
|
+ values('electricity', 'accumulated_time', 'power', 'created_time')
|
|
|
+ if not socket_power_qs.exists():
|
|
|
+ return response.json(0, {})
|
|
|
+ # 时间和功耗
|
|
|
+ data = {}
|
|
|
+ new_list = []
|
|
|
+ socket_qs = socket_power_qs.filter(created_time__gte=startTime, created_time__lt=endTime).aggregate(
|
|
|
+ electricity=Sum('electricity'), accumulatedTime=Sum('accumulated_time'))
|
|
|
+ data['electricityTimeAll'] = round(socket_qs['electricity'], 2) if socket_qs[
|
|
|
+ 'electricity'] else 0
|
|
|
+ data['accumulatedTimeAll'] = socket_qs['accumulatedTime'] if socket_qs['accumulatedTime'] else 0
|
|
|
+
|
|
|
+ # 分割时间
|
|
|
+ diction = cls.splittings_time(startTime, endTime, unit)
|
|
|
+ if unit == 'year':
|
|
|
+ # 开始月
|
|
|
+ socket_qs = socket_power_qs.filter(created_time__gte=startTime,
|
|
|
+ created_time__lt=diction['startMonth_time']).aggregate(
|
|
|
+ electricity=Sum('electricity'), accumulatedTime=Sum('accumulated_time'))
|
|
|
+ electricity = socket_qs['electricity'] if socket_qs[
|
|
|
+ 'electricity'] else 0
|
|
|
+ # 標記月日
|
|
|
+ subscript = cls.get_subscript(unit, startTime)
|
|
|
+ new_list.append({
|
|
|
+ 'subscript': subscript,
|
|
|
+ 'time': int(startTime),
|
|
|
+ 'electricity': round(electricity, 2)
|
|
|
+ })
|
|
|
+ # 查询天月
|
|
|
+ for item in diction['time_list']:
|
|
|
+ socket_qs = socket_power_qs.filter(created_time__gte=item[0],
|
|
|
+ created_time__lt=item[1]).aggregate(
|
|
|
+ electricity=Sum('electricity'))
|
|
|
+ electricity = socket_qs['electricity'] if socket_qs[
|
|
|
+ 'electricity'] else 0
|
|
|
+ # 標記月日
|
|
|
+ subscript = cls.get_subscript(unit, item[0])
|
|
|
+ new_list.append({
|
|
|
+ 'subscript': subscript,
|
|
|
+ 'time': item[0],
|
|
|
+ 'electricity': round(electricity, 2)
|
|
|
+ })
|
|
|
+
|
|
|
+ if unit == 'year':
|
|
|
+ # 结束月
|
|
|
+ socket_qs = socket_power_qs.filter(created_time__gte=diction['endMonth_time'],
|
|
|
+ created_time__lt=endTime).aggregate(
|
|
|
+ electricity=Sum('electricity'))
|
|
|
+ electricity = socket_qs['electricity'] if socket_qs[
|
|
|
+ 'electricity'] else 0
|
|
|
+ # 標記月日
|
|
|
+ subscript = cls.get_subscript(unit, endTime)
|
|
|
+ new_list.append({
|
|
|
+ 'subscript': subscript,
|
|
|
+ 'time': int(endTime),
|
|
|
+ 'electricity': round(electricity, 2)
|
|
|
+ })
|
|
|
+ # 降序排序
|
|
|
+ # new_list.sort(key=lambda k: k["time"], reverse=True)
|
|
|
+ data['week_or_month_or_year'] = new_list
|
|
|
+ return response.json(0, data)
|
|
|
+ except Exception as e:
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_subscript(cls, unit, time_stamp):
|
|
|
+ """
|
|
|
+ 標記月日
|
|
|
+ @param unit: 时间单位
|
|
|
+ @param time_stamp: 时间戳
|
|
|
+ @return: subscript
|
|
|
+ """
|
|
|
+ time_tuple = time.localtime(int(time_stamp)) # 把时间戳转换成时间元祖
|
|
|
+ time_tuple = time.strftime('%Y-%m-%d-%w', time_tuple) # 把时间元祖转换成格式化好的时间
|
|
|
+ if unit == 'week' or unit == 'year':
|
|
|
+ if unit == 'week':
|
|
|
+ subscript = int(str(time_tuple).split('-')[3])
|
|
|
+ return subscript
|
|
|
+ else:
|
|
|
+ Year, Month, Day = int(time_tuple.split('-')[0]), int(time_tuple.split('-')[1]), int(
|
|
|
+ time_tuple.split('-')[2])
|
|
|
+ subscript = datetime.date(Year, Month, Day).month
|
|
|
+ subscript -= 1
|
|
|
+ return subscript
|
|
|
+ else:
|
|
|
+ Year, Month, Day = int(time_tuple.split('-')[0]), int(time_tuple.split('-')[1]), int(
|
|
|
+ time_tuple.split('-')[2])
|
|
|
+ subscript = datetime.date(Year, Month, Day).day
|
|
|
+ subscript -= 1
|
|
|
+ return subscript
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_schedule_data(request_dict, response):
|
|
|
+ """
|
|
|
+ 查询插座日志记录日期
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ serial_number = request_dict.get('serialNumber', None)
|
|
|
+ if not serial_number:
|
|
|
+ return response.json(444, {'error': 'serialNumber'})
|
|
|
+
|
|
|
+ try:
|
|
|
+ socket_schedule_qs = SceneLog.objects.extra(
|
|
|
+ select={'date': "FROM_UNIXTIME(created_time,'%%Y-%%m-%%d')"}).values('date').filter(
|
|
|
+ device_id=serial_number).annotate(count=Count('created_time')).order_by('-date')[:31]
|
|
|
+ schedule_date_list = []
|
|
|
+ for socket_schedule in socket_schedule_qs:
|
|
|
+ schedule_date_list.append({
|
|
|
+ 'timestamp': CommonService.str_to_timestamp(socket_schedule['date'], '%Y-%m-%d'),
|
|
|
+ 'count': socket_schedule['count'],
|
|
|
+ 'format': socket_schedule['date']
|
|
|
+ })
|
|
|
+ return response.json(0, schedule_date_list)
|
|
|
+ except Exception as e:
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def alexa_socket_switch(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ alexa智能开关
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @request_dict status: 开关状态
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ serialNumber = request_dict.get('serial_number', None)
|
|
|
+ status = request_dict.get('power_controller', None)
|
|
|
+ if not all([serialNumber, status]):
|
|
|
+ return response.json(444)
|
|
|
+ # 同步数据库并下发MQTT消息到插座设备
|
|
|
+ try:
|
|
|
+ socket_info_qs = SocketInfo.objects.get(serial_number=serialNumber)
|
|
|
+ device_id = socket_info_qs.device_id
|
|
|
+ result = cls.save_socket_switch(device_id, serialNumber, int(status))
|
|
|
+ if not result:
|
|
|
+ return response.json(177)
|
|
|
+ return response.json(0)
|
|
|
+ except Exception as e:
|
|
|
+ print(e)
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_socket_state(cls, request_dict, response):
|
|
|
+ """
|
|
|
+ 获取alexa智能开关状态
|
|
|
+ @request_dict serialNumber: 设备序列号
|
|
|
+ @request_dict status: 开关状态
|
|
|
+ @param request_dict: 请求参数
|
|
|
+ @param response: 响应对象
|
|
|
+ @return: response
|
|
|
+ """
|
|
|
+ serial_number = request_dict.get('serial_number', None)
|
|
|
+ if not all([serial_number]):
|
|
|
+ return response.json(444)
|
|
|
+ # 同步数据库并下发MQTT消息到插座设备
|
|
|
+ try:
|
|
|
+ socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number).values('status')
|
|
|
+ if socket_info_qs.exists():
|
|
|
+ res = {
|
|
|
+ 'power_state': socket_info_qs[0]['status']
|
|
|
+ }
|
|
|
+ return response.json(0, res)
|
|
|
+ return response.json(173)
|
|
|
+ except Exception as e:
|
|
|
+ print(e)
|
|
|
+ return response.json(500, repr(e))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def delete_alexa_socket(cls, serial_number):
|
|
|
+ url = 'https://www.zositech.xyz/deviceStatus/deleteSwitch'
|
|
|
+ data = {
|
|
|
+ 'serial_number': serial_number
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ requests.post(url=url, data=data, timeout=5)
|
|
|
+ except Exception as e:
|
|
|
+ print(repr(e))
|