Browse Source

智能开关

peng 1 year ago
parent
commit
bebf4fffb5

+ 1 - 1
Controller/SensorGateway/EquipmentFamilyController.py

@@ -1069,7 +1069,7 @@ class EquipmentFamilyView(View):
                             device_qs['data_joined'] = device_qs['data_joined'].strftime("%Y-%m-%d %H:%M:%S")
                         else:
                             device_qs['data_joined'] = ''
-                    if device_qs['Type'] == 200 or device_qs['Type'] == 201:
+                    if device_qs['Type'] in [200, 201, 202]:  # 网关、插座、开关
                         if device_qs['Type'] == 201:
                             socket_info_qs = SocketInfo.objects.filter(device_id=device_qs['id']).values(
                                 'status')

+ 214 - 94
Controller/SensorGateway/SmartSwitchController.py

@@ -4,11 +4,18 @@
 # @Time : 2023/7/10 11:20
 # @File: SmartSwitchController.py
 """
+import datetime
+import time
 
 from django.views import View
 
-from Model.models import SwitchInfo, SwitchDimmingSettings, SwitchChronopher, Device_Info, FamilyRoom
+from Model.models import SwitchInfo, SwitchDimmingSettings, SwitchChronopher, Device_Info, SceneLog
 from Service.CommonService import CommonService
+from Object.ApschedulerObject import ApschedulerObject
+from django.db import transaction
+from Ansjer.config import LOGGER
+
+APSCHEDULER_TOPIC_NAME = 'loocam/switch/time_scheduling/{}'  # 排程主题
 
 
 class SmartSwitchView(View):
@@ -28,14 +35,22 @@ class SmartSwitchView(View):
             return response.json(token_code)
         if operation == 'get-switch-info':  # 设备获取智能开关数据
             return self.get_switch_info(request_dict, response)
-        elif operation == 'get-switch-setting':  # 获取智能开关调光设置
-            return self.get_switch_setting(request_dict, response)
+        elif operation == 'get-dimming-setting':  # 获取智能开关调光设置
+            return self.get_dimming_setting(request_dict, response)
         elif operation == 'get-chronopher-setting':  # 获取定时计划
             return self.get_chronopher_setting(request_dict, response)
         elif operation == 'add-or-edit-chronopher':  # 添加/编辑定时计划
             return self.add_or_edit_chronopher(request_dict, response)
         elif operation == 'delete-chronopher':  # 删除定时计划
             return self.delete_chronopher(request_dict, response)
+        elif operation == 'edit-dimming-correction':  # 设置调光校正
+            return self.edit_dimming_correction(request_dict, response)
+        elif operation == 'edit-dimming-setting':  # 修改智能开关调光设置
+            return self.edit_dimming_setting(request_dict, response)
+        elif operation == 'reset':  # 设备重置
+            return self.reset(request_dict, response)
+        elif operation == 'switch-report-log':  # 设备上报执行日志
+            return self.create_log(request_dict, response)
         else:
             return response.json(414)
 
@@ -67,9 +82,9 @@ class SmartSwitchView(View):
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
     @staticmethod
-    def get_switch_setting(request_dict, response):
+    def get_dimming_setting(request_dict, response):
         """
-        获取智能开关设备设置信息
+        获取智能开关调光设置信息
         @param request_dict: 请求参数
         @param user_id: 用戶user_id
         @request_dict deviceId: 设备id
@@ -90,7 +105,6 @@ class SmartSwitchView(View):
                 'press': switch_setting_info_qs[0]['press'],
                 'doublePressClickTurnOnSpeed': switch_setting_info_qs[0]['double_press_click_turn_on_speed'],
                 'doublePressClickTurnOffSpeed': switch_setting_info_qs[0]['double_press_click_turn_off_speed'],
-                'led': switch_setting_info_qs[0]['led'],
                 'dimmingCorrection': switch_setting_info_qs[0]['dimming_correction'],
             }
             return response.json(0, res)
@@ -99,33 +113,22 @@ class SmartSwitchView(View):
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
     @staticmethod
-    def edit_switch_setting(request_dict, response):
+    def edit_dimming_correction(request_dict, response):
         """
-        修改智能开关设备设置
+        修改智能开关调光校正
         @param request_dict: 请求参数
         @request_dict deviceId: 设备id
-        @request_dict deviceNickname: 设备名称
-        @request_dict location: 位置
-        @request_dict led: LED指示灯
         @request_dict dimmingCorrection: 调光校正
         @param response: 响应对象
         @return: response
         """
         device_id = request_dict.get('deviceId', None)
-        device_nick_name = request_dict.get('deviceNickname', None)
-        location = request_dict.get('location', None)
-        led = request_dict.get('led', None)
         dimming_correction = request_dict.get('dimmingCorrection', None)
 
         if not device_id:
             return response.json(444)
         try:
-            switch_setting_data = {
-                'device_id': device_id,
-                'led': led,
-                'dimming_correction': dimming_correction,
-            }
-            SwitchDimmingSettings.objects.filter(device_id=device_id).update(**switch_setting_data)
+            SwitchDimmingSettings.objects.filter(device_id=device_id).update(dimming_correction=dimming_correction)
             return response.json(0)
         except Exception as e:
             print(e)
@@ -172,65 +175,6 @@ class SmartSwitchView(View):
             print(e)
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
-    @staticmethod
-    def get_location_list(request_dict, response):
-        """
-        获取房间列表
-        @param request_dict: 请求参数
-        @request_dict deviceId: 设备id
-        @param response: 响应对象
-        @return: response
-        """
-        device_id = request_dict.get('deviceId', None)
-        if not device_id:
-            return response.json(444)
-        try:
-            room_list_qs = SwitchDimmingSettings.objects.filter(device_id=device_id).values('location', 'location_list')
-            res = {
-                'location': room_list_qs[0]['location'],
-                'locationList': room_list_qs[0]['location_list']
-            }
-            return response.json(0, res)
-        except Exception as e:
-            print(e)
-            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
-
-    @staticmethod
-    def add_location(request_dict, response):
-        """
-        添加房间
-        @param request_dict: 请求参数
-        @request_dict deviceId: 设备id
-        @param response: 响应对象
-        @return: response
-        """
-        room_name = request_dict.get('roomName', None)
-        if not room_name:
-            return response.json(444)
-        try:
-            return response.json(0)
-        except Exception as e:
-            print(e)
-            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
-
-    @staticmethod
-    def delete_location(request_dict, response):
-        """
-        删除房间
-        @param request_dict: 请求参数
-        @request_dict deviceId: 设备id
-        @param response: 响应对象
-        @return: response
-        """
-        room_name = request_dict.get('roomName', None)
-        if not room_name:
-            return response.json(444)
-        try:
-            return response.json(0)
-        except Exception as e:
-            print(e)
-            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
-
     @staticmethod
     def get_chronopher_setting(request_dict, response):
         """
@@ -295,36 +239,106 @@ class SmartSwitchView(View):
         slow_open_or_close_speed = request_dict.get('slowOpenOrCloseSpeed', None)
         repeat = request_dict.get('repeat', None)
 
-        if not all([device_id, time_type_radio, repeat]):
-            return response.json(444)
-        if time_type_radio == 1:
+        if not all([device_id, repeat]):
+            return response.json(444, {'param': 'deviceId,repeat'})
+        device_qs = Device_Info.objects.filter(id=device_id).values('serial_number')
+        if not device_qs.exists():
+            return response.json(174)
+        if time_type_radio == 1:  # 时间点
             if not all([time_point, slow_open_or_close_speed]):
-                return response.json(444)
+                return response.json(444, {'param': 'timePoint,slowOpenOrCloseSpeed'})
             chronopher_data = {
                 'device_id': device_id,
                 'time_type_radio': time_type_radio,
                 'time_point': time_point,
                 'time_point_device_will_doing': time_point_device_will_doing,
-                'slow_open_or_close_speed': slow_open_or_close_speed
+                'slow_open_or_close_speed': slow_open_or_close_speed,
+                'repeat': repeat
             }
-        else:
+        elif time_type_radio == 2:  # 时间段
             if not all([time_quantum_start_time, time_quantum_end_time]):
-                return response.json(444)
+                return response.json(444, {'param': 'timeQuantumStartTime,timeQuantumEndTime'})
+            time_quantum_start_time = int(time_quantum_start_time)
+            time_quantum_end_time = int(time_quantum_end_time)
             chronopher_data = {
                 'device_id': device_id,
                 'time_type_radio': time_type_radio,
                 'time_quantum_start_time': time_quantum_start_time,
                 'time_quantum_end_time': time_quantum_end_time,
                 'time_quantum_device_will_doing': time_quantum_device_will_doing,
+                'repeat': repeat
             }
+        else:
+            return response.json(444, {'param': 'timeTypeRadio'})
         try:
-            if is_edit:
-                if not chronopher_id:
-                    return response.json(444)
-                SwitchChronopher.objects.filter(device_id=device_id, id=chronopher_id).update(**chronopher_data)
-            else:
-                SwitchChronopher.objects.create(**chronopher_data)
-            return response.json(0)
+            with transaction.atomic():
+                apscheduler_obj = ApschedulerObject()
+                if is_edit:
+                    if not chronopher_id:
+                        return response.json(444, {'param': 'timeTypeRadio'})
+                    update_flag = SwitchChronopher.objects.filter(device_id=device_id, id=chronopher_id).update(
+                        **chronopher_data)
+                    if not update_flag:
+                        return response.json(173)
+                    apscheduler_obj.del_job('switchchronopher_{}'.format(chronopher_id))
+                    apscheduler_obj.del_job('switchchronopher_{}_1'.format(chronopher_id))
+                    apscheduler_obj.del_job('switchchronopher_{}_2'.format(chronopher_id))
+                else:
+                    switch_qs = SwitchChronopher.objects.create(**chronopher_data)
+                    chronopher_id = switch_qs.id
+
+                # 设置定时任务
+                serial_number = device_qs[0]['serial_number']
+                topic_name = APSCHEDULER_TOPIC_NAME.format(serial_number)
+                if time_type_radio == 1:
+                    task_id = 'switchchronopher_{}'.format(chronopher_id)
+                    if time_point_device_will_doing in ['0', '1']:  # 开启或关闭
+                        msg = {
+                            "taskId": chronopher_id,
+                            "deviceSwitch": int(time_point_device_will_doing),  # 设备开关0:关,1:开
+                            "slowTime": slow_open_or_close_speed
+                        }
+                    else:  # 开启且设置亮度
+                        msg = {
+                            "taskId": chronopher_id,
+                            "deviceSwitch": 1,  # 设备开关0:关,1:开
+                            "pwmControl": int(time_point_device_will_doing),
+                            'slowTime': slow_open_or_close_speed
+                        }
+                    time_str = datetime.datetime.fromtimestamp(int(time_point))
+                    apscheduler_obj.create_cron_job(SmartSwitchView.send_mqtt, task_id, repeat, time_str.hour,
+                                                    time_str.minute, (serial_number, topic_name, msg, task_id))
+                else:
+                    start_hour = int(time_quantum_start_time / 60 // 60)
+                    start_minute = int(time_quantum_start_time / 60 % 60)
+                    end_hour = int(time_quantum_end_time / 60 // 60)
+                    end_minute = int(time_quantum_end_time / 60 % 60)
+                    if time_quantum_device_will_doing in ['0', '1']:
+                        begin_task_id = 'switchchronopher_{}_1'.format(chronopher_id)  # 开始任务id
+                        end_task_id = 'switchchronopher_{}_2'.format(chronopher_id)  # 结束任务id
+                        msg = {"taskId": chronopher_id,
+                               "deviceSwitch": int(time_quantum_device_will_doing)}
+                        apscheduler_obj.create_cron_job(SmartSwitchView.send_mqtt, begin_task_id, repeat, start_hour,
+                                                        start_minute, (serial_number, topic_name, msg, begin_task_id))
+                        msg = {"taskId": chronopher_id,
+                               "deviceSwitch": 0 if int(time_quantum_device_will_doing) == 1 else 1}
+                        apscheduler_obj.create_cron_job(SmartSwitchView.send_mqtt, end_task_id, repeat, end_hour,
+                                                        end_minute, (serial_number, topic_name, msg, end_task_id))
+
+                    else:  # 间隔任务
+                        minute = int(time_quantum_device_will_doing)
+                        task_id = 'switchchronopher_{}'.format(chronopher_id)  # 开始任务id
+                        msg = {"taskId": chronopher_id,
+                               "deviceSwitch": -1}
+                        if minute >= 60:
+                            hour = '{}-{}/{}'.format(start_hour, end_hour, minute // 60)
+                            minute = start_minute
+                        else:
+                            hour = '{}-{}'.format(start_hour, end_hour)
+                            minute = '*/{}'.format(minute)
+                        apscheduler_obj.create_cron_job(SmartSwitchView.send_mqtt, task_id, repeat, hour, minute,
+                                                        (serial_number, topic_name, msg, task_id))
+                return response.json(0)
         except Exception as e:
             print(e)
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
@@ -345,8 +359,114 @@ class SmartSwitchView(View):
         if not chronopher_id:
             return response.json(444, {'error param': 'deviceId or chronopherId'})
         try:
-            SwitchChronopher.objects.filter(device_id=device_id, id=chronopher_id).delete()
+            delete_flag = SwitchChronopher.objects.filter(device_id=device_id, id=chronopher_id).delete()
+            if not delete_flag[0]:
+                return response.json(173)
+            apscheduler_obj = ApschedulerObject()
+            apscheduler_obj.del_job('switchchronopher_{}'.format(chronopher_id))  # 删除定时任务
+            apscheduler_obj.del_job('switchchronopher_{}_1'.format(chronopher_id))
+            apscheduler_obj.del_job('switchchronopher_{}_2'.format(chronopher_id))
+            return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def send_mqtt(serial_number, topic_name, msg, task_id):
+        """
+        定时发送mqtt, (不要随意更改,否则定时任务不执行)
+        @param serial_number: 设备序列号
+        @param topic_name: 主题
+        @param msg: 消息
+        @param task_id: 任务id
+        @return: response
+        """
+        result = CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
+        LOGGER.info(
+            '定时发送mqtt结果:{},参数:{},{},{},{},{}'.format(result, serial_number, topic_name, msg, int(time.time()), task_id))
+
+    @staticmethod
+    def create_log(request_dict, response):
+        """
+        生成执行日志
+        @param request_dict: 请求参数
+        @request_dict serialNumber: 设备序列号
+        @request_dict chronopherId: 排程id
+        @request_dict status: 执行状态
+        @param response: 响应对象
+        @return: response
+        """
+        serial_number = request_dict.get('serialNumber', None)
+        chronopher_id = request_dict.get('taskId', None)
+        status = request_dict.get('status', None)
+        implement_time = request_dict.get('implementTime', None)
+
+        if not all([serial_number, chronopher_id, status, implement_time]):
+            return response.json(444, {'error param': 'deviceId or chronopherId'})
+        device_qs = Device_Info.objects.filter(serial_number=serial_number).values('id')
+        if not device_qs.exists():
+            return response.json(173)
+        device_id = device_qs[0]['id']
+        chronopher_qs = SwitchChronopher.objects.filter(device_id=device_id, id=chronopher_id)
+        if not chronopher_qs.exists():
+            return response.json(173)
+        try:
+            scene_log = {
+                'scene_id': chronopher_id,
+                'device_id': device_id,
+                'tasks': '',
+                'status': status,
+                'created_time': implement_time,
+            }
+            scene_qs = SceneLog.objects.filter(created_time=implement_time, device_id=device_id,
+                                               scene_id=chronopher_id)
+            if not scene_qs.exists():
+                SceneLog.objects.create(**scene_log)
             return response.json(0)
         except Exception as e:
             print(e)
             return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def get_log(request_dict, response):
+        """
+        查询执行日志
+        @param request_dict: 请求参数
+        @request_dict deviceId: 设备id
+        @param response: 响应对象
+        @return: response
+        """
+        device_id = request_dict.get('deviceId', None)
+        if not device_id:
+            return response.json(444, {'error param': 'deviceId'})
+        try:
+            scene_qs = SceneLog.objects.filter(device_id=device_id).values('tasks', 'status', 'created_time', 'id')
+            return response.json(0, list(scene_qs))
+        except Exception as e:
+            print(e)
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def reset(request_dict, response):
+        """
+        查询执行日志
+        @param request_dict: 请求参数
+        @request_dict serialNumber: 设备序列号
+        @param response: 响应对象
+        @return: response
+        """
+        serial_number = request_dict.get('serialNumber', None)
+        if not serial_number:
+            return response.json(444, {'error param': 'serialNumber'})
+        device_qs = Device_Info.objects.filter(serial_number=serial_number)
+        if not device_qs.exists():
+            return response.json(173)
+        device_id_list = list(device_qs.values_list('id', flat=True))
+        try:
+            # 删除智能开关数据
+            SwitchDimmingSettings.objects.filter(device_id__in=device_id_list).delete()
+            SwitchChronopher.objects.filter(device_id__in=device_id_list).delete()
+            SceneLog.objects.filter(device_id__in=device_id_list).delete()
+        except Exception as e:
+            print(e)
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 4 - 5
Model/models.py

@@ -4161,8 +4161,7 @@ class SwitchDimmingSettings(models.Model):
     double_click = models.CharField(max_length=8, default='', verbose_name='双击')  # 0: 无, 1: 缓慢开/关, x%: 预设亮度
     press = models.CharField(max_length=8, default='', verbose_name='长按')  # 0: 无, 1: 缓慢开/关, x%: 预设亮度
     double_press_click_turn_on_speed = models.SmallIntegerField(default=0, verbose_name='双击/长按开启速度')
-    double_press_click_turn_off_speed = models.SmallIntegerField(default=0, verbose_name='双击/长按单击关闭速度')
-    led = models.BooleanField(default=False, verbose_name='LED指示灯')  # True: 开, False: 关
+    double_press_click_turn_off_speed = models.SmallIntegerField(default=0, verbose_name='双击/长按关闭速度')
     dimming_correction = models.CharField(max_length=8, default='', verbose_name='调光校正')
 
     class Meta:
@@ -4181,9 +4180,9 @@ class SwitchChronopher(models.Model):
     time_point_device_will_doing = models.CharField(max_length=8, default='',
                                                     verbose_name='设备将会')  # 0: 开启, 1: 关闭, x%: 预设亮度
     time_quantum_device_will_doing = models.SmallIntegerField(default=0,
-                                                              verbose_name='设备将会')  # 0: 开启, 1: 关闭, x: 开启/关闭切换间隔
-    slow_open_or_close_speed = models.SmallIntegerField(default=0, verbose_name='缓慢开/关速度')
-    repeat = models.SmallIntegerField(default=0, verbose_name=u'重复周期')
+                                                              verbose_name='设备将会')  # 0: 开启, 1: 关闭, x: 开启/关闭切换间隔(分钟)
+    slow_open_or_close_speed = models.SmallIntegerField(default=0, verbose_name='缓慢开/关速度')  # 秒
+    repeat = models.CharField(default=0, max_length=11, verbose_name=u'重复周期')  # 0-6:星期天到星期六
 
     class Meta:
         db_table = 'switch_chronopher'

+ 10 - 3
Object/ApschedulerObject.py

@@ -4,6 +4,7 @@ from django_apscheduler.jobstores import DjangoJobStore
 from Ansjer.config import LOGGER
 from django_apscheduler.models import DjangoJob
 import datetime
+from apscheduler.triggers.cron import CronTrigger
 
 
 class ApschedulerObject:
@@ -17,9 +18,15 @@ class ApschedulerObject:
         now_time = time.time()
         print('hello world:[{}]'.format(now_time))
 
-    def cron_job(self, task_id, day_of_week, hour, minute):  # 周期任务
-        self.scheduler.add_job(self.auto_hello, 'cron', day_of_week=day_of_week, hour=hour, minute=minute,
-                               replace_existing=True, id=task_id, max_instances=1, coalesce=True)
+    def create_cron_job(self, func, task_id, day_of_week, hour, minute, args):  # 周期任务
+        self.scheduler.add_job(func=func, trigger='cron', day_of_week=day_of_week, hour=hour, minute=minute,
+                               replace_existing=True, id=task_id, max_instances=1, coalesce=False, args=args)
+
+    def create_interval_job(self, func, task_id, minutes, start_time, end_time, args):  # 间隔任务
+        self.scheduler.add_job(func=func, trigger='interval', minutes=minutes,
+                               start_date=datetime.datetime.fromtimestamp(start_time),
+                               end_date=datetime.datetime.fromtimestamp(end_time),
+                               replace_existing=True, id=task_id, max_instances=1, coalesce=False, args=args)
 
     def create_date_job(self, func, task_id, time_stamp, args):
         """