# -*- encoding: utf-8 -*- """ @File : GatewayDeviceController.py @Time : 2022/6/6 13:50 @Author : stephen @Email : zhangdongming@asj6.wecom.work @Software: PyCharm """ import datetime import time import requests from django.db import transaction from django.db.models import Q from django.views.generic.base import View from Ansjer.Config.gatewaySensorConfig import SMART_SCENE_TOPIC from Ansjer.config import CONFIG_INFO, AWS_IOT_SES_ACCESS_CHINA_ID, AWS_IOT_SES_ACCESS_CHINA_SECRET, \ AWS_IOT_SES_ACCESS_CHINA_REGION, AWS_IOT_SES_ACCESS_FOREIGN_ID, AWS_IOT_SES_ACCESS_FOREIGN_SECRET, \ AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA from Ansjer.config import LOGGER from Controller.SensorGateway.EquipmentFamilyController import EquipmentFamilyView from Controller.SensorGateway.SmartSocketController import SmartSocketView from Model.models import FamilyRoomDevice, FamilyRoom, GatewaySubDevice, Device_Info, UserFamily, FamilyMember, \ UidSetModel, iotdeviceInfoModel, SmartScene, SceneLog, SocketInfo, SocketPowerStatistics, SocketSchedule, \ CountryModel from Object.AWS.AWSIoTDataPlaneUtil import AWSIoTDataPlaneService from Object.RedisObject import RedisObject from Object.ResponseObject import ResponseObject from Object.TokenObject import TokenObject from Service.CommonService import CommonService class GatewayDeviceView(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 validation(self, request_dict, request, operation): if operation == 'bind-serial-user': response = ResponseObject() return self.bind_serial_user(request_dict, response) token = TokenObject(request.META.get('HTTP_AUTHORIZATION')) lang = request_dict.get('lang', None) response = ResponseObject(lang) if lang else ResponseObject(token.lang) if token.code != 0: return response.json(token.code) user_id = token.userID # 网关设备 if operation == 'list': return self.gateway_device_list(request_dict, response) elif operation == 'del': return self.gateway_device_del(user_id, request_dict, response) elif operation == 'update': return self.gateway_device_update(user_id, request_dict, response) elif operation == 'my/family/list': return self.my_family_list(user_id, response) elif operation == 'location-setting': return self.device_location_setting(user_id, request_dict, response) elif operation == 'get-serial-user': return self.get_serial_user(user_id, request_dict, response) @classmethod def device_location_setting(cls, user_id, request_dict, response): """ 网关位置迁移 @param user_id: 用户id @param request_dict: 请求参数字典 @param response: 响应对象 @return: [] """ device_id = request_dict.get('deviceId', None) family_id = request_dict.get('familyId', None) room_id = request_dict.get('roomId', None) if not all([device_id, family_id]): return response.json(444) family_id = int(family_id) permission = EquipmentFamilyView.get_member_permission_details(user_id, family_id) if not permission or permission == '003': return response.json(404) try: with transaction.atomic(): family_room_device_qs = FamilyRoomDevice.objects.filter(device_id=device_id, family_id=family_id) if family_room_device_qs.exists(): family_room_device_qs = family_room_device_qs.filter(sub_device=0) if family_room_device_qs.exists() and room_id: family_room_device_qs.update(room_id=int(room_id)) else: user_family_qs = UserFamily.objects.filter(id=family_id) if not user_family_qs: return response.json(173) family_room_device_qs = FamilyRoomDevice.objects.filter(device_id=device_id) if family_room_device_qs.exists(): param_data = {'family_id': family_id, 'room_id': 0} if room_id: param_data['room_id'] = room_id family_room_device_qs.update(**param_data) return response.json(0) except Exception as e: print(e) return response.json(177, repr(e)) @classmethod def gateway_device_update(cls, user_id, request_dict, response): """ 网关设备修改名称 @param user_id: @param request_dict: @param response: @return: """ device_name = request_dict.get('deviceName') device_id = request_dict.get('deviceId') if not all([device_name, device_id]): return response.json(444) device_info_qs = Device_Info.objects.filter(userID_id=user_id, id=device_id) if device_info_qs.exists(): device_info_qs.update(NickName=device_name) uid_set_qs = UidSetModel.objects.filter(uid=device_info_qs[0].UID) if uid_set_qs.exists(): uid_set_qs.update(nickname=device_name) if device_info_qs[0].Type == 201: device_info_qs = device_info_qs.values('userID__region_country', 'serial_number') region = device_info_qs[0]['userID__region_country'] cls.update_socket(serial_number=device_info_qs[0]['serial_number'], device_name=device_name, user_id=user_id, region=region) return response.json(0) @classmethod def my_family_list(cls, user_id, response): """ 我的家庭列表 @param user_id: @param response: @return: """ user_family_qs = UserFamily.objects.filter(user_id=user_id).values() family_list = [] if user_family_qs.exists(): family_member_qs = FamilyMember.objects.filter(user_id=user_id, identity=1) \ .order_by('sort').values('identity', 'family_id', 'family__name', 'permission_id', 'permission__no', 'family__location', 'user__username', 'user__userIconUrl') items = EquipmentFamilyView.family_info_list(family_member_qs) return response.json(0, items) return response.json(0, family_list) @classmethod def gateway_device_del(cls, user_id, request_dict, response): """ 网关设备删除或删除子设备 @param user_id: @param request_dict: @param response: @return: """ device_id = request_dict.get('deviceId') family_id = request_dict.get('familyId') # 1 删除网关 否则删除子设备 sub_ids = request_dict.get('subIds') if not family_id: return response.json(444) permission = EquipmentFamilyView.get_member_permission_details(user_id, family_id) if not permission or permission == '003': return response.json(404) try: with transaction.atomic(): if device_id: device_qs = Device_Info.objects.filter(id=device_id) if device_qs.exists(): family_device_qs = FamilyRoomDevice.objects.filter(device_id=device_id) if family_device_qs.exists(): family_device_qs.delete() uid_set_qs = UidSetModel.objects.filter(uid=device_qs.first().UID) if uid_set_qs.exists(): uid_set_qs.delete() socket_info_qs = SocketInfo.objects.filter(device_id=device_id) if socket_info_qs.exists(): # 设备在不在线, 都发布重置 serial_number = device_qs.first().serial_number cls.reset_device(serial_number) socket_info_qs.delete() socket_power_qs = SocketPowerStatistics.objects.filter(device_id=device_id) if socket_power_qs.exists(): socket_power_qs.delete() socket_schedule_qs = SocketSchedule.objects.filter(device_id=device_id) if socket_schedule_qs.exists(): socket_schedule_qs.delete() scene_log_qs = SceneLog.objects.filter(device_id=serial_number) if scene_log_qs.exists(): scene_log_qs.delete() SmartSocketView.delete_alexa_socket(serial_number) # 如果有子设备,删除子设备和关联的场景数据 gateway_qs = GatewaySubDevice.objects.filter(device_id=device_id) if gateway_qs.exists(): sub_id_list = gateway_qs.values_list('id', flat=True) smart_scene_qs = SmartScene.objects.filter( Q(device_id=device_id) | Q(sub_device_id__in=sub_id_list)) else: smart_scene_qs = SmartScene.objects.filter(device_id=device_id) if smart_scene_qs.exists(): # 通知设备删除场景id smart_scene_info = smart_scene_qs.values('id') serial_number = device_qs.first().serial_number topic_name = SMART_SCENE_TOPIC.format(serial_number) for smart_scene in smart_scene_info: msg = { 'smart_scene_delete': int(smart_scene['id']) } success = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg) try: assert success except AssertionError: return response.json(10044) time.sleep(0.3) smart_scene_qs.delete() gateway_qs.delete() # 删除子设备 scene_log_qs = SceneLog.objects.filter(device_id=device_id) if scene_log_qs.exists(): scene_log_qs.delete() device_qs.delete() elif sub_ids: sub_ids = sub_ids.split(',') ids = [] for item in sub_ids: sub_id = int(item) ids.append(sub_id) sub_device_qs = GatewaySubDevice.objects.filter(id=sub_id).values('device_type', 'src_addr', 'device__serial_number') serial_number = sub_device_qs[0]['device__serial_number'] topic_name = SMART_SCENE_TOPIC.format(serial_number) device_type = sub_device_qs[0]['device_type'] if device_type == 216: # 删除智能按钮通知设备 msg = { 'sos_delete': int(sub_device_qs[0]['src_addr'], 16) } success = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg) try: assert success except AssertionError: return response.json(10044) time.sleep(0.3) family_device_qs = FamilyRoomDevice.objects.filter(sub_device__in=ids) if family_device_qs.exists(): family_device_qs.delete() gateway_sub_qs = GatewaySubDevice.objects.filter(id__in=ids) if gateway_sub_qs.exists(): gateway_sub_qs.delete() smart_scene_qs = SmartScene.objects.filter(sub_device_id__in=ids) if smart_scene_qs.exists(): smart_scene_info = smart_scene_qs.values('id') for smart_scene in smart_scene_info: # 通知设备删除场景id msg = { 'smart_scene_delete': int(smart_scene['id']) } success = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg) try: assert success except AssertionError: return response.json(10044) time.sleep(0.3) smart_scene_qs.delete() scene_log_qs = SceneLog.objects.filter(sub_device_id__in=ids) if scene_log_qs.exists(): scene_log_qs.delete() return response.json(0) except Exception as e: print(e) return response.json(177, repr(e)) @staticmethod def reset_device(serial_number): """ 下发消息到设备 """ try: # 更新影子为离线状态 data = { "state": {"reported": {"online": 0}} } iot_data_plane = None thing_name = 'LC_' + serial_number if 'test' == CONFIG_INFO or CONFIG_INFO == 'cn': iot_data_plane = AWSIoTDataPlaneService(AWS_IOT_SES_ACCESS_CHINA_ID, AWS_IOT_SES_ACCESS_CHINA_SECRET, AWS_IOT_SES_ACCESS_CHINA_REGION) elif 'us' == CONFIG_INFO: iot_data_plane = AWSIoTDataPlaneService(AWS_IOT_SES_ACCESS_FOREIGN_ID, AWS_IOT_SES_ACCESS_FOREIGN_SECRET, AWS_IOT_SES_ACCESS_FOREIGN_REGION_AMERICA) if iot_data_plane: res = iot_data_plane.update_thing_shadow(thing_name, data) LOGGER.info('删除插座更新设备影子状态{}'.format(res)) # 下发设备进行重置 SOCKET_TOPIC_NAME = 'loocam/smart-socket/{}' # 插座发布消息主题(因设备当前版本只能订阅一个主题) topic_name = SOCKET_TOPIC_NAME.format(serial_number) # 发布消息内容,重置设备 msg = {'type': 6, 'data': {'device_reset': 1}} 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 gateway_device_list(cls, request_dict, response): """ 网关设备列表 @param request_dict: @param response: @return: """ device_id = request_dict.get('deviceId', None) if not device_id: return response.json(444) device_qs = FamilyRoomDevice.objects.filter(device_id=device_id, sub_device=0) if not device_qs.exists(): return response.json(173) try: device_qs = device_qs.values('family_id', 'device_id', 'room_id', 'device__Type', 'device__NickName', 'device__UID', 'device__serial_number') device_qs = device_qs.first() room_id = device_qs['room_id'] family_id = device_qs['family_id'] gateway_room_name = '' if room_id: room_qs = FamilyRoom.objects.filter(id=room_id) gateway_room_name = room_qs.first().name if room_qs.exists() else '' iot_device_info_qs = iotdeviceInfoModel.objects.filter( serial_number=device_qs['device__serial_number'][0:6]) iot_data = {} if iot_device_info_qs.exists(): iot_device_Info = iot_device_info_qs.values('endpoint', 'token_iot_number') iot_data = { 'endpoint': iot_device_Info[0]['endpoint'], 'token_iot_number': iot_device_Info[0]['token_iot_number'] } gateway = { 'deviceId': device_qs['device_id'], 'deviceType': device_qs['device__Type'], 'deviceNickName': device_qs['device__NickName'], 'UID': device_qs['device__UID'], 'serialNumber': device_qs['device__serial_number'], 'roomName': gateway_room_name, 'iot': iot_data, 'roomId': room_id, 'familyId': family_id, 'power': 0, 'electricity': 0, 'countDownTime': 0, 'socketStatus': False, 'online': False, 'accumulatedTime': 0, 'start': False, } if device_qs['device__Type'] == 201: socket_info_qs = SocketInfo.objects.filter(device_id=device_id).values('online', 'type_switch', 'status', 'count_down_time', 'start') if not socket_info_qs.exists(): return response.json(173) socket_data = cls.smart_socket(device_id, socket_info_qs) gateway = {key: socket_data.get(key, gateway[key]) for key in gateway.keys()} family_device_qs = FamilyRoomDevice.objects.filter(device_id=device_id) family_device_qs = family_device_qs.filter(~Q(sub_device=0)).order_by('-created_time') sub_device = [] sub_id_list = [] if family_device_qs.exists(): family_device_qs = family_device_qs.values() for item in family_device_qs: sub_id = item['sub_device'] sub_id_list.append(sub_id) gateway_sub_qs = GatewaySubDevice.objects.filter(device_id=device_id, id=sub_id).values( 'id', 'device_type', 'nickname', 'src_addr', 'status', 'created_time', 'ieee_addr') if not gateway_sub_qs.exists(): continue room_id = item['room_id'] room_qs = FamilyRoom.objects.filter(id=room_id) gateway_room_name = room_qs.first().name if room_qs.exists() else '' gateway_sub_qs = gateway_sub_qs.first() sub_device.append({ 'gatewaySubId': gateway_sub_qs['id'], 'nickName': gateway_sub_qs['nickname'], 'deviceType': gateway_sub_qs['device_type'], 'srcAddr': gateway_sub_qs['src_addr'], 'status': gateway_sub_qs['status'], 'createdTime': gateway_sub_qs['created_time'], 'roomName': gateway_room_name, 'roomId': room_qs.first().id if room_qs.exists() else 0, 'ieeeAddr': gateway_sub_qs['ieee_addr'], 'familyId': family_id, }) scene_count = SmartScene.objects.filter(Q(device_id=device_id) | Q(sub_device_id__in=sub_id_list)).count() res = {'gateway': gateway, 'sub_device': sub_device, 'sub_device_count': len(sub_device), 'scene_count': scene_count} return response.json(0, res) except Exception as e: print(e.args) return response.json(500) @classmethod def smart_socket(cls, device_id, socket_info_qs): """ 查詢插座信息 """ nowTime = int(time.time()) today = datetime.date.today() # 今天开始时间 today_start_time = int(time.mktime(time.strptime(str(today), '%Y-%m-%d'))) data = { 'power': 0, 'electricity': 0, 'countDownTime': 0, 'accumulatedTime': 0, 'socketStatus': False, 'online': False, 'start': False, } # 插座信息 socket_info_qs = socket_info_qs.filter(device_id=device_id).values('online', 'type_switch', 'status', 'count_down_time', 'start') type_switch_list = [type_switch[v] for type_switch in socket_info_qs.values('type_switch') for v in type_switch] # 判断开关类型 0:总开关,1:倒计时开关 if len(type_switch_list) == 2: socket_info_qs = socket_info_qs.filter(type_switch=1) else: socket_info_qs = socket_info_qs.filter(type_switch=0) # 插座信息 data['socketStatus'] = socket_info_qs[0]['status'] data['start'] = socket_info_qs[0]['start'] data['online'] = socket_info_qs[0]['online'] data['countDownTime'] = socket_info_qs[0]['count_down_time'] if socket_info_qs[0][ 'count_down_time'] else 0 # 当前设备电量信息 socket_power_qs = SocketPowerStatistics.objects.filter(device_id=device_id, created_time__gte=today_start_time, created_time__lt=nowTime).values('accumulated_time', 'power', 'created_time', 'electricity'). \ order_by('-created_time') if not socket_power_qs.exists(): return data data['power'] = round(socket_power_qs[0]['power'], 1) data['electricity'] = round(socket_power_qs[0]['electricity'], 1) data['accumulatedTime'] = socket_power_qs[0]['accumulated_time'] return data @staticmethod def bind_serial_user(request_dict, response): """ 绑定序列号和用户id @param request_dict: 请求参数字典 @request_dict user_id: 用户id @request_dict serial_number: 序列号 @param response: 响应对象 @return: """ user_id = request_dict.get('user_id') serial_number = request_dict.get('serial_number') if not all([user_id, serial_number]): return response.json(444) try: redis_obj = RedisObject() result = redis_obj.set_data(user_id, serial_number, 300) if not result: return response.json(178) return response.json(0) except Exception as e: return response.json(500, repr(e)) @staticmethod def get_serial_user(user_id, request_dict, response): """ 获取用户id绑定网关序列号 @param user_id: 用户id @param request_dict: 请求参数字典 @param response: 响应对象 @return: """ try: redis_obj = RedisObject() serial_number = redis_obj.get_data(user_id) if not serial_number: return response.json(173) return response.json(0, {'serialNumber': serial_number}) except Exception as e: return response.json(500, repr(e)) @classmethod def update_socket(cls, serial_number, device_name, user_id, region): url = 'https://www.zositech.xyz/deviceStatus/addOrUpdateSwitch' try: country_qs = CountryModel.objects.filter(id=region).values('region__continent_code') data = { 'nick_name': device_name, 'serial_number': serial_number, 'user_id': user_id, 'region': country_qs[0]['region__continent_code'] if country_qs.exists() else 'EN' } requests.post(url=url, data=data, timeout=5) except Exception as e: print(repr(e)) # # ___====-_ _-====___ # _--^^^#####// \\#####^^^--_ # _-^##########// ( ) \\##########^-_ # -############// |\^^/| \\############- # _/############// (@::@) \\############\_ # /#############(( \\// ))#############\ # -###############\\ (oo) //###############- # -#################\\ / VV \ //#################- # -###################\\/ \//###################- # _#/|##########/\######( /\ )######/\##########|\#_ # |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \| # ` |/ V V ` V \#\| | | |/#/ V ' V V \| ' # ` ` ` ` / | | | | \ ' ' ' ' # ( | | | | ) # __\ | | | | /__ # (vvv(VVV)(VVV)vvv) # 神兽保佑 # 代码无BUG! #