浏览代码

Merge branch 'test' of http://192.168.136.99:3000/servers/ASJPush into test

zhangdongming 1 年之前
父节点
当前提交
f73996ab7f

+ 11 - 0
AnsjerPush/Config/gatewaySensorConfig.py

@@ -8,6 +8,14 @@
 # MQTT主题名
 # MQTT主题名
 SMART_SCENE_TOPIC = 'loocam/gateway_sensor/smart_scene/{}'
 SMART_SCENE_TOPIC = 'loocam/gateway_sensor/smart_scene/{}'
 GET_SCENE_TOPIC = 'loocam/gateway_sensor/get_scene/{}'
 GET_SCENE_TOPIC = 'loocam/gateway_sensor/get_scene/{}'
+SMART_SOCKET_TOPIC = 'loocam/smart-socket/{}'
+
+# 设备类型
+DEVICE_TYPE = {
+    'gateway': 200,
+    'socket': 201,
+    'switch': 202
+}
 
 
 # 传感器类型
 # 传感器类型
 SENSOR_TYPE = {
 SENSOR_TYPE = {
@@ -54,4 +62,7 @@ EVENT_TYPE = {
     'temperature': 2200,
     'temperature': 2200,
     'humidity': 2201,
     'humidity': 2201,
     'tem_hum_sensor_low_power': 2202,
     'tem_hum_sensor_low_power': 2202,
+    # 插座电源
+    'socket_power_on': 2010,
+    'socket_power_off': 2011,
 }
 }

+ 1 - 1
AnsjerPush/cn_config/cn_formal_config.py

@@ -73,7 +73,7 @@ PAYPAL_CRD = {
     "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
     "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
     "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
     "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
 }
 }
-DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+DETECT_PUSH_DOMAIN = 'http://push.zositechc.cn/'
 
 
 
 
 JPUSH_CONFIG = {
 JPUSH_CONFIG = {

+ 9 - 1
AnsjerPush/eur_config/eur_formal_config.py

@@ -73,7 +73,7 @@ PAYPAL_CRD = {
     "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
     "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
     "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
     "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
 }
 }
-DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+DETECT_PUSH_DOMAIN = 'http://push.zositeche.com/'
 
 
 
 
 JPUSH_CONFIG = {
 JPUSH_CONFIG = {
@@ -198,4 +198,12 @@ OPPOPUSH_CONFIG = {
         'Key': '882266a2000e4407990932be0b7043f5',
         'Key': '882266a2000e4407990932be0b7043f5',
         'Secret': '1037f29469c8416e87ab9cb8537c68ce'
         'Secret': '1037f29469c8416e87ab9cb8537c68ce'
     }
     }
+}
+
+HONORPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'client_id': 'b5dab836effb48d09f08178a26e0ecd6',
+        'client_secret': '7z8RazyCVmPYx4icpIqYr6KLFnEowkag',
+        'app_id': 104435107
+    }
 }
 }

+ 1 - 1
AnsjerPush/test_config/test_config.py

@@ -59,7 +59,7 @@ SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 DOMAIN_HOST = 'www.dvema.com'
 DOMAIN_HOST = 'www.dvema.com'
 SERVER_HOST = 'localhost'
 SERVER_HOST = 'localhost'
-DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+DETECT_PUSH_DOMAIN = 'http://test.push.zositechc.cn/'
 
 
 
 
 JPUSH_CONFIG = {
 JPUSH_CONFIG = {

+ 1 - 0
AnsjerPush/urls.py

@@ -8,6 +8,7 @@ from Controller.Cron import CronTaskController
 urlpatterns = [
 urlpatterns = [
     path('deviceShadow/generateUTK', ShadowController.generate_utk),
     path('deviceShadow/generateUTK', ShadowController.generate_utk),
     path('deviceShadow/decryptETK', ShadowController.decrypt_etk),
     path('deviceShadow/decryptETK', ShadowController.decrypt_etk),
+    path('honorPush/receipt', ShadowController.honor_push_receipt),
     path('deviceShadow/update', ShadowController.update_device_shadow),
     path('deviceShadow/update', ShadowController.update_device_shadow),
 
 
     path('notify/push', DetectController.NotificationView.as_view()),
     path('notify/push', DetectController.NotificationView.as_view()),

+ 8 - 0
AnsjerPush/us_config/formal_config.py

@@ -197,4 +197,12 @@ MEIZUPUSH_CONFIG = {
         'ID': 151022,
         'ID': 151022,
         'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
         'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
     }
     }
+}
+
+HONORPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'client_id': 'b5dab836effb48d09f08178a26e0ecd6',
+        'client_secret': '7z8RazyCVmPYx4icpIqYr6KLFnEowkag',
+        'app_id': 104435107
+    }
 }
 }

+ 17 - 0
Controller/ShadowController.py

@@ -71,6 +71,23 @@ def decrypt_etk(request):
     return response.json(0, {'uid': uid})
     return response.json(0, {'uid': uid})
 
 
 
 
+def honor_push_receipt(request):
+    """
+    荣耀推送回执
+    """
+    request.encoding = 'utf-8'
+    response = ResponseObject()
+    if request.method == 'GET':
+        request_dict = request.GET
+    elif request.method == 'POST':
+        request_dict = request.POST
+    else:
+        return response.json(444, 'wrong method')
+    logger = logging.getLogger('info')
+    logger.info('荣耀推送回执:{}'.format(request_dict))
+    return response.json(0)
+
+
 def update_device_shadow(request):
 def update_device_shadow(request):
     """
     """
     设备生成或更新(复位时)设备影子
     设备生成或更新(复位时)设备影子

+ 221 - 20
Controller/gatewayController.py

@@ -4,15 +4,22 @@
 @Time : 2022/5/9 10:51
 @Time : 2022/5/9 10:51
 @File :gatewayController.py
 @File :gatewayController.py
 """
 """
+import datetime
+import threading
 import time
 import time
 
 
+import pytz
+import requests
 from django.views.generic.base import View
 from django.views.generic.base import View
 
 
-from AnsjerPush.Config.gatewaySensorConfig import SENSOR_TYPE, EVENT_TYPE
+from AnsjerPush.Config.gatewaySensorConfig import SENSOR_TYPE, EVENT_TYPE, DEVICE_TYPE, SMART_SOCKET_TOPIC
 from AnsjerPush.config import LOGGER, XM_PUSH_CHANNEL_ID
 from AnsjerPush.config import LOGGER, XM_PUSH_CHANNEL_ID
-from Model.models import SensorRecord, GatewaySubDevice, GatewayPush, Device_Info, SceneLog, SmartScene, CountryModel
+from Model.models import SensorRecord, GatewaySubDevice, GatewayPush, Device_Info, SceneLog, SmartScene, CountryModel, \
+    EffectiveTime
 from Object.RedisObject import RedisObject
 from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
 from Object.ResponseObject import ResponseObject
+from Service.CommonService import CommonService
+from AnsjerPush.config import DETECT_PUSH_DOMAIN
 from Service.EquipmentInfoService import EquipmentInfoService
 from Service.EquipmentInfoService import EquipmentInfoService
 from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
 from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
 from Service.PushService import PushObject
 from Service.PushService import PushObject
@@ -63,9 +70,18 @@ class GatewayView(View):
         if not all([serial_number, ieee_addr, sensor_type, event_type]):
         if not all([serial_number, ieee_addr, sensor_type, event_type]):
             return response.json(444)
             return response.json(444)
 
 
+        num = None
         n_time = int(time.time())
         n_time = int(time.time())
 
 
         try:
         try:
+            # 获取温湿度
+            if sensor_type == SENSOR_TYPE['tem_hum_sensor'] and (
+                    event_type == EVENT_TYPE['temperature'] or event_type == EVENT_TYPE['humidity']):
+                num = request_dict.get('num')
+                if num is None:
+                    return response.json(444)
+                num = int(num) / 100
+
             # 查询子设备表id
             # 查询子设备表id
             gateway_sub_device_qs = GatewaySubDevice.objects.filter(device__serial_number=serial_number,
             gateway_sub_device_qs = GatewaySubDevice.objects.filter(device__serial_number=serial_number,
                                                                     device_type=sensor_type, ieee_addr=ieee_addr). \
                                                                     device_type=sensor_type, ieee_addr=ieee_addr). \
@@ -73,11 +89,21 @@ class GatewayView(View):
             if not gateway_sub_device_qs.exists():
             if not gateway_sub_device_qs.exists():
                 return response.json(173)
                 return response.json(173)
 
 
+            gateway_sub_device_id = gateway_sub_device_qs[0]['id']
+            # 多线程执行场景任务
+            task_kwargs = {
+                'num': num,
+                'n_time': n_time,
+                'event_type': event_type,
+                'gateway_sub_device_id': gateway_sub_device_id
+            }
+            execute_task_thread = threading.Thread(target=cls.execute_scene_tasks, kwargs=task_kwargs)
+            execute_task_thread.start()
+
             country_id = gateway_sub_device_qs[0]['device__userID__region_country']
             country_id = gateway_sub_device_qs[0]['device__userID__region_country']
             lang = cls.confirm_lang(country_id)
             lang = cls.confirm_lang(country_id)
             alarm = cls.get_alarm(lang, event_type)
             alarm = cls.get_alarm(lang, event_type)
 
 
-            gateway_sub_device_id = gateway_sub_device_qs[0]['id']
             nickname = gateway_sub_device_qs[0]['nickname']
             nickname = gateway_sub_device_qs[0]['nickname']
             sensor_record_dict = {
             sensor_record_dict = {
                 'gateway_sub_device_id': gateway_sub_device_id,
                 'gateway_sub_device_id': gateway_sub_device_id,
@@ -85,12 +111,9 @@ class GatewayView(View):
                 'event_type': event_type,
                 'event_type': event_type,
                 'created_time': n_time,
                 'created_time': n_time,
             }
             }
-            # 处理温湿度,不推送
-            if sensor_type == SENSOR_TYPE['tem_hum_sensor'] and (
-                    event_type == EVENT_TYPE['temperature'] or event_type == EVENT_TYPE['humidity']):
-                num = request_dict.get('num', None)
-                num = str(int(num) / 100)
-                sensor_record_dict['alarm'] = num
+            # 温湿度上报不推送
+            if num is not None:
+                sensor_record_dict['alarm'] = str(num)
                 SensorRecord.objects.create(**sensor_record_dict)
                 SensorRecord.objects.create(**sensor_record_dict)
                 return response.json(0)
                 return response.json(0)
 
 
@@ -293,14 +316,6 @@ class GatewayView(View):
         scene_id = request_dict.get('sceneId', None)
         scene_id = request_dict.get('sceneId', None)
         status = request_dict.get('status', None)
         status = request_dict.get('status', None)
 
 
-        # 防止定时任务同时请求生成多条场景日志
-        redis_obj = RedisObject()
-        key = scene_id + 'scene_log_limit'
-        is_lock = redis_obj.CONN.setnx(key, 1)
-        redis_obj.CONN.expire(key, 60)
-        if not is_lock:
-            return response.json(0)
-
         LOGGER.info('---场景日志推送接口--- request_dict:{}'.format(request_dict))
         LOGGER.info('---场景日志推送接口--- request_dict:{}'.format(request_dict))
         if not all([scene_id, status]):
         if not all([scene_id, status]):
             return response.json(444)
             return response.json(444)
@@ -434,6 +449,192 @@ class GatewayView(View):
             LOGGER.info('成功接收并保存,插座序列号{},状态:{}'.format(serial_number, status))
             LOGGER.info('成功接收并保存,插座序列号{},状态:{}'.format(serial_number, status))
             return response.json(0)
             return response.json(0)
         except Exception as e:
         except Exception as e:
-            print(repr(e))
-            LOGGER.info('---插座开关日志推送接口异常--- {}'.format(repr(e)))
-            return response.json(500, repr(e))
+            LOGGER.info('插座开关日志推送接口异常, error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def execute_scene_tasks(cls, gateway_sub_device_id, event_type, num, n_time):
+        """
+        执行场景任务
+        @param gateway_sub_device_id: 子设备id
+        @param event_type: 事件类型
+        @param num: 温湿度值
+        @param n_time: 当前时间
+        @return:
+        """
+        smart_scene_qs = SmartScene.objects.filter(sub_device_id=gateway_sub_device_id, is_enable=True). \
+            values('id', 'scene_data', 'tz', 'is_all_day', 'effective_time_id')
+        if smart_scene_qs.exists():
+            for smart_scene in smart_scene_qs:
+                if smart_scene['scene_data']:
+                    scene_data_dict = eval(smart_scene['scene_data'])
+                    condition = scene_data_dict.get('condition')
+                    if condition:
+                        # None 或 event_type
+                        scene_data_event_type = condition.get('event_type')
+                        # 触发事件类型跟条件事件类型一致,且任务列表不为空
+                        task_list = scene_data_dict.get('task_list')
+                        if event_type == scene_data_event_type and task_list:
+                            # 判断温湿度是否在条件设置的范围
+                            if num is not None:
+                                condition_value = condition.get('value')
+                                space_index = condition_value.index(' ')
+                                symbol, value = condition_value[:space_index], float(condition_value[space_index + 1:])
+                                # 温湿度不在设定范围,不执行
+                                if not ((symbol == '≥' and num >= value) or (symbol == '≤' and num <= value)):
+                                    continue
+                            execute_task = cls.judge_execute_task(smart_scene, n_time)
+
+                            # 执行任务
+                            if execute_task:
+                                scene_id = 0
+                                task_list_len = len(task_list)
+                                for index, task in enumerate(task_list):
+                                    # 最后一个任务上报日志
+                                    if index == task_list_len - 1:
+                                        scene_id = smart_scene['id']
+                                    kwargs = {
+                                        'device_type': task['device_type'],
+                                        'event_type': task['event_type'],
+                                        'serial_number': task['serial_number'],
+                                        'delay_time': task['delay_time'],
+                                        'scene_id': scene_id
+                                    }
+                                    pub_mqtt_thread = threading.Thread(target=cls.pub_mqtt, kwargs=kwargs)
+                                    pub_mqtt_thread.start()
+
+    @classmethod
+    def judge_execute_task(cls, smart_scene, n_time):
+        """
+        判断是否执行任务
+        @param smart_scene: 场景数据
+        @param n_time: 当前时间
+        @return execute_task: bool
+        """
+        execute_task = False
+        # 判断时间是否在执行时间范围内
+        if smart_scene['is_all_day'] == 1:
+            # 全天必执行
+            execute_task = True
+        elif smart_scene['is_all_day'] == 2:
+            # 非全天,判断当前时间是否在设置的时间范围
+            tz = smart_scene['tz']
+            time_minute, week = cls.get_time_info(n_time, tz)
+
+            effective_time_id = smart_scene['effective_time_id']
+            effective_time_qs = EffectiveTime.objects.filter(id=effective_time_id). \
+                values('start_time', 'end_time', 'repeat')
+            if effective_time_qs.exists():
+                start_time = effective_time_qs[0]['start_time']
+                end_time = effective_time_qs[0]['end_time']
+                repeat = effective_time_qs[0]['repeat']
+                time_frame_dict, time_frame_next_day_dict = cls.get_time_frame_dict(
+                    start_time, end_time, repeat)
+                # 判断当前时间是否在设置的时间范围
+                if time_frame_dict.get(week):
+                    start_time = time_frame_dict[week]['start_time']
+                    end_time = time_frame_dict[week]['end_time']
+                    if start_time <= time_minute <= end_time:
+                        execute_task = True
+                if time_frame_next_day_dict.get(week):
+                    start_time = time_frame_next_day_dict[week]['start_time']
+                    end_time = time_frame_next_day_dict[week]['end_time']
+                    if start_time <= time_minute <= end_time:
+                        execute_task = True
+        return execute_task
+
+    @staticmethod
+    def get_time_info(timestamp, timezone_offset):
+        """
+        根据时间戳和时区获取时间(时分转为分钟数)和星期
+        @param timestamp: 时间戳
+        @param timezone_offset: 时区
+        @return: time_minute, week
+        """
+        # 计算时区偏移量(以分钟为单位)
+        timezone_offset_minutes = int(timezone_offset * 60)
+        timezone = pytz.FixedOffset(timezone_offset_minutes)
+        # 将时间戳转换为datetime对象,并应用时区
+        dt_object = datetime.datetime.fromtimestamp(timestamp, tz=timezone)
+        # 获取时分,并转为分钟数
+        hour = dt_object.hour
+        minute = dt_object.minute
+        time_minute = hour * 60 + minute
+        # 获取星期(0表示星期一,6表示星期日)
+        week = str(dt_object.weekday())
+        return time_minute, week
+
+    @staticmethod
+    def get_time_frame_dict(start_time, end_time, repeat):
+        """
+        获取时间范围字典
+        @param start_time: 开始时间
+        @param end_time: 结束时间
+        @param repeat: 星期周期的十进制数,如127 -> 0,1,2,3,4,5,6
+        @return: time_frame_dict, time_frame_next_day_dict
+        """
+        # 十进制转为7位的二进制并倒序
+        bin_str = bin(repeat)[2:].zfill(7)[::-1]
+        # 生成星期周期列表
+        week_list = []
+        for i, bit in enumerate(bin_str):
+            if bit == '1':
+                week_list.append(i)
+
+        # 生成时间范围字典
+        time_frame_dict = {}
+        time_frame_next_day_dict = {}
+        # 非隔天
+        if end_time > start_time:
+            for week in week_list:
+                time_frame_dict[str(week)] = {
+                    'start_time': start_time,
+                    'end_time': end_time
+                }
+        # 隔天
+        else:
+            # time_frame_dict记录当天时间范围,time_frame_next_day_dict记录溢出到第二天的时间范围
+            for week in week_list:
+                time_frame_dict[str(week)] = {
+                    'start_time': start_time,
+                    'end_time': 1439    # 23:59
+                }
+                week += 1
+                if week == 7:
+                    week = 0
+                time_frame_next_day_dict[str(week)] = {
+                    'start_time': 0,    # 00:00
+                    'end_time': end_time
+                }
+        return time_frame_dict, time_frame_next_day_dict
+
+    @staticmethod
+    def pub_mqtt(device_type, event_type, serial_number, delay_time, scene_id=0):
+        """
+        发布mqtt消息
+        @param device_type: 设备类型
+        @param event_type: 事件类型
+        @param serial_number: 序列号
+        @param delay_time: 延迟时间
+        @param scene_id: 场景id
+        @return:
+        """
+        if device_type == DEVICE_TYPE['socket']:
+            topic_name = SMART_SOCKET_TOPIC.format(serial_number)
+            status = 1 if event_type == EVENT_TYPE['socket_power_on'] else 0
+            msg = {
+                'type': 1,
+                'data': {'deviceSwitch': status}
+            }
+            if delay_time:
+                time.sleep(delay_time)
+            CommonService.req_publish_mqtt_msg(serial_number, topic_name, msg)
+
+        # 没有设备任务时,最后一个任务上报场景日志
+        if scene_id:
+            data = {
+                'sceneId': scene_id,
+                'status': 1
+            }
+            url = DETECT_PUSH_DOMAIN + 'gatewayService/sceneLogPush'
+            requests.post(url=url, data=data, timeout=8)

+ 12 - 0
Model/models.py

@@ -2701,6 +2701,18 @@ class UnicomFlowPush(models.Model):
         app_label = 'db2'
         app_label = 'db2'
 
 
 
 
+class EffectiveTime(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记id')
+    start_time = models.SmallIntegerField(default=0, verbose_name=u'开始时间')
+    end_time = models.SmallIntegerField(default=0, verbose_name=u'结束时间')
+    repeat = models.SmallIntegerField(default=0, verbose_name=u'重复周期')
+
+    class Meta:
+        db_table = 'effective_time'
+        verbose_name = '场景执行时间'
+        verbose_name_plural = verbose_name
+
+
 class SceneLog(models.Model):
 class SceneLog(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name='自增标记ID')
     scene_name = models.CharField(default='', max_length=100, verbose_name='场景名称')
     scene_name = models.CharField(default='', max_length=100, verbose_name='场景名称')

+ 94 - 0
Service/CommonService.py

@@ -7,13 +7,17 @@ import hashlib
 from pathlib import Path
 from pathlib import Path
 from random import Random
 from random import Random
 import ipdb
 import ipdb
+import requests
 import simplejson as json
 import simplejson as json
 from boto3 import Session
 from boto3 import Session
 from django.core import serializers
 from django.core import serializers
 from django.utils import timezone
 from django.utils import timezone
 from pyipip import IPIPDatabase
 from pyipip import IPIPDatabase
+import OpenSSL.crypto as ct
+from base64 import encodebytes
 
 
 from AnsjerPush.config import BASE_DIR, ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION_NAME, PUSH_BUCKET
 from AnsjerPush.config import BASE_DIR, ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION_NAME, PUSH_BUCKET
+from Model.models import iotdeviceInfoModel
 
 
 
 
 # 复用性且公用较高封装代码在这
 # 复用性且公用较高封装代码在这
@@ -328,3 +332,93 @@ class CommonService:
         struct_time = time.localtime(timestamp)
         struct_time = time.localtime(timestamp)
         time_str = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
         time_str = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
         return time_str
         return time_str
+
+    @staticmethod
+    def req_publish_mqtt_msg(identification_code, topic_name, msg, qos=1):
+        """
+        通用发布MQTT消息函数
+        @param identification_code: 标识码
+        @param topic_name: 主题名
+        @param msg: 消息内容
+        @param qos: mqtt qos等级
+        @return: boolean
+        """
+        if not all([identification_code, topic_name]):
+            return False
+
+        if identification_code.endswith('11L'):
+            thing_name = 'LC_' + identification_code
+        else:
+            thing_name = 'Ansjer_Device_' + identification_code
+
+        try:
+            # 获取数据组织将要请求的url
+            iot = iotdeviceInfoModel.objects.filter(
+                thing_name=thing_name).values(
+                'endpoint', 'token_iot_number')
+            if not iot.exists():
+                return False
+            endpoint = iot[0]['endpoint']
+            Token = iot[0]['token_iot_number']
+
+            # api doc: https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/http.html
+            # url: https://IoT_data_endpoint/topics/url_encoded_topic_name?qos=1
+            # post请求url发布MQTT消息
+            url = 'https://{}/topics/{}?qos={}'.format(endpoint, topic_name, qos)
+            authorizer_name = 'Ansjer_Iot_Auth'
+            signature = CommonService.rsa_sign(Token)  # Token签名
+            headers = {
+                'x-amz-customauthorizer-name': authorizer_name,
+                'Token': Token,
+                'x-amz-customauthorizer-signature': signature}
+            r = requests.post(url=url, headers=headers, json=msg, timeout=2)
+            if r.status_code == 200:
+                res = r.json()
+                if res['message'] == 'OK':
+                    return True
+                return False
+            else:
+                return False
+        except Exception as e:
+            return False
+
+    @staticmethod
+    def rsa_sign(Token):
+        # 私钥签名Token
+        if not Token:
+            return ''
+        private_key_file = '''-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA5iJzEDPqtGmFMggekVro6C0lrjuC2BjunGkrFNJWpDYzxCzE
+X5jf4/Fq7hcIaQd5sqHugDxPVollSLPe9zNilbrd0sZfU+Ed8gRVuKW9KwfE9XFr
+L0pt6bKRQ0IIRfiZ9TuR0tsQysvcO1GZSXcYfPue3tGM1zOnWFThWDqZ06+sOxzt
+RMRl4yNfbpCG4MfxG3itNXOfrjZv2OMLSXrxmzubSvRpUYSvQPs4fm9302SAnySY
+0MKzx6H6528ZQm/IDDSZy6EmNBIyTRDfxC56vnYcXvqedAQh7jJnjdvt6Q4MhASH
+eIYi1FBSdu2NT6wgpnrqXzx5pq9kR/lnsLID0wIDAQABAoIBAQCiF4GT1/1oNSpr
+ouxk1PNXFPWFUsVGD8mAwVJmx//eiY7MjfuCmdqYYmI+cFqsH2fIOeYSzGfVO9Dq
+9EYHN1oovAWhf7eFDPpajFMUSyiCNmazub8VAAeKowtNpCTPo9pMsDh1m3aoYA4u
+ebrN0+Sbo16y8kWRDgDAZoiR7DSMs8lczk16hwfv5mw8XpNDbaL3Coi4Koe2S1Yh
+2SX3vWFlpd7qF1ZYXuZIp+b8JPrV7n9eUKoFgzj0gqgwQK80CoexIjiOrNMPvkQa
+q+8kCvFjAzKxOK7e8gjM8lMRiGodb61kmYZkkJzFwWO4EaGbl34lfVECd1Ixp3tF
+be0OWAGBAoGBAPSteXDzzToD8ovM7LL11x0jWwI6HOiHu89kZtW566rIezjWBuA2
+TxrcYKM3h9jQRXS3CsMdoIv6XGk5lqM8ADtjn23FBWe/THYLh8bm8JOgh5RRWQDg
+SvkLfi9Ih2mM4NJfmuuDOh3Nze2efLM7+kOZWUQwF2Zx9mL5jvRBk351AoGBAPDI
+sYmT2Li+i5+0vykA2m5uPF8ZOW8BGtAfCZv0suW7BNzSgin78g9WapRd/4p0NNiL
+/nVMqPPCpd1akCUpV+GDWQt0hV+HZjxANE0KWhciQRyo2qvo51j8SWILJSgh0tXC
+aTF8qt6oGw3VN3m57vKhbrlDaz0J/NDJFci6msAnAoGBAOuG6bXPGijUj+//DYKf
+n7jOxdZ49kboEePrtAncdHzri6IEdI3z+WXT6bpzw/LzWUimwldb96WHFNm9s8Hi
+Ch8hIODbnP5naUTgiIzw1XhmONyPCewL/F+LrqX5XVA/alNX8JrwsUrrR2WLAGLQ
+Q3I69XDsEjptTU2tCO0bCs3ZAoGBAJ2lCHfm0JHET230zONvp5N9oREyVqQSuRdh
++syc3TQDyh85w/bw+X6JOaaCFHj1tFPC9Iqf8k4GNspCLPXnp54CfR4+38O3xnvU
+HWoDSRC0YKT++IxtJGriYrlKSr2Hx54kdvLriIPW1D+uRW/xCDza7L9nIKMKEvgv
+b4/IfOEpAoGAeKM9Te7T1VzlAkS0CJOwanzwYV/zrex84WuXxlsGgPQ871lTs5AP
+H1QLfLfFXH+UVrCEC2yv4eml/cqFkpB3gE5i4MQ8GPVIOSs5tsIyl8YUA03vdNdB
+GCqvlyw5dfxNA+EtxNE2wCW/LW7ENJlACgcfgPlBZtpLheWoZB/maw4=
+-----END RSA PRIVATE KEY-----'''
+        # 使用密钥文件方式
+        # private_key_file_path = os.path.join(BASE_DIR, 'static/iotCore/private.pem')#.replace('\\', '/')
+        # private_key_file = open(private_key_file_path, 'r')
+        private_key = ct.load_privatekey(ct.FILETYPE_PEM, private_key_file)
+        signature = ct.sign(private_key, Token.encode('utf8'), 'sha256')
+        signature = encodebytes(signature).decode('utf8').replace('\n', '')
+        # print('signature:', signature)
+        return signature

+ 7 - 5
Service/CustomizedPushService.py

@@ -6,7 +6,7 @@ import threading
 import time
 import time
 from concurrent.futures import ThreadPoolExecutor
 from concurrent.futures import ThreadPoolExecutor
 
 
-from Model.models import DeviceTypeModel, Device_Info, GatewayPush, CountryModel, SysMsgModel
+from Model.models import DeviceTypeModel, Device_Info, GatewayPush, CountryModel, SysMsgModel, CustomizedPush
 from Service.CommonService import CommonService
 from Service.CommonService import CommonService
 from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
 from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
 from Service.PushService import PushObject
 from Service.PushService import PushObject
@@ -28,9 +28,10 @@ class CustomizedPushObject:
         # 设备型号和国家
         # 设备型号和国家
         device_name_list = device_name.split(',')
         device_name_list = device_name.split(',')
         device_type_list = DeviceTypeModel.objects.filter(name__in=device_name_list).values_list('type', flat=True)
         device_type_list = DeviceTypeModel.objects.filter(name__in=device_name_list).values_list('type', flat=True)
-        country_qs = CountryModel.objects.filter(country_name=country).values('id')
-        country_id = country_qs[0]['id']
-        device_info_qs = Device_Info.objects.filter(Type__in=device_type_list, userID__region_country=country_id)
+        country_name_list = country.split(',')
+        country_id_list = CountryModel.objects.filter(country_name__in=country_name_list).values_list('id', flat=True)
+        device_info_qs = Device_Info.objects.filter(Type__in=device_type_list,
+                                                    userID__region_country__in=country_id_list)
         # 获取时间范围
         # 获取时间范围
         now_time = int(time.time())
         now_time = int(time.time())
 
 
@@ -73,6 +74,7 @@ class CustomizedPushObject:
         @param kwargs: 参数
         @param kwargs: 参数
         @return:
         @return:
         """
         """
+        customized_push_id = kwargs['id']
         user_id_list = kwargs['user_id_list']
         user_id_list = kwargs['user_id_list']
         title = kwargs['title']
         title = kwargs['title']
         msg = kwargs['msg']
         msg = kwargs['msg']
@@ -119,7 +121,7 @@ class CustomizedPushObject:
                     target=cls.thr_pool_push,
                     target=cls.thr_pool_push,
                     kwargs=pre_push_kwargs)
                     kwargs=pre_push_kwargs)
                 pre_push_thread.start()
                 pre_push_thread.start()
-                CUSTOMIZED_PUSH_LOGGER.info('customized_push_id:{}推送完成'.format(kwargs['id']))
+                CUSTOMIZED_PUSH_LOGGER.info('customized_push_id:{}推送完成'.format(customized_push_id))
         except Exception as e:
         except Exception as e:
             CUSTOMIZED_PUSH_LOGGER.info('定制化推送或保存数据异常,'
             CUSTOMIZED_PUSH_LOGGER.info('定制化推送或保存数据异常,'
                                         'error_line:{},error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
                                         'error_line:{},error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 6 - 2
Service/DevicePushService.py

@@ -18,7 +18,7 @@ import requests
 
 
 from AnsjerPush.Config.aiConfig import DEVICE_EVENT_TYPE, ALGORITHM_COMBO_TYPES
 from AnsjerPush.Config.aiConfig import DEVICE_EVENT_TYPE, ALGORITHM_COMBO_TYPES
 from AnsjerPush.config import CONFIG_INFO, CONFIG_CN, MULTI_CHANNEL_TYPE_LIST, SYS_EVENT_TYPE_LIST, AWS_ACCESS_KEY_ID, \
 from AnsjerPush.config import CONFIG_INFO, CONFIG_CN, MULTI_CHANNEL_TYPE_LIST, SYS_EVENT_TYPE_LIST, AWS_ACCESS_KEY_ID, \
-    AWS_SECRET_ACCESS_KEY, EVENT_DICT, EVENT_DICT_CN, CONFIG_US
+    AWS_SECRET_ACCESS_KEY, EVENT_DICT, EVENT_DICT_CN, CONFIG_US, CONFIG_TEST
 from AnsjerPush.config import XMPUSH_CONFIG, OPPOPUSH_CONFIG, XM_PUSH_CHANNEL_ID
 from AnsjerPush.config import XMPUSH_CONFIG, OPPOPUSH_CONFIG, XM_PUSH_CHANNEL_ID
 from Model.models import UidPushModel, SysMsgModel, DeviceSharePermission, DeviceChannelUserSet, \
 from Model.models import UidPushModel, SysMsgModel, DeviceSharePermission, DeviceChannelUserSet, \
     DeviceChannelUserPermission, UidSetModel, Device_Info
     DeviceChannelUserPermission, UidSetModel, Device_Info
@@ -253,6 +253,11 @@ class DevicePushService:
                     # 缓存数据多于100条,批量保存前100条,否则保存全部
                     # 缓存数据多于100条,批量保存前100条,否则保存全部
                     equipment_info_len = redis_obj.llen(equipment_info_key)
                     equipment_info_len = redis_obj.llen(equipment_info_key)
                     end = 99 if equipment_info_len > 100 else equipment_info_len - 1
                     end = 99 if equipment_info_len > 100 else equipment_info_len - 1
+                    if CONFIG_INFO == CONFIG_TEST:
+                        end = equipment_info_len
+                    LOGGING.info(
+                        'uid:{},time:{},存表:{},条数:{}, 事件类型:{}'.format(uid, params['n_time'], equipment_info_key, end,
+                                                                     params['event_type']))
 
 
                     if end != 0:
                     if end != 0:
                         equipment_info_redis_list = redis_obj.lrange(equipment_info_key, 0, end)
                         equipment_info_redis_list = redis_obj.lrange(equipment_info_key, 0, end)
@@ -266,7 +271,6 @@ class DevicePushService:
                                     encode('UTF-8', 'ignore').decode('UTF-8')
                                     encode('UTF-8', 'ignore').decode('UTF-8')
                             equipment_info_list.append(equipment_info_model(**equipment_info_data))
                             equipment_info_list.append(equipment_info_model(**equipment_info_data))
                         equipment_info_model.objects.bulk_create(equipment_info_list)
                         equipment_info_model.objects.bulk_create(equipment_info_list)
-
             return True
             return True
         except Exception as e:
         except Exception as e:
             LOGGING.info('推送消息或存表异常uid{}: error_line:{}, error_msg:{}'.format(uid, e.__traceback__.tb_lineno, repr(e)))
             LOGGING.info('推送消息或存表异常uid{}: error_line:{}, error_msg:{}'.format(uid, e.__traceback__.tb_lineno, repr(e)))

+ 16 - 6
Service/PushService.py

@@ -519,25 +519,35 @@ class PushObject:
             headers = {'Content-Type': 'application/json', 'Authorization': authorization_token,
             headers = {'Content-Type': 'application/json', 'Authorization': authorization_token,
                        'timestamp': str(int(time.time()) * 1000)}
                        'timestamp': str(int(time.time()) * 1000)}
             extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
             extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
-                          'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
-                          'uid': uid, 'channel': channel
+                          'received_at': n_time, 'event_time': n_time, 'event_type': str(event_type), 'nickname': nickname,
+                          'uid': uid, 'channel': channel, 'title': msg_title, 'body': msg_text
                           }
                           }
+            # 通知推送
             push_data = {
             push_data = {
                 "android": {
                 "android": {
-                    "data": json.dumps(extra_data),
                     "notification": {
                     "notification": {
                         "body": msg_text,
                         "body": msg_text,
                         "title": msg_title,
                         "title": msg_title,
+                        "importance": "NORMAL",
                         "clickAction": {
                         "clickAction": {
                             "type": 3
                             "type": 3
                         }
                         }
-                    }
+                    },
+                    "targetUserType": 0,
+                    "data": json.dumps(extra_data)
                 },
                 },
-                "notification": {},
                 "token": [token_val]
                 "token": [token_val]
             }
             }
             response = requests.post(push_url, json=push_data, headers=headers)
             response = requests.post(push_url, json=push_data, headers=headers)
-            LOGGER.info("uid{}时间{},荣耀推送返回值:{}".format(uid, n_time, response.json()))
+            LOGGER.info("uid:{},时间:{},荣耀推送通知返回值:{}".format(uid, n_time, response.json()))
+            # 一键通话透传推送
+            if int(event_type) in [606, 607]:
+                push_data = {
+                    "data": json.dumps(extra_data),
+                    "token": [token_val]
+                }
+                response = requests.post(push_url, json=push_data, headers=headers)
+                LOGGER.info("uid:{},时间:{},荣耀透传推送返回值:{}".format(uid, n_time, response.json()))
             return True
             return True
         except Exception as e:
         except Exception as e:
             LOGGER.info("荣耀推送异常:error_line:{},error_msg:{}".format(e.__traceback__.tb_lineno, repr(e)))
             LOGGER.info("荣耀推送异常:error_line:{},error_msg:{}".format(e.__traceback__.tb_lineno, repr(e)))