合并
This commit is contained in:
liangweihao 2025-05-28 09:52:25 +08:00
commit f5a3aaaab0
1166 changed files with 125485 additions and 3556 deletions

10
IoT/config.yaml Normal file
View File

@ -0,0 +1,10 @@
server_uri: "6396c4c5d6.st1.iotda-device.cn-south-1.myhuaweicloud.com"
port: 1883
product_id: "673602f0d14760402fbd033b"
secret: "RobotStorm"
max_files: 20
watch_dirs:
- "/home/jsfb/jsfb_ws/collected_data"
- "/home/jsfb/jsfb_ws/LanguageLog"
- "/home/jsfb/jsfb_ws/UILog"
- "/home/jsfb/jsfb_ws/MassageLog"

20
IoT/iot.service Executable file
View File

@ -0,0 +1,20 @@
[Unit]
Description=IoT service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/home/jsfb/jsfb_ws/MassageRobot_Dobot/IoT
Environment="PATH=/home/jsfb/anaconda3/envs/CPU_robotarm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/home/jsfb/anaconda3/envs/CPU_robotarm/bin/python iot_lite.pyc
Restart=always
RestartSec=5s
StartLimitIntervalSec=0
StartLimitBurst=0
User=jsfb
Group=jsfb
TimeoutStopSec=5
[Install]
WantedBy=multi-user.target

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,48 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
from iot_device_sdk_python.client.connect_auth_info import ConnectAuthInfo
from iot_device_sdk_python.client.mqtt_connect_conf import MqttConnectConf
class ClientConf:
"""
客户端配置
"""
def __init__(self, connect_auth_info: ConnectAuthInfo, mqtt_connect_conf: Optional[MqttConnectConf] = None):
self.__connect_auth_info = connect_auth_info
self.__mqtt_connect_conf = MqttConnectConf()
if mqtt_connect_conf is not None:
self.__mqtt_connect_conf = mqtt_connect_conf
@property
def connect_auth_info(self):
return self.__connect_auth_info
@connect_auth_info.setter
def connect_auth_info(self, value):
self.__connect_auth_info = value
@property
def mqtt_connect_conf(self):
return self.__mqtt_connect_conf
@mqtt_connect_conf.setter
def mqtt_connect_conf(self, value):
self.__mqtt_connect_conf = value

Binary file not shown.

View File

@ -0,0 +1,319 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
class ConnectAuthInfo:
"""
连接鉴权配置
"""
SECRET_AUTH = 0
X509_AUTH = 1
PROTOCOL_MQTT = "MQTT"
BS_MODE_DIRECT_CONNECT = 0
BS_MODE_STANDARD_BOOTSTRAP = 1
BS_MODE_BOOTSTRAP_WITH_SCOPEID = 2
def __init__(self):
""" id在平台注册设备获得 """
self._id: Optional[str] = None
""" 认证的类型0表示密码方式1表示x509证书方式默认为0 """
self._auth_type: int = self.SECRET_AUTH
""" 设备密码 """
self._secret: Optional[str] = None
""" x509证书的pem文件路径 """
self._cert_path: Optional[str] = None
""" x509证书的key文件路径 """
self._key_path: Optional[str] = None
""" iot平台的ca证书存放路径用于设备侧校验平台 """
self._iot_cert_file: Optional[str] = None
""" 设备自注册场景下使用 """
self._scope_id: Optional[str] = None
""" 设备接入平台地址(不包括端口) """
self._server_uri: Optional[str] = None
""" 端口 """
self._port: Optional[int] = None
""" 协议类型不填则默认为MQTT """
self._protocol: str = self.PROTOCOL_MQTT
""" 0表示直连模式1表示标准设备发放流程2表示通过自注册的方式进行批量发放默认为0 """
self._bs_mode: int = self.BS_MODE_DIRECT_CONNECT
""" 设备发放平台的证书路径 """
self._bs_cert_path: Optional[str] = None
""" 设备发放时上报的消息 ,在静态策略数据上报方式中,需要在上报的属性 “baseStrategyKeyword” 包含设置的关键字"""
self._bs_message: Optional[str] = None
""" 是否校验时间戳,"0"表示HMACSHA256不校验时间戳"1"表示HMACSHA256校验时间戳默认为"1""""
self._check_timestamp: str = "1"
""" 是否支持重连True表示支持重连 False表示不支持重连"""
self._reconnect_on_failure = True
""" 最小退避时间, 默认1s"""
self._min_backoff = 1 * 1000 # 1s
""" 最大退避时间默认30s"""
self._max_backoff = 30 * 1000
""" 是否开启端侧规则"""
self._enable_rule_manage = False
""" max buffer max"""
self._max_buffer_message = 0
""" qos1时最多可以同时发布多条消息默认20条 """
self._inflight_messages: Optional[int] = 20
""" 是否自动上报版本号"""
self._auto_report_device_info: Optional[bool] = False
@property
def id(self):
"""
id在平台注册设备获得
"""
return self._id
@id.setter
def id(self, value):
self._id = value
@property
def auth_type(self):
"""
认证的类型0表示密码方式1表示x509证书方式默认为0
"""
return self._auth_type
@auth_type.setter
def auth_type(self, value):
self._auth_type = value
@property
def secret(self):
"""
设备密码
"""
return self._secret
@secret.setter
def secret(self, value):
self._secret = value
@property
def cert_path(self):
"""
x509证书的pem文件路径
"""
return self._cert_path
@cert_path.setter
def cert_path(self, value):
self._cert_path = value
@property
def key_path(self):
"""
x509证书的key文件路径
"""
return self._key_path
@key_path.setter
def key_path(self, value):
self._key_path = value
@property
def iot_cert_path(self):
"""
iot平台的ca证书存放路径用于设备侧校验平台
"""
return self._iot_cert_file
@iot_cert_path.setter
def iot_cert_path(self, value):
self._iot_cert_file = value
@property
def server_uri(self):
"""
设备接入平台地址不包括端口
"""
return self._server_uri
@server_uri.setter
def server_uri(self, value):
self._server_uri = value
@property
def port(self):
"""
端口
"""
return self._port
@port.setter
def port(self, value):
self._port = value
@property
def protocol(self):
"""
协议类型不填则默认为MQTT
"""
return self._protocol
@protocol.setter
def protocol(self, value):
self._protocol = value
@property
def scope_id(self):
"""
设备自注册场景下使用
"""
return self._scope_id
@scope_id.setter
def scope_id(self, value):
self._scope_id = value
@property
def bs_mode(self):
"""
是否为设备发放场景默认为1
"""
return self._bs_mode
@bs_mode.setter
def bs_mode(self, value):
self._bs_mode = value
@property
def bs_cert_path(self):
"""
设备发放平台的证书路径
"""
return self._bs_cert_path
@bs_cert_path.setter
def bs_cert_path(self, value):
self._bs_cert_path = value
@property
def bs_message(self):
"""
静态策略数据上报方式下上报的数据
"""
return self._bs_message
@bs_message.setter
def bs_message(self, value):
self._bs_message = value
@property
def check_timestamp(self):
"""
是否校验时间戳默认为"1"
"""
return self._check_timestamp
@check_timestamp.setter
def check_timestamp(self, value):
self._check_timestamp = value
@property
def reconnect_on_failure(self):
"""
是否支持重连TRUE支持重连
"""
return self._reconnect_on_failure
@reconnect_on_failure.setter
def reconnect_on_failure(self, value):
self._reconnect_on_failure = value
@property
def min_backoff(self):
"""
最小退避时间
"""
return self._min_backoff
@min_backoff.setter
def min_backoff(self, value):
self._min_backoff = value
@property
def max_backoff(self):
"""
最大退避时间
"""
return self._max_backoff
@max_backoff.setter
def max_backoff(self, value):
self._max_backoff = value
@property
def enable_rule_manage(self):
"""
是否支持端侧规则
"""
return self._enable_rule_manage
@enable_rule_manage.setter
def enable_rule_manage(self, value):
self._enable_rule_manage = value
@property
def max_buffer_message(self):
"""
最大缓存消息默认为0不缓存消息
断链时生产失败的消息存放队列待重连后重新发送
"""
return self._max_buffer_message
@max_buffer_message.setter
def max_buffer_message(self, value):
self._max_buffer_message = value
@property
def inflight_messages(self):
"""
qos1时最多可以同时发布多条消息默认20条
"""
return self._inflight_messages
@inflight_messages.setter
def inflight_messages(self, value):
self._inflight_messages = value
@property
def auto_report_device_info(self):
"""
qos1时最多可以同时发布多条消息默认20条
"""
return self._auto_report_device_info
@auto_report_device_info.setter
def auto_report_device_info(self, value):
self._auto_report_device_info = value

Binary file not shown.

View File

@ -0,0 +1,798 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
设备客户端提供和平台的通讯能力包括
消息双向异步不需要定义模型
属性双向设备可以上报属性平台可以向设备读写属性属性需要在模型定义
命令单向同步平台向设备调用设备的命令
时间双向异步需要在模型定义
用户不能直接创建DeviceClient实例只能先创建IoTDevice实例然后通过IoTDevice的get_client方法获取DeviceClient实例
"""
from __future__ import absolute_import, division, annotations
from typing import TYPE_CHECKING, List, Optional
import json
import logging
import time
import traceback
import sys
import os
import stat
from iot_device_sdk_python.client.connect_auth_info import ConnectAuthInfo
from iot_device_sdk_python.client.iot_result import IotResult
from iot_device_sdk_python.client.listener.command_listener import CommandListener
from iot_device_sdk_python.client.listener.device_message_listener import DeviceMessageListener
from iot_device_sdk_python.client.listener.device_shadow_listener import DeviceShadowListener
from iot_device_sdk_python.client.listener.property_listener import PropertyListener
from iot_device_sdk_python.client.listener.raw_device_message_listener import RawDeviceMessageListener
from iot_device_sdk_python.client.mqtt_connect_conf import MqttConnectConf
from iot_device_sdk_python.client.request.device_message import DeviceMessage
from iot_device_sdk_python.client.request.raw_device_message import RawDeviceMessage
from iot_device_sdk_python.client.request.shadow_data import ShadowData
from iot_device_sdk_python.transport.mqtt.mqtt_connection import MqttConnection
from iot_device_sdk_python.client.request.device_event import DeviceEvent
from iot_device_sdk_python.client.request.device_events import DeviceEvents
from iot_device_sdk_python.transport.raw_message import RawMessage
from iot_device_sdk_python.transport.raw_message_listener import RawMessageListener
from iot_device_sdk_python.transport.action_listener import ActionListener
from iot_device_sdk_python.transport.connection import Connection
from iot_device_sdk_python.utils.iot_util import get_request_id_from_msg, str_is_empty, get_event_time
from iot_device_sdk_python.client.request.service_property import ServiceProperty
from iot_device_sdk_python.client.request.command import Command
from iot_device_sdk_python.client.request.command_response import CommandRsp
from iot_device_sdk_python.client.request.props_set import PropSet
from iot_device_sdk_python.client.request.props_get import PropsGet
from iot_device_sdk_python.client.request.device_base_info import DeviceBaseInfo
from iot_device_sdk_python.rule.model.action_handler import ActionHandler
from iot_device_sdk_python.rule.model.actions import Action
if TYPE_CHECKING:
from iot_device_sdk_python.service.abstract_device import AbstractDevice
class DeviceClient(RawMessageListener):
_logger = logging.getLogger(__name__)
# SDK版本信息不能更改
__SDK_VERSION = "Python_v1.2.0"
__SERVER_INFO_PATH = os.path.join(sys.path[0], "server_info.json")
__SERVER_URI = "server_uri"
__PORT = "port"
__SECRET = "secret"
__BOOTSTRAP_TIMEOUT = 10.0
def __init__(self, connect_auth_info: ConnectAuthInfo, mqtt_connect_conf: MqttConnectConf, device: AbstractDevice):
self.check_connect_auth_info(connect_auth_info)
self.check_mqtt_connect_conf(mqtt_connect_conf)
self.__connect_auth_info = connect_auth_info
self.__mqtt_connect_conf = mqtt_connect_conf
self._device = device
self.__connection: Optional[Connection] = None
if self.__connect_auth_info.protocol == ConnectAuthInfo.PROTOCOL_MQTT:
self.__connection = MqttConnection(connect_auth_info, mqtt_connect_conf, self)
else:
self._logger.error("Current SDK only supports PROTOCOL_MQTT.")
return
# 设备发放是否成功
self.__bs_flag = False
# 是否开启端侧规则
self.__enable_rule_manage = connect_auth_info.enable_rule_manage
# raw_msg_listener是原始消息接收监听器
self.__raw_msg_listener_map = dict()
# 设置原始消息监听器,用于接收平台下发的原始设备消息
self.__raw_device_msg_listener: Optional[RawDeviceMessageListener] = None
# 设置消息监听器,用于接收平台下发的设备消息
self.__device_msg_listener: Optional[DeviceMessageListener] = None
# 属性监听器,用于接收平台下发的属性读写操作
self.__property_listener: Optional[PropertyListener] = None
# 命令监听器,用于接收平台下发的命令
self.__command_listener: Optional[CommandListener] = None
# 影子监听器,用于接收平台下发的设备影子数据
self.__shadow_listener: Optional[DeviceShadowListener] = None
# 端侧规则监听器,用于自定义处理端侧规则
self.__rule_action_handler: Optional[ActionHandler] = None
@staticmethod
def check_connect_auth_info(connect_auth_info: ConnectAuthInfo):
"""
检查连接鉴权配置若配置有问题则抛出错误
Args:
connect_auth_info: 连接鉴权配置
"""
if connect_auth_info is None:
raise ValueError("ConnectAuthInfo is null")
if str_is_empty(connect_auth_info.id):
raise ValueError("ConnectAuthInfo id is invalid")
if connect_auth_info.protocol != ConnectAuthInfo.PROTOCOL_MQTT:
# 当前SDK只支持MQTT协议
raise ValueError("ConnectAuthInfo protocol is invalid, currently protocol only support MQTT")
if str_is_empty(connect_auth_info.server_uri):
raise ValueError("ConnectAuthInfo server_uri is invalid")
if connect_auth_info.auth_type not in [ConnectAuthInfo.SECRET_AUTH, ConnectAuthInfo.X509_AUTH]:
raise ValueError("ConnectAuthInfo auth_type is invalid")
if str_is_empty(connect_auth_info.secret) and (
str_is_empty(connect_auth_info.cert_path) or str_is_empty(connect_auth_info.key_path)) is None:
raise ValueError("ConnectAuthInfo secret or certificate is invalid")
if connect_auth_info.port != 1883 and connect_auth_info.port != 8883:
raise ValueError("ConnectAuthInfo port is invalid")
if connect_auth_info.port == 8883 and str_is_empty(connect_auth_info.iot_cert_path):
raise ValueError("ConnectAuthInfo iot_cert_path is invalid")
if connect_auth_info.bs_mode not in [ConnectAuthInfo.BS_MODE_DIRECT_CONNECT,
ConnectAuthInfo.BS_MODE_STANDARD_BOOTSTRAP,
ConnectAuthInfo.BS_MODE_BOOTSTRAP_WITH_SCOPEID]:
raise ValueError("ConnectAuthInfo bs_mode is invalid")
if connect_auth_info.bs_mode == ConnectAuthInfo.BS_MODE_BOOTSTRAP_WITH_SCOPEID and str_is_empty(
connect_auth_info.scope_id):
raise ValueError("ConnectAuthInfo scope_id is invalid")
if connect_auth_info.check_timestamp not in ["0", "1"]:
raise ValueError("ConnectAuthInfo check_timestamp is invalid")
@staticmethod
def check_mqtt_connect_conf(mqtt_connect_conf: MqttConnectConf):
"""
检查mqtt配置若配置有问题则抛出错误
Args:
mqtt_connect_conf: mqtt配置
"""
if mqtt_connect_conf is None:
raise ValueError("MqttConnectConf is null")
if not isinstance(mqtt_connect_conf.keep_alive_time, int) \
or mqtt_connect_conf.keep_alive_time < 30 \
or mqtt_connect_conf.keep_alive_time > 1200:
raise ValueError("MqttConnectConf keep_alive_time is invalid")
if not isinstance(mqtt_connect_conf.qos, int) or mqtt_connect_conf.qos not in [0, 1]:
raise ValueError("MqttConnectConf qos is invalid")
if not isinstance(mqtt_connect_conf.timeout, float):
raise ValueError("MqttConnectConf timeout is invalid")
def connect(self):
"""
和平台建立连接连接成功时SDK将自动向平台订阅系统定义的topic
Returns:
int: 结果码0表示连接成功其他表示连接失败
"""
if self.__connect_auth_info.bs_mode == ConnectAuthInfo.BS_MODE_STANDARD_BOOTSTRAP or \
self.__connect_auth_info.bs_mode == ConnectAuthInfo.BS_MODE_BOOTSTRAP_WITH_SCOPEID:
# 设备发放场景
if os.path.exists(self.__SERVER_INFO_PATH):
server_info_dict = dict()
try:
# 已成功进行过设备发放从文件中读取iot平台连接信息
with open(self.__SERVER_INFO_PATH, 'r') as server_info:
server_info_dict: dict = json.load(server_info)
except Exception:
self._logger.error("load server_info failed, traceback: %s", traceback.format_exc())
if "server_uri" in server_info_dict.keys() and "port" in server_info_dict.keys():
server_uri = server_info_dict.get(self.__SERVER_URI)
port = server_info_dict.get(self.__PORT)
secret = server_info_dict.get(self.__SECRET)
self.__connect_auth_info.server_uri = server_uri
self.__connect_auth_info.port = port
if secret:
self.__connect_auth_info.secret = secret
else:
# 进行设备发放
rc = self.__bootstrap()
if rc != 0:
# 发放失败
self._logger.error("bootstrap device failed.")
return rc
else:
# 进行设备发放
rc = self.__bootstrap()
if rc != 0:
# 发放失败
self._logger.error("bootstrap device failed.")
return rc
# 建立设备到iot平台的连接 将连接模式设置为直连。
self.__connect_auth_info.bs_mode = ConnectAuthInfo.BS_MODE_DIRECT_CONNECT
rc = self.__connect()
if rc != 0:
return rc
if self.__connect_auth_info.auto_report_device_info:
# 建链成功后SDK自动上报版本号软固件版本号由设备上报
self.report_device_info(DeviceBaseInfo())
return rc
def __connect(self):
"""
和平台建立连接连接成功时SDK将自动向平台订阅系统定义的topic
Returns:
int: 结果码0表示连接成功其他表示连接失败
"""
return self.__connection.connect()
def __bootstrap(self):
"""
进行设备发放流程返回 0表示成功返回其它表示失败
"""
rc = self.__connect()
if rc != 0:
return rc
bs_topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/bootstrap/down"
self.__connection.subscribe_topic(bs_topic, 0)
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/bootstrap/up"
raw_message = RawMessage(topic, self.__connect_auth_info.bs_message)
self.publish_raw_message(raw_message)
start_time = time.time()
while True:
# 等待设备发放成功
time.sleep(1)
if self.__bs_flag:
break
now = time.time()
if now - start_time > self.__BOOTSTRAP_TIMEOUT:
self._logger.error("bootstrap failed, timeout.")
return -1
# 释放设备到发放服务端的连接
self.close()
return rc
def _get_connection(self):
return self.__connection
def close(self):
""" 释放connection连接 """
self.__connection.close()
def on_message_received(self, message: RawMessage):
"""
收到原始消息后依据topic的不同调用不同的方法
Args:
message: 原始数据
"""
try:
topic = message.topic
# 若订阅了自定义的topic这里先检查接收到的topic是否是自定义的
raw_msg_listener = self.__raw_msg_listener_map[topic] if topic in self.__raw_msg_listener_map else None
if raw_msg_listener is not None:
raw_msg_listener.on_message_received(message)
return
if "/messages/down" in topic:
self.on_device_msg(message) # 平台下发消息到设备测
elif "sys/commands/request_id" in topic:
self.on_command(message) # 平台下发指令到设备侧
elif "/sys/properties/set/request_id" in topic:
self.on_properties_set(message) # 处理写属性操作
elif "/sys/properties/get/request_id" in topic:
self.on_properties_get(message) # 处理读属性操作
elif "/sys/shadow/get/response" in topic:
self.on_device_shadow(message) # 处理获取平台设备影子数据
elif "/sys/events/down" in topic:
self.on_event(message) # 处理平台下发事件
elif "/sys/bootstrap/down" in topic:
self.on_bootstrap(message)
else:
self._logger.warning("unknown topic: %s", topic)
except Exception as e:
self._logger.error("on_message_received error, tracback: %s", traceback.format_exc())
self._logger.error("on_message_received error: %s", str(e))
def report_device_message(self, device_message: DeviceMessage, listener: Optional[ActionListener] = None):
"""
上报设备消息
Args:
device_message: 设备消息
listener: 发布监听器若不设置监听器则设为None
"""
topic = '$oc/devices/' + self.__connect_auth_info.id + '/sys/messages/up'
try:
payload = json.dumps(device_message.to_dict())
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def report_properties(self, services: List[ServiceProperty], listener: Optional[ActionListener] = None):
"""
上报设备属性
Args:
services: 设备属性列表
listener: 发布监听器若不设置监听器则设为None
"""
topic = '$oc/devices/' + self.__connect_auth_info.id + '/sys/properties/report'
service_list = list()
for service in services:
service_list.append(service.to_dict())
try:
payload = json.dumps({"services": service_list})
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
"""
处理端侧规则
"""
if self.__enable_rule_manage:
self._device.get_rule_manage_service().handle_rule(services)
def get_device_shadow(self, request_id: str, service_id: Optional[str] = None,
object_device_id: Optional[str] = None, listener: Optional[ActionListener] = None):
"""
设备侧获取平台的设备影子数据
Args:
request_id: 请求id
service_id: 服务id
object_device_id: device_id
listener: 发布监听器若不设置监听器则设为None
"""
if object_device_id is not None:
topic = "$oc/devices/" + object_device_id + "/sys/shadow/get/request_id=" + request_id
else:
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/shadow/get/request_id=" + request_id
payload_dict = dict()
if service_id is not None:
payload_dict["service_id"] = service_id
try:
payload = json.dumps(payload_dict)
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def report_event(self, device_event: DeviceEvent, listener: Optional[ActionListener] = None):
"""
事件上报
Args:
device_event: 事件
listener: 发布监听器若不设置监听器则设为None
"""
device_events = DeviceEvents()
device_events.device_id = self.__connect_auth_info.id
device_events.services = [device_event]
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/events/up"
try:
payload = json.dumps(device_events.to_dict())
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def report_sub_event(self, sub_device_id: str, device_event: DeviceEvent,
listener: Optional[ActionListener] = None):
"""
子设备事件上报
Args:
sub_device_id: 子设备ID
device_event: 事件
listener: 发布监听器若不设置监听器则设为None
"""
device_events = DeviceEvents()
device_events.device_id = self.__connect_auth_info.id
if sub_device_id is not None:
device_events.device_id = sub_device_id
device_events.services = [device_event]
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/events/up"
try:
payload = json.dumps(device_events.to_dict())
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def respond_command(self, request_id: str, command_response: CommandRsp, listener: Optional[ActionListener] = None):
"""
上报命令响应
Args:
request_id: 请求id响应的请求id必须和请求的一致
command_response: 命令响应
listener: 发布监听器
"""
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/commands/response/request_id=" + request_id
try:
payload = json.dumps(command_response.to_dict())
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def publish_raw_message(self, raw_message: RawMessage, listener: Optional[ActionListener] = None):
"""
发布消息
Args:
raw_message: 消息
listener: 发布监听器若不设置监听器则设为None
"""
self.__connection.publish_message(raw_message, listener)
def on_device_msg(self, message: RawMessage):
"""
处理平台消息下发若当前DeviceClient设置了消息监听器则执行此消息监听器的on_device_message()方法
Args:
message: 原始数据
"""
self._logger.debug(f"receive message from platform, topic = %s, msg = %s", message.topic, message.payload)
raw_device_message = RawDeviceMessage(message.payload)
device_msg = raw_device_message.to_device_message()
if self.__raw_device_msg_listener is not None:
self.__raw_device_msg_listener.on_raw_device_message(raw_device_message)
if device_msg is not None:
is_current_device = device_msg.device_id is None \
or len(device_msg.device_id) == 0 \
or device_msg.device_id == self.__connect_auth_info.id
if self.__device_msg_listener is not None and is_current_device:
self.__device_msg_listener.on_device_message(device_msg)
else:
self._device.on_device_message(device_msg)
def on_command(self, message: RawMessage):
"""
处理平台命令下发若当前DeviceClient设置了命令监听器则执行此命令监听器的on_command()方法
Args:
message: 原始数据
"""
request_id = get_request_id_from_msg(message)
try:
self._logger.debug("receive command from platform, topic = %s, msg = %s", message.topic,
str(message.payload))
cmd = json.loads(message.payload)
except Exception as e:
self._logger.error("json.loads failed, Exception: %s", str(e))
raise e
command = Command()
command.convert_from_dict(cmd)
if self.__command_listener is not None:
self.__command_listener.on_command(request_id, command.service_id, command.command_name,
command.paras)
else:
self._device.on_command(request_id, command)
def on_rule_command(self, request_id: str, command: Command):
if self.__command_listener is not None:
self.__command_listener.on_command(request_id, command.service_id, command.command_name, command.paras)
return
self._logger.warning("command listener was not config for rules.")
def on_device_shadow(self, message: RawMessage):
"""
处理平台设备影子数据下发若当前DeviceClient设置了影子监听器则执行此命令监听器的on_shadow_get()方法
Args:
message: 原始数据
"""
request_id = get_request_id_from_msg(message)
try:
payload: dict = json.loads(message.payload)
device_id: str = payload.get("object_device_id")
shadow_list: List[ShadowData] = list()
shadow_dict_list: list = payload.get("shadow")
for shadow_dict in shadow_dict_list:
shadow = ShadowData()
shadow.convert_from_dict(shadow_dict)
shadow_list.append(shadow)
if self.__shadow_listener is not None:
self.__shadow_listener.on_shadow_get(request_id, device_id, shadow_list)
else:
# 没有设置影子监听器,这里不做处理
pass
except Exception as e:
self._logger.error("handle device shadow failed, Exception: %s", str(e))
pass
def on_properties_set(self, message: RawMessage):
"""
处理平台设置设备属性若当前DeviceClient设置了属性监听器则执行此命令监听器的on_property_set()方法
Args:
message: 原始数据
"""
request_id = get_request_id_from_msg(message)
try:
self._logger.debug("receive properties_set from platform, topic = %s, msg = %s",
message.topic, str(message.payload))
payload: dict = json.loads(message.payload)
except Exception as e:
self._logger.error("json.loads failed, Exception: %s", str(e))
raise e
prop_set: PropSet = PropSet()
service_list: list = payload["services"]
service_property_list = [ServiceProperty(service_id=a["service_id"],
properties=a["properties"]) for a in service_list]
prop_set.services = service_property_list
if self.__property_listener is not None:
self.__property_listener.on_property_set(request_id, prop_set.services)
else:
self._device.on_properties_set(request_id, prop_set)
def on_properties_get(self, message: RawMessage):
"""
处理平台查询设备属性若当前DeviceClient设置了属性监听器则执行此命令监听器的on_property_get()方法
Args:
message: 原始数据
"""
request_id = get_request_id_from_msg(message)
try:
self._logger.debug("receive properties_get from platform, topic = %s, msg = %s", message.topic,
str(message.payload))
obj = json.loads(message.payload)
except Exception as e:
self._logger.error("json.loads failed, Exception: %s", str(e))
raise e
prop_get = PropsGet()
prop_get.convert_from_dict(obj)
if self.__property_listener is not None:
self.__property_listener.on_property_get(request_id, prop_get.service_id)
else:
self._device.on_properties_get(request_id, prop_get)
def on_event(self, message: RawMessage):
"""
处理平台事件下发
Args:
message: 原始数据
"""
try:
self._logger.debug("receive events from platform, topic = %s, msg = %s", message.topic,
str(message.payload))
payload: dict = json.loads(message.payload)
except Exception as e:
self._logger.error("json.loads failed, Exception: %s", str(e))
raise e
device_events = DeviceEvents()
device_events.convert_from_dict(payload)
if not device_events:
self._logger.error("device events invalid, payload: %s", str(payload))
return
self._device.on_event(device_events)
def on_rule_action_handler(self, action: List[Action]):
"""
处理端侧规则
Args:
action: 原始数据
"""
if self.__rule_action_handler is not None:
self.__rule_action_handler.handle_rule_action(action)
return
self._device.on_rule_action_handler(action)
def on_bootstrap(self, message: RawMessage):
"""
处理设备发放信息
Args:
message: 原始数据
"""
try:
self._logger.debug("receive bootstrap info from platform, topic = %s, msg = %s", message.topic,
str(message.payload))
payload: dict = json.loads(message.payload)
except Exception as e:
self._logger.error("json.loads failed, Exception: %s", str(e))
raise e
address = str(payload.get("address"))
device_secret = payload.get("deviceSecret")
self.__connect_auth_info.server_uri = address.split(":")[0]
self.__connect_auth_info.port = int(address.split(":")[-1])
if device_secret:
self.__connect_auth_info.secret = device_secret
# 设备发放成功保存获取的iot平台地址和端口
if os.path.exists(self.__SERVER_INFO_PATH):
os.remove(self.__SERVER_INFO_PATH)
server_info_dict = {self.__SERVER_URI: self.__connect_auth_info.server_uri,
self.__PORT: self.__connect_auth_info.port,
self.__SECRET: device_secret}
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
modes = stat.S_IWUSR | stat.S_IRUSR
with os.fdopen(os.open(self.__SERVER_INFO_PATH, flags, modes), 'w') as server_info:
json.dump(server_info_dict, server_info)
self._logger.info("bootstrap success, change server address to %s", address)
self.__bs_flag = True
def respond_properties_get(self, request_id: str, services: List[ServiceProperty],
listener: Optional[ActionListener] = None):
"""
上报读属性响应
Args:
request_id: 请求id响应的请求id必须和请求的一致
services: 设备属性列表
listener: 发布监听器
"""
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/properties/get/response/request_id=" + request_id
service_list = list()
for service in services:
service_list.append(service.to_dict())
try:
payload = json.dumps({"services": service_list})
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def respond_properties_set(self, request_id: str, iot_result: IotResult, listener: Optional[ActionListener] = None):
"""
上报写属性响应
Args:
request_id: 请求id响应的请求id必须和请求的一致
iot_result: 写属性结果
listener: 发布监听器
"""
topic = "$oc/devices/" + self.__connect_auth_info.id + "/sys/properties/set/response/request_id=" + request_id
try:
payload = json.dumps(iot_result.to_dict())
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
self.publish_raw_message(RawMessage(topic, payload, self.__mqtt_connect_conf.qos), listener)
def set_raw_device_msg_listener(self, raw_device_msg_listener: RawDeviceMessageListener):
"""
设置原始消息监听器用于接收平台下发的消息消息保持为二进制格式
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
raw_device_msg_listener: 消息监听器
"""
if not isinstance(raw_device_msg_listener, RawDeviceMessageListener):
self._logger.error("device_msg_listener should be RawDeviceMessageListener type")
return
self.__raw_device_msg_listener = raw_device_msg_listener
def set_device_msg_listener(self, device_msg_listener: DeviceMessageListener):
"""
设置消息监听器用于接收平台下发的消息
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
device_msg_listener: 消息监听器
"""
if not isinstance(device_msg_listener, DeviceMessageListener):
self._logger.error("device_msg_listener should be DeviceMessageListener type")
return
self.__device_msg_listener = device_msg_listener
def set_properties_listener(self, property_listener: PropertyListener):
"""
设置属性监听器用于接收平台下发的属性读写
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
property_listener: 属性监听器
"""
if not isinstance(property_listener, PropertyListener):
self._logger.error("property_listener should be PropertyListener")
return
self.__property_listener = property_listener
def set_command_listener(self, command_listener: CommandListener):
"""
设置命令监听器用于接收平台下发的命令
需要通过IoTDevice的getClient接口获取DeviceClient实例后调用此方法设置命令监听器
Args:
command_listener: 命令监听器
"""
if not isinstance(command_listener, CommandListener):
self._logger.error("command_listener should be CommandListener")
return
self.__command_listener = command_listener
def set_device_shadow_listener(self, device_shadow_listener: DeviceShadowListener):
"""
设置影子监听器用于接收平台下发的设备影子数据
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
device_shadow_listener: 影子监听器
"""
if not isinstance(device_shadow_listener, DeviceShadowListener):
self._logger.error("device_shadow_listener should be DeviceShadowListener")
return
self.__shadow_listener = device_shadow_listener
def set_rule_action_handler(self, rule_action_handler: ActionHandler):
"""
设置端侧规则监听器用于自定义处理端侧规则
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
rule_action_handler: 端侧规则监听器
"""
if not isinstance(rule_action_handler, ActionHandler):
self._logger.error("rule_action_handler should be ActionHandler")
return
self.__rule_action_handler = rule_action_handler
def subscribe_topic(self, topic: str, qos: int, message_listener):
"""
订阅自定义topic此接口只能用于订阅自定义topic
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
topic: 自定义topic
qos: qos
message_listener: 接收自定义消息的监听器
"""
self.__connection.subscribe_topic(topic, qos)
self.__raw_msg_listener_map[topic] = message_listener
def add_connect_listener(self, connect_listener):
"""
设置链路监听器用户接收链路建立和断开事件
Args:
connect_listener: 链路监听器
"""
self.__connection.add_connect_listener(connect_listener)
def set_connect_action_listener(self, connect_action_listener):
"""
设置连接动作监听器用户接受连接成功或失败的事件
Args:
connect_action_listener: 连接动作监听器
"""
self.__connection.set_connect_action_listener(connect_action_listener)
def report_device_info(self, device_info: DeviceBaseInfo, listener: Optional[ActionListener] = None):
"""
上报设备信息包括软件版本硬件版本以及SDK版本
需要通过IoTDevice的getClient方法获取DeviceClient实例后调用此方法设置消息监听器
Args:
device_info: 设备信息
listener: 发布监听器若不设置监听器则设为None
"""
device_event = DeviceEvent()
device_event.service_id = "$sdk_info"
device_event.event_type = "sdk_info_report"
device_event.event_time = get_event_time()
paras: dict = {"device_sdk_version": self.__SDK_VERSION,
"sw_version": device_info.sw_version,
"fw_version": device_info.fw_version}
device_event.paras = paras
self.report_event(device_event, listener)
def enable_rule_manage(self):
return self.__enable_rule_manage

Binary file not shown.

View File

@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class IotResult:
"""
处理结果
"""
def __init__(self, result_code: int, result_desc: str):
self._result_code: int = result_code
self._result_desc: str = result_desc
@property
def result_code(self):
"""
结果码0表示成功其他为失败
"""
return self._result_code
@result_code.setter
def result_code(self, value):
self._result_code = value
@property
def result_desc(self):
"""
结果描述
"""
return self._result_desc
@result_desc.setter
def result_desc(self, value):
self._result_desc = value
def to_dict(self):
return {"result_code": self._result_code, "result_desc": self._result_desc}
SUCCESS = IotResult(0, "Success")
FAIL = IotResult(1, "Fail")
TIMEOUT = IotResult(2, "Timeout")

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,35 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from abc import abstractmethod, ABCMeta
class CommandListener(metaclass=ABCMeta):
"""
命令监听器用于接收平台下发的命令
"""
@abstractmethod
def on_command(self, request_id: str, service_id: str, command_name: str, paras: dict):
"""
命令处理
Args:
request_id: 请求id
service_id: 服务id
command_name: 命令名
paras: 命令参数
"""

View File

@ -0,0 +1,36 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
from iot_device_sdk_python.transport.action_listener import ActionListener
class DefaultPublishActionListener(ActionListener):
"""
默认发布监听器用户可自行实现ActionListener类
"""
def on_success(self, message: str):
"""
发布成功
"""
print(message)
def on_failure(self, message: str, e: Optional[Exception]):
"""
发布失败
"""
print(message)

View File

@ -0,0 +1,33 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from abc import abstractmethod, ABCMeta
from iot_device_sdk_python.client.request.device_message import DeviceMessage
class DeviceMessageListener(metaclass=ABCMeta):
"""
设备消息监听器用于接收平台下发的设备消息
"""
@abstractmethod
def on_device_message(self, message: DeviceMessage):
"""
处理平台下发的设备消息
Args:
message: 设备消息内容
"""

View File

@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from abc import abstractmethod, ABCMeta
from typing import List
from iot_device_sdk_python.client.request.shadow_data import ShadowData
class DeviceShadowListener(metaclass=ABCMeta):
"""
影子数据下发监听器
"""
@abstractmethod
def on_shadow_get(self, request_id: str, object_device_id: str, shadow: List[ShadowData]):
"""
处理平台下发的设备影子数据
Args:
request_id: 请求id
object_device_id: 设备id
shadow: 影子数据
"""

View File

@ -0,0 +1,47 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List
from abc import abstractmethod, ABCMeta
from iot_device_sdk_python.client.request.service_property import ServiceProperty
class PropertyListener(metaclass=ABCMeta):
"""
属性监听器用于接收平台下发的属性读写操作
"""
@abstractmethod
def on_property_set(self, request_id: str, services: List[ServiceProperty]):
"""
处理写属性操作
Args:
request_id: 请求id
services: 服务属性列表
"""
@abstractmethod
def on_property_get(self, request_id: str, service_id: str):
"""
处理读属性操作
Args:
request_id: 请求id
service_id: 服务id,可选
"""

View File

@ -0,0 +1,35 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from abc import abstractmethod, ABCMeta
from iot_device_sdk_python.client.request.raw_device_message import RawDeviceMessage
class RawDeviceMessageListener(metaclass=ABCMeta):
"""
设备消息监听器用于接收平台下发的设备消息
"""
@abstractmethod
def on_raw_device_message(self, message: RawDeviceMessage):
"""
处理平台下发的设备消息
Args:
message: 设备消息内容
"""

View File

@ -0,0 +1,58 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
class MqttConnectConf:
"""
mqtt 配置
"""
def __init__(self):
""" 保活时间仅MQTT协议sdk默认填写120单位。可选30~1200范围 """
self._keep_alive_time: int = 120
""" 客户端qos0或1默认为1 """
self._qos: int = 1
""" 连接超时时间 """
self._timeout: float = 1.0
@property
def keep_alive_time(self):
""" 保活时间仅MQTT协议sdk默认填写120单位。可选30~1200范围 """
return self._keep_alive_time
@keep_alive_time.setter
def keep_alive_time(self, value):
self._keep_alive_time = value
@property
def qos(self):
""" 客户端qos0或1默认为1 """
return self._qos
@qos.setter
def qos(self, value):
self._qos = value
@property
def timeout(self):
""" 连接超时时间 """
return self._timeout
@timeout.setter
def timeout(self, value):
self._timeout = value

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,96 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class Command:
"""
设备命令
"""
def __init__(self):
self._service_id: str = ""
self._command_name: str = ""
self._device_id: str = ""
self._paras: dict = dict()
@property
def service_id(self):
"""
设备的服务ID在设备关联的产品模型中定义
"""
return self._service_id
@service_id.setter
def service_id(self, value):
self._service_id = value
@property
def command_name(self):
"""
设备命令名称在设备关联的产品模型中定义
"""
return self._command_name
@command_name.setter
def command_name(self, value):
self._command_name = value
@property
def device_id(self):
"""
命令对应的目标设备ID命令下发对应的最终目标设备没有携带则表示目标设备即topic中指定的设备
"""
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def paras(self):
"""
设备命令的执行参数具体字段在设备关联的产品模型中定义
"""
return self._paras
@paras.setter
def paras(self, value):
self._paras = value
def to_dict(self):
"""
将请求内容放到字典中
Returns:
dict: 字典形式的请求
"""
return {"service_id": self._service_id,
"command_name": self._command_name,
"object_device_id": self._device_id,
"paras": self._paras}
def convert_from_dict(self, json_dict: dict):
json_name = ["service_id", "command_name", "object_device_id", "paras"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "service_id":
self.service_id = json_dict.get(key)
elif key == "command_name":
self.command_name = json_dict.get(key)
elif key == "object_device_id":
self.device_id = json_dict.get(key)
elif key == "paras":
self.paras = json_dict.get(key)
else:
pass

Binary file not shown.

View File

@ -0,0 +1,101 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class CommandRsp:
"""
命令响应
"""
def __init__(self):
self._result_code: int = 0
self._response_name: str = ""
self._paras: dict = dict()
@property
def result_code(self):
"""
标识命令的执行结果0表示成功其他表示失败不带默认认为成功
"""
return self._result_code
@result_code.setter
def result_code(self, value):
self._result_code = value
@property
def response_name(self):
"""
命令的响应名称在设备关联的产品模型中定义
"""
return self._response_name
@response_name.setter
def response_name(self, value):
self._response_name = value
@property
def paras(self):
"""
命令的响应参数具体字段在设备关联的产品模型中定义
"""
return self._paras
@paras.setter
def paras(self, value):
self._paras = value
def to_dict(self):
"""
将响应内容放到字典中
Returns:
dict: 字典形式的响应
"""
return {"result_code": self._result_code,
"response_name": self._response_name,
"paras": self._paras}
def convert_from_dict(self, json_dict: dict):
json_name = ["result_code", "response_name", "paras"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "result_code":
self.result_code = json_dict.get(key)
elif key == "response_name":
self.response_name = json_dict.get(key)
elif key == "paras":
self.paras = json_dict.get(key)
else:
pass
@staticmethod
def success_code():
"""
返回成功的结果码
Returns:
int: 成功的结果码
"""
return 0
@staticmethod
def fail_code():
"""
返回失败的结果码
Returns:
int: 失败的结果码
"""
return -1

View File

@ -0,0 +1,41 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class DeviceBaseInfo:
def __init__(self):
self._fw_version: str = ""
self._sw_version: str = ""
@property
def fw_version(self):
"""
固件版本
"""
return self._fw_version
@fw_version.setter
def fw_version(self, value):
self._fw_version = value
@property
def sw_version(self):
"""
软件版本
"""
return self._sw_version
@sw_version.setter
def sw_version(self, value):
self._sw_version = value

View File

@ -0,0 +1,108 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class DeviceEvent:
"""
服务的事件
"""
def __init__(self):
self._service_id: str = ""
self._event_type: str = ""
self._event_time: str = ""
self._event_id: str = ""
self._paras: dict = dict()
@property
def service_id(self):
"""
事件所属的服务
"""
return self._service_id
@service_id.setter
def service_id(self, value):
self._service_id = value
@property
def event_type(self):
"""
事件类型
"""
return self._event_type
@event_type.setter
def event_type(self, value):
self._event_type = value
@property
def event_time(self):
"""
事件发生的时间
"""
return self._event_time
@event_time.setter
def event_time(self, value):
self._event_time = value
@property
def event_id(self):
"""
事件id通过该参数关联对应的事件请求
"""
return self._event_id
@event_id.setter
def event_id(self, value):
self._event_id = value
@property
def paras(self):
"""
事件具体的参数
"""
return self._paras
@paras.setter
def paras(self, value):
self._paras = value
def to_dict(self):
"""
将请求内容放到字典中
Returns:
dict: 字典形式的请求
"""
return {"service_id": self._service_id, "event_type": self._event_type, "event_time": self._event_time,
"event_id": self._event_id, "paras": self._paras}
def convert_from_dict(self, json_dict: dict):
json_name = ["service_id", "event_type", "event_time", "event_id", "paras"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "service_id":
self.service_id = json_dict.get(key)
elif key == "event_type":
self.event_type = json_dict.get(key)
elif key == "event_time":
self.event_time = json_dict.get(key)
elif key == "event_id":
self.event_id = json_dict.get(key)
elif key == "paras":
self.paras = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,78 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List
from iot_device_sdk_python.client.request.device_event import DeviceEvent
class DeviceEvents:
"""
设备事件
"""
def __init__(self):
self._device_id: str = ""
self._services: List[DeviceEvent] = []
@property
def device_id(self):
"""
事件对应的最终目标设备没有携带则表示目标设备即topic中指定的设备
"""
return self._device_id
@device_id.setter
def device_id(self, value: str):
self._device_id = value
@property
def services(self):
"""
事件服务列表
"""
return self._services
@services.setter
def services(self, value):
self._services = value
def to_dict(self):
"""
将请求内容放到字典中
Returns:
dict: 字典形式的请求
"""
service_list = list()
for service in self._services:
service_list.append(service.to_dict())
return {"object_device_id": self._device_id, "services": service_list}
def convert_from_dict(self, json_dict: dict):
json_name = ["object_device_id", "services"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "object_device_id":
self.device_id = json_dict.get(key)
elif key == "services":
device_event_dict_list = json_dict.get(key)
for device_event_dict in device_event_dict_list:
device_event = DeviceEvent()
device_event.convert_from_dict(device_event_dict)
self._services.append(device_event)
else:
pass

View File

@ -0,0 +1,94 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class DeviceMessage:
"""
设备消息
"""
def __init__(self):
self._object_device_id: str = ""
self._id: str = ""
self._name: str = ""
self._content: str = ""
@property
def device_id(self):
"""
消息对应的最终目标设备没有携带则表示目标设备即topic中指定的设备
"""
return self._object_device_id
@device_id.setter
def device_id(self, value):
self._object_device_id = value
@property
def id(self):
"""
消息id消息的唯一标识
"""
return self._id
@id.setter
def id(self, value):
self._id = value
@property
def name(self):
"""
消息名称
"""
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def content(self):
"""
消息内容
"""
return self._content
@content.setter
def content(self, value):
self._content = value
def to_dict(self):
"""
将请求内容放到字典中
Returns:
dict: 字典形式的请求
"""
return {"object_device_id": self._object_device_id, "id": self._id, "name": self._name,
"content": self._content}
def convert_from_dict(self, json_dict: dict):
json_name = ["object_device_id", "name", "id", "content"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "object_device_id":
self.device_id = json_dict.get(key)
elif key == "id":
self.id = json_dict.get(key)
elif key == "name":
self.name = json_dict.get(key)
elif key == "content":
self.content = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class PropertiesData:
"""
属性数据
"""
def __init__(self):
self._properties: dict = dict()
self._event_time: str = ""
@property
def properties(self):
"""
设备服务的属性列表具体字段在设备关联的产品模型里定义可以设置多个字段
"""
return self._properties
@properties.setter
def properties(self, value):
self._properties = value
@property
def event_time(self):
return self._event_time
@event_time.setter
def event_time(self, value):
self._event_time = value
def convert_from_dict(self, json_dict: dict):
json_name = ["properties", "event_time"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "properties":
self.properties = json_dict.get(key)
elif key == "event_time":
self.event_time = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,56 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class PropsGet:
"""
读属性操作
"""
def __init__(self):
self._device_id: str = ""
self._service_id: str = ""
@property
def device_id(self):
"""
命令对应的目标设备ID命令下发对应的最终目标设备没有携带则表示目标设备即topic中指定的设备
"""
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def service_id(self):
"""
设备的服务ID在设备关联的产品模型中定义
"""
return self._service_id
@service_id.setter
def service_id(self, value):
self._service_id = value
def convert_from_dict(self, json_dict: dict):
json_name = ["object_device_id", "service_id"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "object_device_id":
self.device_id = json_dict.get(key)
elif key == "service_id":
self.service_id = json_dict.get(key)
else:
pass

Binary file not shown.

View File

@ -0,0 +1,50 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List
from iot_device_sdk_python.client.request.service_property import ServiceProperty
class PropSet:
"""
写属性操作
"""
def __init__(self):
self._device_id: str = ""
self._services: List[ServiceProperty] = []
@property
def device_id(self):
"""
命令对应的目标设备ID命令下发对应的最终目标设备没有携带则表示目标设备即topic中指定的设备
"""
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def services(self):
"""
设备服务数据列表
"""
return self._services
@services.setter
def services(self, value):
self._services = value

Binary file not shown.

View File

@ -0,0 +1,70 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import logging
from json import JSONDecodeError
from iot_device_sdk_python.client.request.device_message import DeviceMessage
class RawDeviceMessage:
_logger = logging.getLogger(__name__)
__SYSTEM_MESSAGE_KEYS = {"name", "id", "content", "object_device_id"}
"""
设备消息
"""
def __init__(self, payload: bytes):
self._payload = payload
@property
def payload(self):
"""
message下发的原始数据
"""
return self._payload
@payload.setter
def payload(self, payload: bytes):
self._payload = payload
def to_utf8_string(self):
""""
尝试将原始消息以utf-8格式decode如无法decode则raise UnicodeDecodeError
"""
return self._payload.decode('utf-8')
def to_device_message(self):
try:
device_msg_dict = json.loads(self.to_utf8_string())
except (JSONDecodeError, UnicodeDecodeError):
self._logger.debug("device message is not in system format")
return None # can't convert the system format
if any(map(lambda a: a not in self.__SYSTEM_MESSAGE_KEYS, device_msg_dict.keys())):
self._logger.debug("device message is not in system format because contain unexpected keys")
return None
if any(map(lambda a: a is not None and not isinstance(a, str), device_msg_dict.values())):
self._logger.debug("device message is not in system format because some values are not str")
return None
device_msg = DeviceMessage()
device_msg.convert_from_dict(device_msg_dict)
return device_msg

View File

@ -0,0 +1,83 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class ServiceProperty:
"""
服务属性
"""
def __init__(self, service_id: str = "", properties: dict = None, event_time: str = None):
self._service_id: str = service_id
self._properties: dict = properties
self._event_time: str = event_time
@property
def service_id(self):
"""
设备的服务ID在设备关联的产品模型中定义
"""
return self._service_id
@service_id.setter
def service_id(self, value):
self._service_id = value
@property
def properties(self):
"""
属性值具体字段由设备模型定义
"""
return self._properties
@properties.setter
def properties(self, value):
self._properties = value
@property
def event_time(self):
"""
设备采集数据UTC时间格式为毫秒级别yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
20161219T114920Z或者2020-08-12T12:12:12.333Z
设备上报数据不带该参数或参数格式错误时则数据上报时间以平台时间为准
"""
return self._event_time
@event_time.setter
def event_time(self, value):
self._event_time = value
def to_dict(self):
"""
将请求内容放到字典中
Returns:
dict: 字典形式的请求
"""
return {"service_id": self._service_id, "properties": self._properties, "event_time": self._event_time}
def convert_from_dict(self, json_dict: dict):
json_name = ["service_id", "properties", "event_time"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "service_id":
self.service_id = json_dict.get(key)
elif key == "properties":
self.properties = json_dict.get(key)
elif key == "event_time":
self.event_time = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,94 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
from iot_device_sdk_python.client.request.properties_data import PropertiesData
class ShadowData:
"""
影子数据
"""
def __init__(self):
self._service_id: str = ""
self._desired: Optional[PropertiesData] = None
self._reported: Optional[PropertiesData] = None
self._version: Optional[int] = None
@property
def service_id(self):
"""
服务id
"""
return self._service_id
@service_id.setter
def service_id(self, value):
self._service_id = value
@property
def desired(self):
"""
设备影子desired区的属性列表
"""
return self._desired
@desired.setter
def desired(self, value):
self._desired = value
@property
def reported(self):
"""
设备影子reported区的属性列表
"""
return self._reported
@reported.setter
def reported(self, value):
self._reported = value
@property
def version(self):
"""
设备影子版本信息
"""
return self._version
@version.setter
def version(self, value):
self._version = value
def convert_from_dict(self, json_dict: dict):
json_name = ["service_id", "desired", "reported", "version"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "service_id":
self.service_id = json_dict.get(key)
elif key == "desired":
desired = PropertiesData()
desired.convert_from_dict(json_dict.get(key))
self.desired = desired
elif key == "reported":
reported = PropertiesData()
reported.convert_from_dict(json_dict.get(key))
self.reported = reported
elif key == "version":
self.version = json_dict.get(key)
else:
pass

Binary file not shown.

View File

@ -0,0 +1,137 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
import logging
import time
from iot_device_sdk_python.service.abstract_service import AbstractService
from iot_device_sdk_python.client.request.device_event import DeviceEvent
from iot_device_sdk_python.utils.iot_util import get_event_time
class DeviceLogService(AbstractService):
_logger = logging.getLogger(__name__)
_LOG_CONFIG = "log_config"
def __init__(self):
super().__init__()
self._log_switch = True # 默认为True
self._end_time = None
self._connect_lost_dict: Optional[dict] = None
self._connect_failed_dict: Optional[dict] = None
def on_event(self, device_event: DeviceEvent):
"""
设备日志服务的事件处理方法
Args:
device_event: 设备事件
"""
if device_event.event_type == self._LOG_CONFIG:
# 平台下发日志收集通知
paras: dict = device_event.paras
if "switch" in paras.keys():
str_switch = paras.get("switch")
else:
self._logger.warning("event.paras doesn't contain key: switch. paras: %s", str(paras))
return
if "end_time" in paras.keys():
end_time = paras.get("end_time")
self.end_time = end_time
else:
self._logger.debug("event.paras doesn't contain key: end_time, paras: %s", str(paras))
if str_switch == "on":
self.log_switch = True
elif str_switch == "off":
self.log_switch = False
def report_device_log(self, timestamp: str, log_type: str, content: str):
"""
设备上报日志内容
Args:
timestamp: 日志产生的时间戳精确到秒
log_type: 日志类型总共有如下几种
DEVICE_STATUS: 设备状态
DEVICE_PROPERTY: 设备属性
DEVICE_MESSAGE: 设备消息
DEVICE_COMMAND: 设备命令
content: 日志内容
"""
device_event = DeviceEvent()
device_event.service_id = self.service_id
device_event.event_type = "log_report"
device_event.event_time = get_event_time()
paras: dict = {"timestamp": timestamp,
"type": log_type,
"content": content}
device_event.paras = paras
self.get_iot_device().get_client().report_event(device_event)
def can_report_log(self):
"""
根据平台上设置的开关和结束时间来判断能否上报日志
Returns:
bool: True为能上报日志False为不具备上报的条件;
"""
end_time: str = self.end_time
if end_time is not None:
end_time = end_time.replace("T", "")
end_time = end_time.replace("Z", "")
current_time = time.strftime("%Y%m%d%H%M%S", time.localtime())
if self.log_switch and (end_time is None or current_time < end_time):
return True
return False
@property
def log_switch(self):
return self._log_switch
@log_switch.setter
def log_switch(self, value):
self._log_switch = value
@property
def end_time(self):
return self._end_time
@end_time.setter
def end_time(self, value):
self._end_time = value
@property
def connect_lost_dict(self):
return self._connect_lost_dict
@connect_lost_dict.setter
def connect_lost_dict(self, value):
self._connect_lost_dict = value
@property
def connect_failed_dict(self):
return self._connect_failed_dict
@connect_failed_dict.setter
def connect_failed_dict(self, value):
self._connect_failed_dict = value

View File

@ -0,0 +1,64 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
from iot_device_sdk_python.transport.connect_action_listener import ConnectActionListener
from iot_device_sdk_python.devicelog.device_log_service import DeviceLogService
from iot_device_sdk_python.utils.iot_util import get_gmt_timestamp
class DefaultConnActionLogListener(ConnectActionListener):
def __init__(self, device_log_service: DeviceLogService):
self._device_log_service = device_log_service
def on_success(self, token: int):
"""
首次建链成功
Args:
token: 返回token
"""
# 只有当connect_failed_dict不为空时report一次设备日志上报建链失败的原因
# 若建链成功这里不上报任何设备日志。因为会与DefaultConnLogListener的connect_complete()上报重复的日志,造成浪费
if self._device_log_service.connect_failed_dict is not None:
tmp = list(self._device_log_service.connect_failed_dict.keys())
timestamp = tmp[0]
self._device_log_service.report_device_log(timestamp, "DEVICE_STATUS",
self._device_log_service.connect_failed_dict.get(timestamp))
def on_failure(self, token: int, err: Optional[Exception]):
"""
首次建链失败
Args:
token: 返回token
err: 失败异常
"""
failed_dict = dict()
failed_dict[str(get_gmt_timestamp())] = "connect failed, the reason is " + str(err)
self._device_log_service.connect_failed_dict = failed_dict

View File

@ -0,0 +1,67 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from iot_device_sdk_python.transport.connect_listener import ConnectListener
from iot_device_sdk_python.devicelog.device_log_service import DeviceLogService
from iot_device_sdk_python.utils.iot_util import get_gmt_timestamp
class DefaultConnLogListener(ConnectListener):
def __init__(self, device_log_service: DeviceLogService):
self._device_log_service = device_log_service
def connection_lost(self, cause: str):
"""
连接丢失通知
Args:
cause: 连接丢失原因
"""
lost_dict = dict()
str_current_time_millis = str(get_gmt_timestamp())
lost_dict[str_current_time_millis] = "connect lost"
self._device_log_service.connect_lost_dict = lost_dict
def connect_complete(self, reconnect: bool, server_uri: str):
"""
连接成功通知如果是断链重连的情景重连成功会上报断链的时间戳
Args:
reconnect: 是否为重连当前此参数没有作用
server_uri: 服务端地址
"""
self._device_log_service.report_device_log(str(get_gmt_timestamp()), "DEVICE_STATUS",
"connect complete, the uri is " + str(server_uri))
if self._device_log_service.connect_lost_dict is not None:
key_list = list(self._device_log_service.connect_lost_dict.keys())
timestamp = key_list[0]
self._device_log_service.report_device_log(timestamp, "DEVICE_STATUS",
self._device_log_service.connect_lost_dict.get(timestamp))

Binary file not shown.

View File

@ -0,0 +1,43 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, annotations
from abc import ABCMeta, abstractmethod
from iot_device_sdk_python.filemanager.url_info import UrlInfo
class FileManagerListener(metaclass=ABCMeta):
"""
监听文件上传下载事件
"""
@abstractmethod
def on_upload_url(self, url_info: UrlInfo):
"""
接收文件上传url进行文件上传操作
Args:
url_info: 上传参数
"""
@abstractmethod
def on_download_url(self, url_info: UrlInfo):
"""
接收文件下载url,进行文件下载操作
Args:
url_info: 下载参数
"""

View File

@ -0,0 +1,197 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import Optional
import logging
import os
from iot_device_sdk_python.client.request.device_event import DeviceEvent
from iot_device_sdk_python.service.abstract_service import AbstractService
from iot_device_sdk_python.filemanager.url_info import UrlInfo
from iot_device_sdk_python.filemanager.file_manager_listener import FileManagerListener
from iot_device_sdk_python.transport.action_listener import ActionListener
from iot_device_sdk_python.utils.iot_util import get_event_time
from iot_device_sdk_python.utils.iot_util import sha256_hash_from_file
class FileManagerService(AbstractService):
"""
文件管理器
"""
_logger = logging.getLogger(__name__)
def __init__(self):
super().__init__()
self._listener: Optional[FileManagerListener] = None
# file_name -> upload_file_path
self._upload_file_dict: dict = dict()
# file_name -> download_file_path
self._download_file_dict: dict = dict()
@property
def upload_file_dict(self):
return self._upload_file_dict
@property
def download_file_dict(self):
return self._download_file_dict
def get_listener(self):
return self._listener
def set_listener(self, listener: FileManagerListener):
"""
设置文件管理监听器
Args:
listener: 文件管理监听器
"""
self._listener = listener
def upload_file(self, file_name: str, file_path: str, file_attributes: Optional[dict] = None):
if file_name not in self._upload_file_dict.keys():
self._upload_file_dict[file_name] = file_path
self.get_upload_file_url(file_name, file_attributes)
else:
pass
def get_upload_file_url(self, file_name: str, file_attributes: Optional[dict] = None,
listener: Optional[ActionListener] = None):
"""
获取文件上传url
Args:
file_name: 文件名
file_attributes: 文件属性
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = self.service_id
device_event.event_type = "get_upload_url"
device_event.event_time = get_event_time()
file_attributes_dict = dict()
if file_attributes is None:
try:
file_sha256_hash: str = sha256_hash_from_file(self._upload_file_dict.get(file_name))
size = os.path.getsize(self._upload_file_dict.get(file_name))
except Exception as e:
self._logger.error("sha256 or getsize failed, Exception: %s", str(e))
raise e
file_attributes_dict = {"hash_code": file_sha256_hash, "size": size}
device_event.paras = {"file_name": file_name, "file_attributes": file_attributes_dict}
self.get_iot_device().get_client().report_event(device_event, listener)
def download_file(self, file_name: str, file_path: str, file_attributes: Optional[dict] = None):
if file_name not in self._download_file_dict.keys():
self._download_file_dict[file_name] = file_path
self.get_download_file_url(file_name, file_attributes)
else:
pass
def get_download_file_url(self, file_name: str, file_attributes: Optional[dict] = None,
listener: Optional[ActionListener] = None):
"""
获取文件下载url
Args:
file_name: 下载文件名
file_attributes: 文件属性
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = self.service_id
device_event.event_type = "get_download_url"
device_event.event_time = get_event_time()
if file_attributes is not None:
paras: dict = {"file_name": file_name, "file_attributes": file_attributes}
else:
paras: dict = {"file_name": file_name}
device_event.paras = paras
self.get_iot_device().get_client().report_event(device_event, listener)
def on_event(self, device_event: DeviceEvent):
"""
文件服务的事件处理方法
Args:
device_event: 事件
"""
if self._listener is None:
self._logger.warning("listener in FileManagerService is None, can not process")
return
if not isinstance(self._listener, FileManagerListener):
self._logger.warning("listener is not FileManagerListener, can not process")
return
if device_event.event_type == "get_upload_url_response":
paras: dict = device_event.paras
url_info = UrlInfo()
url_info.convert_from_dict(paras)
self._listener.on_upload_url(url_info)
elif device_event.event_type == "get_download_url_response":
paras: dict = device_event.paras
url_info = UrlInfo()
url_info.convert_from_dict(paras)
self._listener.on_download_url(url_info)
def report_upload_result(self, object_name: str, result_code: int, status_code: Optional[int] = None,
status_description: Optional[str] = None, listener: Optional[ActionListener] = None):
"""
设备上报文件上传结果
Args:
object_name: OBS上传对象名称
result_code: 设备上传文件状态0表示上传成功1表示上传失败
status_code: 文件上传到OBS返回的状态码
status_description: 文件上传到OBS时状态的描述
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = self.service_id
device_event.event_type = "upload_result_report"
device_event.event_time = get_event_time()
paras: dict = {"object_name": object_name,
"result_code": result_code}
if status_code is not None:
paras["status_code"] = status_code
if status_description is not None:
paras["status_description"] = status_description
device_event.paras = paras
self.get_iot_device().get_client().report_event(device_event, listener)
def report_download_result(self, object_name: str, result_code: int, status_code: Optional[int] = None,
status_description: Optional[str] = None, listener: Optional[ActionListener] = None):
"""
设备上报文件下载结果
Args:
object_name: OBS下载对象名称
result_code: 设备下载文件状态0表示上传成功1表示上传失败
status_code: 文件下载到OBS返回的状态码
status_description: 文件下载到OBS时状态的描述
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = self.service_id
device_event.event_type = "download_result_report"
device_event.event_time = get_event_time()
paras: dict = {"object_name": object_name,
"result_code": result_code}
if status_code is not None:
paras["status_code"] = status_code
if status_description is not None:
paras["status_description"] = status_description
device_event.paras = paras
self.get_iot_device().get_client().report_event(device_event, listener)

View File

@ -0,0 +1,101 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class UrlInfo:
def __init__(self):
self._url: str = ''
self._bucket_name: str = ''
self._object_name: str = ''
self._expire: int = 0
self._file_attributes: dict = dict()
@property
def url(self):
"""
文件上传/下载URL
"""
return self._url
@url.setter
def url(self, value):
self._url = value
@property
def bucket_name(self):
"""
OBS桶的名称
"""
return self._bucket_name
@bucket_name.setter
def bucket_name(self, value):
self._bucket_name = value
@property
def object_name(self):
"""
OBS待上传对象名称/OBS待下载对象名称
"""
return self._object_name
@object_name.setter
def object_name(self, value):
self._object_name = value
@property
def expire(self):
"""
URL过期时间单位
"""
return self._expire
@expire.setter
def expire(self, value):
self._expire = value
@property
def file_attributes(self):
"""
文件属性JSON格式的字典
"""
return self._file_attributes
@file_attributes.setter
def file_attributes(self, value):
self._file_attributes = value
def to_dict(self):
return {"url": self._url, "bucket_name": self._bucket_name, "object_name": self._object_name,
"expire": self._expire, "file_attributes": self._file_attributes}
def convert_from_dict(self, json_dict: dict):
json_name = ["url", "bucket_name", "object_name", "expire"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "url":
self.url = json_dict.get(key)
elif key == "bucket_name":
self.bucket_name = json_dict.get(key)
elif key == "object_name":
self.object_name = json_dict.get(key)
elif key == "expire":
self.expire = json_dict.get(key)
elif key == "file_attributes":
self.file_attributes = json_dict.get(key)
else:
pass

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,501 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List, Optional
import logging
import json
from iot_device_sdk_python.client.client_conf import ClientConf
from iot_device_sdk_python.client.request.props_get import PropsGet
from iot_device_sdk_python.client.request.props_set import PropSet
from iot_device_sdk_python.iot_device import IotDevice
from iot_device_sdk_python.client.request.device_event import DeviceEvent
from iot_device_sdk_python.gateway.sub_dev_discovery_listener import SubDevDiscoveryListener
from iot_device_sdk_python.gateway.gtw_operate_sub_device_listener import GtwOperateSubDeviceListener
from iot_device_sdk_python.gateway.sub_devices_persistence import SubDevicesPersistence
from iot_device_sdk_python.transport.action_listener import ActionListener
from iot_device_sdk_python.utils.iot_util import get_node_id_from_device_id, get_event_time
from iot_device_sdk_python.gateway.requests.device_info import DeviceInfo
from iot_device_sdk_python.client.request.device_message import DeviceMessage
from iot_device_sdk_python.client.request.service_property import ServiceProperty
from iot_device_sdk_python.gateway.requests.device_property import DeviceProperty
from iot_device_sdk_python.transport.raw_message import RawMessage
from iot_device_sdk_python.gateway.requests.device_status import DeviceStatus
from iot_device_sdk_python.gateway.requests.added_sub_device_info import AddedSubDeviceInfo
from iot_device_sdk_python.client.request.device_events import DeviceEvents
from iot_device_sdk_python.gateway.requests.sub_devices_info import SubDevicesInfo
from iot_device_sdk_python.gateway.requests.gtw_add_sub_device_rsp import GtwAddSubDeviceRsp
from iot_device_sdk_python.gateway.requests.gtw_del_sub_device_rsp import GtwDelSubDeviceRsp
from iot_device_sdk_python.gateway.requests.added_sub_device_info_rsp import AddedSubDeviceInfoRsp
from iot_device_sdk_python.gateway.requests.add_sub_device_failed_reason import AddSubDeviceFailedReason
from iot_device_sdk_python.gateway.requests.del_sub_device_failed_reason import DelSubDeviceFailedReason
from iot_device_sdk_python.client.request.command import Command
class AbstractGateway(IotDevice):
"""
抽象网关实现了子设备管理子设备消息转发功能
"""
_logger = logging.getLogger(__name__)
def __init__(self, sub_devices_persistence: SubDevicesPersistence, client_conf: ClientConf):
"""
初始化方法
Args:
sub_devices_persistence: 子设备信息持久化提供子设备信息保存能力
"""
super().__init__(client_conf)
self._sub_dev_discovery_listener: Optional[SubDevDiscoveryListener] = None # TODO 子设备发现监听器属于scan事件类型暂未使用
self._gtw_operate_sub_device_listener: Optional[GtwOperateSubDeviceListener] = None # 网关操作子设备监听器
self._sub_device_persistence = sub_devices_persistence # 子设备持久化,提供子设备信息保存能力
def set_sub_dev_discovery_listener(self, sub_dev_discovery_listener: SubDevDiscoveryListener):
"""
设置子设备发现监听器暂未使用
TODO 子设备发现监听器属于scan事件类型暂未使用
Args:
sub_dev_discovery_listener: 子设备发现监听器
"""
self._sub_dev_discovery_listener = sub_dev_discovery_listener
def set_gtw_operate_sub_device_listener(self, gtw_operate_sub_device_listener: GtwOperateSubDeviceListener):
"""
设置网关添加/删除子设备监听器
Args:
gtw_operate_sub_device_listener: 网关操作子设备监听器
"""
self._gtw_operate_sub_device_listener = gtw_operate_sub_device_listener
def get_sub_device_by_node_id(self, node_id: str) -> DeviceInfo:
"""
根据设备标识码查询子设备
Args:
node_id: 设备标识码
Returns:
DeviceInfo: 子设备信息
"""
return self._sub_device_persistence.get_sub_device(node_id)
def get_sub_device_by_device_id(self, device_id: str) -> DeviceInfo:
"""
根据设备id查询子设备
Args:
device_id: 设备id
Returns:
DeviceInfo: 子设备信息
"""
node_id: str = get_node_id_from_device_id(device_id)
return self._sub_device_persistence.get_sub_device(node_id)
def report_sub_dev_list(self, device_infos: List[DeviceInfo], listener: Optional[ActionListener] = None):
"""
上报子设备发现结果
TODO 属于scan事件类型暂未使用
Args:
device_infos: 子设备信息列表
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = "sub_device_discovery"
device_event.event_type = "scan_result"
device_event.event_time = get_event_time()
device_info_list = list()
for device_info in device_infos:
device_info_list.append(device_info.to_dict())
paras: dict = {"devices": device_info_list}
device_event.paras = paras
self.get_client().report_event(device_event, listener)
def report_sub_device_message(self, device_message: DeviceMessage, listener: Optional[ActionListener] = None):
"""
上报子设备消息
Args:
device_message: 设备消息
listener: 发布监听器
"""
self.get_client().report_device_message(device_message, listener)
def report_sub_device_properties(self, device_id: str, services: List[ServiceProperty],
listener: Optional[ActionListener] = None):
"""
上报子设备属性
Args:
device_id: 子设备id
services: 服务属性列表
listener: 发布监听器
"""
device_property = DeviceProperty()
device_property.device_id = device_id
device_property.services = services
self.report_batch_properties([device_property], listener)
def report_batch_properties(self, device_properties: List[DeviceProperty],
listener: Optional[ActionListener] = None):
"""
批量上报子设备属性
Args:
device_properties: 子设备属性列表
listener: 发布监听器
"""
device_property_list = list()
for device_property in device_properties:
device_property_list.append(device_property.to_dict())
devices: dict = {"devices": device_property_list}
topic = "$oc/devices/" + self.get_device_id() + "/sys/gateway/sub_devices/properties/report"
try:
payload = json.dumps(devices)
except Exception as e:
self._logger.error("json.dumps failed, Exception: %s", str(e))
raise e
raw_message = RawMessage(topic, payload)
self.get_client().publish_raw_message(raw_message, listener)
def report_sub_device_status(self, device_id: str, status: str, listener: Optional[ActionListener] = None):
"""
上报子设备状态
Args:
device_id: 子设备id
status: 设备状态OFFLINE:设备离线ONLINE:设备在线
listener: 发布监听器
"""
device_status = DeviceStatus()
device_status.device_id = device_id
device_status.status = status
self.report_batch_status([device_status], listener)
def report_batch_status(self, statuses: List[DeviceStatus], listener: Optional[ActionListener] = None):
"""
批量上报子设备状态
Args:
statuses: 子设备状态列表
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = "$sub_device_manager"
device_event.event_type = "sub_device_update_status"
device_event.event_time = get_event_time()
status_list = list()
for status in statuses:
status_list.append(status.to_dict())
device_event.paras = {"device_statuses": status_list}
self.get_client().report_event(device_event, listener)
def gtw_add_sub_device(self, added_sub_device_infos: List[AddedSubDeviceInfo], event_id: str,
listener: Optional[ActionListener] = None):
"""
网关发起新增子设备请求
Args:
added_sub_device_infos: 子设备信息列表
event_id: 此次请求的事件id不携带则由平台自动生成
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = "$sub_device_manager"
device_event.event_type = "add_sub_device_request"
device_event.event_time = get_event_time()
device_event.event_id = event_id
added_sub_device_info_list = list()
for added_sub_device_info in added_sub_device_infos:
added_sub_device_info_list.append(added_sub_device_info.to_dict())
paras: dict = {"devices": added_sub_device_info_list}
device_event.paras = paras
self.get_client().report_event(device_event, listener)
def gtw_del_sub_device(self, del_sub_devices: List[str], event_id: str, listener: Optional[ActionListener] = None):
"""
网关发起删除子设备请求
Args:
del_sub_devices: 要删除的子设备列表
event_id: 此次请求的事件id不携带则有平台自动生成
listener: 发布监听器
"""
device_event = DeviceEvent()
device_event.service_id = "$sub_device_manager"
device_event.event_type = "delete_sub_device_request"
device_event.event_time = get_event_time()
device_event.event_id = event_id
paras: dict = {"devices": del_sub_devices}
device_event.paras = paras
self.get_client().report_event(device_event, listener)
def on_event(self, device_events: DeviceEvents):
"""
事件处理回调由SDK自动调用
Args:
device_events: 设备事件
"""
# 子设备的事件
if device_events.device_id and device_events.device_id != self.get_device_id():
self.on_sub_dev_event(device_events)
return
# 网关的事件
super().on_event(device_events)
# 网关管理子设备的事件
for event in device_events.services:
event: DeviceEvent
if event.service_id != "$sub_device_manager":
continue
if event.event_type == "start_scan":
# TODO scan的事件类型暂未启用
pass
elif event.event_type == "add_sub_device_notify":
# 平台通知网关子设备新增
sub_devices_info = SubDevicesInfo()
version = event.paras.get("version")
sub_devices_info.version = version
tmp = list()
devices: list = event.paras.get("devices")
for device in devices:
device: dict
device_info = DeviceInfo()
device_info.convert_from_dict(device)
tmp.append(device_info)
sub_devices_info.devices = tmp
self.on_add_sub_devices(sub_devices_info)
elif event.event_type == "delete_sub_device_notify":
# 平台通知网关子设备删除
sub_devices_info = SubDevicesInfo()
version = event.paras.get("version")
sub_devices_info.version = version
tmp = list()
devices: list = event.paras.get("devices")
for device in devices:
device: dict
device_info = DeviceInfo()
device_info.convert_from_dict(device)
tmp.append(device_info)
sub_devices_info.devices = tmp
self.on_delete_sub_devices(sub_devices_info)
elif event.event_type == "add_sub_device_response":
# 网关新增子设备请求响应
gtw_add_sub_device_rsp = GtwAddSubDeviceRsp()
# successful_devices
success_tmp = list()
successful_devices: list = event.paras.get("successful_devices")
for device in successful_devices:
device: dict
added_sub_device_info_rsp = AddedSubDeviceInfoRsp()
added_sub_device_info_rsp.convert_from_dict(device)
success_tmp.append(added_sub_device_info_rsp)
gtw_add_sub_device_rsp.successful_devices = success_tmp
# failed_devices
fail_tmp = list()
failed_devices: list = event.paras.get("failed_devices")
for device in failed_devices:
device: dict
add_sub_device_failed_reason = AddSubDeviceFailedReason()
add_sub_device_failed_reason.convert_from_dict(device)
fail_tmp.append(add_sub_device_failed_reason)
gtw_add_sub_device_rsp.add_sub_device_failed_reasons = fail_tmp
if self._gtw_operate_sub_device_listener is not None:
self._gtw_operate_sub_device_listener.on_add_sub_device_rsp(gtw_add_sub_device_rsp,
event.event_id)
elif event.event_type == "delete_sub_device_response":
# 网关删除子设备请求响应
gtw_del_sub_device_rsp = GtwDelSubDeviceRsp()
# successful_devices
gtw_del_sub_device_rsp.successful_devices = event.paras.get("successful_devices")
# failed_devices
fail_tmp = list()
failed_devices: list = event.paras.get("failed_devices")
for device in failed_devices:
device: dict
del_sub_device_failed_reason = DelSubDeviceFailedReason()
del_sub_device_failed_reason.convert_from_dict(device)
fail_tmp.append(del_sub_device_failed_reason)
gtw_del_sub_device_rsp.failed_devices = fail_tmp
if self._gtw_operate_sub_device_listener is not None:
self._gtw_operate_sub_device_listener.on_del_sub_device_rsp(gtw_del_sub_device_rsp,
event.event_id)
else:
self._logger.info("gateway receive unknown event_type: %s", event.event_type)
def on_device_message(self, message: DeviceMessage):
"""
设备消息处理回调
Args:
message: 消息
"""
# 子设备的
if message.device_id and message.device_id != self.get_device_id():
self.on_sub_dev_message(message)
return
# 网关的
super().on_device_message(message)
def on_command(self, request_id: str, command: Command):
"""
命令处理回调
Args:
request_id: 请求id
command: 命令
"""
# 子设备的
if command.device_id and command.device_id != self.get_device_id():
self.on_sub_dev_command(request_id, command)
return
# 网关的
super().on_command(request_id, command)
def on_properties_set(self, request_id: str, props_set: PropSet):
"""
属性设置处理回调
Args:
request_id: 请求id
props_set: 属性设置请求
"""
# 子设备的
if props_set.device_id and props_set.device_id != self.get_device_id():
self.on_sub_dev_properties_set(request_id, props_set)
return
# 网关的
super().on_properties_set(request_id, props_set)
def on_properties_get(self, request_id: str, props_get: PropsGet):
"""
属性查询处理回调
Args:
request_id: 请求id
props_get: 属性查询请求
"""
# 子设备的
if props_get.device_id and props_get.device_id != self.get_device_id():
self.on_sub_dev_properties_get(request_id, props_get)
return
# 网关的
super().on_properties_get(request_id, props_get)
def on_add_sub_devices(self, sub_devices_info: SubDevicesInfo):
"""
添加子设备处理回调
Args:
sub_devices_info: 子设备信息
Returns:
int: 处理结果0表示成功
"""
if self._sub_device_persistence is not None:
return self._sub_device_persistence.add_sub_devices(sub_devices_info)
return -1
def on_delete_sub_devices(self, sub_devices_info: SubDevicesInfo):
"""
删除子设备处理回调
Args:
sub_devices_info: 子设备信息
Returns:
int: 处理结果0表示成功
"""
if self._sub_device_persistence is not None:
return self._sub_device_persistence.delete_sub_devices(sub_devices_info)
return -1
def sync_sub_devices(self):
"""
向平台请求同步子设备信息
"""
self._logger.debug("start to syncSubDevices, local version is %s",
str(self._sub_device_persistence.get_version()))
device_event = DeviceEvent()
device_event.service_id = "$sub_device_manager"
device_event.event_type = "sub_device_sync_request"
device_event.event_time = get_event_time()
paras: dict = {"version": self._sub_device_persistence.get_version()}
device_event.paras = paras
self.get_client().report_event(device_event)
def on_sub_dev_command(self, request_id: str, command: Command):
"""
子设备命令下发处理网关需要转发给子设备需要子类实现
Args:
request_id: 请求id
command: 命令
"""
def on_sub_dev_properties_set(self, request_id: str, props_set: PropSet):
"""
子设备属性设置网关需要转发给子设备需要子类实现
Args:
request_id: 请求id
props_set: 属性设置
"""
def on_sub_dev_properties_get(self, request_id: str, props_get: PropsGet):
"""
子设备属性查询网关需要转发给子设备需要子类实现
Args:
request_id: 请求id
props_get: 属性查询
"""
def on_sub_dev_message(self, message: DeviceMessage):
"""
子设备消息下发网关需要转发给子设备需要子类实现
Args:
message: 设备消息
"""
def on_sub_dev_event(self, device_events: DeviceEvents):
"""
子设备事件下发网关需要转发给子设备需要子类实现
Args:
device_events: 设备事件
"""

Binary file not shown.

View File

@ -0,0 +1,42 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from abc import ABCMeta, abstractmethod
from iot_device_sdk_python.gateway.requests.gtw_add_sub_device_rsp import GtwAddSubDeviceRsp
from iot_device_sdk_python.gateway.requests.gtw_del_sub_device_rsp import GtwDelSubDeviceRsp
class GtwOperateSubDeviceListener(metaclass=ABCMeta):
@abstractmethod
def on_add_sub_device_rsp(self, gtw_add_sub_device_rsp: GtwAddSubDeviceRsp, event_id: str):
"""
处理网关增加子设备返回结果
Args:
gtw_add_sub_device_rsp: 网关增加子设备响应
event_id: 事件id
"""
@abstractmethod
def on_del_sub_device_rsp(self, gtw_del_sub_device_rsp: GtwDelSubDeviceRsp, event_id: str):
"""
处理网关删除子设备返回结果
Args:
gtw_del_sub_device_rsp: 网关删除子设备响应
event_id: 事件id
"""

View File

@ -0,0 +1,89 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class AddSubDeviceFailedReason:
def __init__(self):
self._node_id: str = ""
self._product_id: str = ""
self._error_code: str = ""
self._error_msg: str = ""
@property
def node_id(self):
return self._node_id
@node_id.setter
def node_id(self, value):
self._node_id = value
@property
def product_id(self):
return self._product_id
@product_id.setter
def product_id(self, value):
self._product_id = value
@property
def error_code(self):
return self._error_code
@error_code.setter
def error_code(self, value):
self._error_code = value
@property
def error_msg(self):
return self._error_msg
@error_msg.setter
def error_msg(self, value):
self._error_msg = value
def to_dict(self):
return {"node_id": self._node_id,
"product_id": self._product_id,
"error_code": self._error_code,
"error_msg": self._error_msg}
def convert_from_dict(self, json_dict: dict):
json_name = ["node_id", "product_id", "error_code", "error_msg"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "node_id":
self.node_id = json_dict.get(key)
elif key == "product_id":
self.product_id = json_dict.get(key)
elif key == "error_code":
self.error_code = json_dict.get(key)
elif key == "error_msg":
self.error_msg = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,117 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
网关添加子设备信息
"""
class AddedSubDeviceInfo:
def __init__(self):
self._parent_device_id: str = ""
self._node_id: str = ""
self._device_id: str = ""
self._name: str = ""
self._description: str = ""
self._product_id: str = ""
self._extension_info: str = ""
@property
def parent_device_id(self):
return self._parent_device_id
@parent_device_id.setter
def parent_device_id(self, value):
self._parent_device_id = value
@property
def node_id(self):
return self._node_id
@node_id.setter
def node_id(self, value):
self._node_id = value
@property
def device_id(self):
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def description(self):
return self.description
@description.setter
def description(self, value):
self._description = value
@property
def product_id(self):
return self._product_id
@product_id.setter
def product_id(self, value):
self._product_id = value
@property
def extension_info(self):
return self._extension_info
@extension_info.setter
def extension_info(self, value):
self._extension_info = value
def to_dict(self):
return {"parent_device_id": self._parent_device_id,
"node_id": self._node_id,
"device_id": self._device_id,
"name": self._name,
"description": self._description,
"product_id": self._product_id,
"extension_info": self._extension_info}
def convert_from_dict(self, json_dict: dict):
json_name = ["parent_device_id", "node_id", "device_id", "name",
"description", "product_id", "extension_info"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "parent_device_id":
self.parent_device_id = json_dict.get(key)
elif key == "node_id":
self.node_id = json_dict.get(key)
elif key == "device_id":
self.device_id = json_dict.get(key)
elif key == "name":
self.name = json_dict.get(key)
elif key == "description":
self.description = json_dict.get(key)
elif key == "product_id":
self.product_id = json_dict.get(key)
elif key == "extension_info":
self.extension_info = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,144 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from iot_device_sdk_python.gateway.requests.device_info import DeviceInfo
class AddedSubDeviceInfoRsp:
def __init__(self):
self._device_info = DeviceInfo()
self._extension_info: str = ""
@property
def parent_device_id(self):
return self._device_info.parent_device_id
@parent_device_id.setter
def parent_device_id(self, value):
self._device_info.parent_device_id = value
@property
def node_id(self):
return self._device_info.node_id
@node_id.setter
def node_id(self, value):
self._device_info.node_id = value
@property
def device_id(self):
return self._device_info.device_id
@device_id.setter
def device_id(self, value):
self._device_info.device_id = value
@property
def name(self):
return self._device_info.name
@name.setter
def name(self, value):
self._device_info.name = value
@property
def description(self):
return self._device_info.description
@description.setter
def description(self, value):
self._device_info.description = value
@property
def manufacturer_id(self):
return self._device_info.manufacturer_id
@manufacturer_id.setter
def manufacturer_id(self, value):
self._device_info.manufacturer_id = value
@property
def model(self):
return self._device_info.model
@model.setter
def model(self, value):
self._device_info.model = value
@property
def product_id(self):
return self._device_info.product_id
@product_id.setter
def product_id(self, value):
self._device_info.product_id = value
@property
def fw_version(self):
return self._device_info.fw_version
@fw_version.setter
def fw_version(self, value):
self._device_info.fw_version = value
@property
def sw_version(self):
return self._device_info.sw_version
@sw_version.setter
def sw_version(self, value):
self._device_info.sw_version = value
@property
def status(self):
return self._device_info.status
@status.setter
def status(self, value):
self._device_info.status = value
@property
def extension_info(self):
return self._extension_info
@extension_info.setter
def extension_info(self, value):
self._extension_info = value
def to_dict(self):
ret = self._device_info.to_dict()
ret["extension_info"] = self._extension_info
return ret
def convert_from_dict(self, json_dict: dict):
self._device_info.convert_from_dict(json_dict)
for key in json_dict.keys():
if key == "extension_info":
self.extension_info = json_dict.get(key)

View File

@ -0,0 +1,76 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class DelSubDeviceFailedReason:
def __init__(self):
self._device_id: str = ""
self._error_code: str = ""
self._error_msg: str = ""
@property
def device_id(self):
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def error_code(self):
return self._error_code
@error_code.setter
def error_code(self, value):
self._error_code = value
@property
def error_msg(self):
return self._error_msg
@error_msg.setter
def error_msg(self, value):
self._error_msg = value
def to_dict(self):
return {"device_id": self._device_id,
"error_code": self._error_code,
"error_msg": self._error_msg}
def convert_from_dict(self, json_dict: dict):
json_name = ["device_id", "error_code", "error_msg"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "device_id":
self.device_id = json_dict.get(key)
elif key == "error_code":
self.error_code = json_dict.get(key)
elif key == "error_msg":
self.error_msg = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,163 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
设备信息
"""
class DeviceInfo:
def __init__(self):
self._parent_device_id: str = ""
self._node_id: str = ""
self._device_id: str = ""
self._name: str = ""
self._description: str = ""
self._manufacturer_id: str = ""
self._model: str = ""
self._product_id: str = ""
self._fw_version: str = ""
self._sw_version: str = ""
self._status: str = ""
@property
def parent_device_id(self):
return self._parent_device_id
@parent_device_id.setter
def parent_device_id(self, value):
self._parent_device_id = value
@property
def node_id(self):
return self._node_id
@node_id.setter
def node_id(self, value):
self._node_id = value
@property
def device_id(self):
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def description(self):
return self._description
@description.setter
def description(self, value):
self._description = value
@property
def manufacturer_id(self):
return self._manufacturer_id
@manufacturer_id.setter
def manufacturer_id(self, value):
self._manufacturer_id = value
@property
def model(self):
return self._model
@model.setter
def model(self, value):
self._model = value
@property
def product_id(self):
return self._product_id
@product_id.setter
def product_id(self, value):
self._product_id = value
@property
def fw_version(self):
return self._fw_version
@fw_version.setter
def fw_version(self, value):
self._fw_version = value
@property
def sw_version(self):
return self._sw_version
@sw_version.setter
def sw_version(self, value):
self._sw_version = value
@property
def status(self):
return self._status
@status.setter
def status(self, value):
self._status = value
def to_dict(self):
return {"parent_device_id": self._parent_device_id,
"node_id": self._node_id,
"device_id": self._device_id,
"name": self._name,
"description": self._description,
"manufacturer_id": self._manufacturer_id,
"model": self._model,
"product_id": self._product_id,
"fw_version": self._fw_version,
"sw_version": self._sw_version,
"status": self._status}
def convert_from_dict(self, json_dict: dict):
json_name = ["parent_device_id", "node_id", "device_id", "name", "description", "manufacturer_id",
"model", "product_id", "fw_version", "sw_version", "status"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "parent_device_id":
self.parent_device_id = json_dict.get(key)
elif key == "node_id":
self.node_id = json_dict.get(key)
elif key == "device_id":
self.device_id = json_dict.get(key)
elif key == "name":
self.name = json_dict.get(key)
elif key == "description":
self.description = json_dict.get(key)
elif key == "product_id":
self.product_id = json_dict.get(key)
elif key == "fw_version":
self.fw_version = json_dict.get(key)
elif key == "sw_version":
self.sw_version = json_dict.get(key)
elif key == "status":
self.status = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,52 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List
from iot_device_sdk_python.client.request.service_property import ServiceProperty
class DeviceProperty:
"""
设备属性
"""
def __init__(self):
self._device_id: str = ""
self._services: List[ServiceProperty] = []
@property
def device_id(self):
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def services(self):
return self._services
@services.setter
def services(self, value):
self._services = value
def to_dict(self):
service_list = list()
for service in self._services:
service_list.append(service.to_dict())
return {"device_id": self._device_id, "services": service_list}

View File

@ -0,0 +1,58 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class DeviceStatus:
def __init__(self):
self._device_id: str = ""
self._status: str = ""
@property
def device_id(self):
return self._device_id
@device_id.setter
def device_id(self, value):
self._device_id = value
@property
def status(self):
return self._status
@status.setter
def status(self, value):
self._status = value
def to_dict(self):
return {"device_id": self._device_id, "status": self._status}
def convert_from_dict(self, json_dict: dict):
json_name = ["device_id", "status"]
for key in json_dict.keys():
if key not in json_name:
continue
if key == "device_id":
self.device_id = json_dict.get(key)
elif key == "status":
self.status = json_dict.get(key)
else:
pass

View File

@ -0,0 +1,59 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List
from iot_device_sdk_python.gateway.requests.added_sub_device_info_rsp import AddedSubDeviceInfoRsp
from iot_device_sdk_python.gateway.requests.add_sub_device_failed_reason import AddSubDeviceFailedReason
class GtwAddSubDeviceRsp:
def __init__(self):
self._successful_devices: List[AddedSubDeviceInfoRsp] = []
self._add_sub_device_failed_reasons: List[AddSubDeviceFailedReason] = []
@property
def successful_devices(self):
return self._successful_devices
@successful_devices.setter
def successful_devices(self, value):
self._successful_devices = value
@property
def add_sub_device_failed_reasons(self):
return self._add_sub_device_failed_reasons
@add_sub_device_failed_reasons.setter
def add_sub_device_failed_reasons(self, value):
self._add_sub_device_failed_reasons = value
def to_dict(self):
return {"successful_devices": self._successful_devices,
"failed_devices": self._add_sub_device_failed_reasons}

View File

@ -0,0 +1,60 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2023-2024 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from typing import List
from iot_device_sdk_python.gateway.requests.del_sub_device_failed_reason import DelSubDeviceFailedReason
class GtwDelSubDeviceRsp:
def __init__(self):
self._successful_devices: List[str] = []
self._failed_devices: List[DelSubDeviceFailedReason] = []
@property
def successful_devices(self):
return self._successful_devices
@successful_devices.setter
def successful_devices(self, value):
self._successful_devices = value
@property
def failed_devices(self):
return self._failed_devices
@failed_devices.setter
def failed_devices(self, value):
self._failed_devices = value
def to_dict(self):
return {"successful_devices": self._successful_devices,
"failed_devices": self._failed_devices}

Some files were not shown because too many files have changed in this diff Show More