# -*- 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 from iot_device_sdk_python.client.client_conf import ClientConf from iot_device_sdk_python.client.device_client import DeviceClient from iot_device_sdk_python.client.mqtt_connect_conf import MqttConnectConf from iot_device_sdk_python.devicelog.listener.default_conn_action_log_listener import DefaultConnActionLogListener from iot_device_sdk_python.ota.ota_service import OTAService from iot_device_sdk_python.service.abstract_service import AbstractService 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.service_property import ServiceProperty from iot_device_sdk_python.client.iot_result import IotResult from iot_device_sdk_python.service.i_service import IService from iot_device_sdk_python.client.iot_result import SUCCESS from iot_device_sdk_python.client.request.device_events import DeviceEvents from iot_device_sdk_python.client.request.device_event import DeviceEvent from iot_device_sdk_python.client.request.device_message import DeviceMessage from iot_device_sdk_python.timesync.time_sync_service import TimeSyncService from iot_device_sdk_python.filemanager.file_manager_service import FileManagerService from iot_device_sdk_python.devicelog.device_log_service import DeviceLogService from iot_device_sdk_python.devicelog.listener.default_conn_log_listener import DefaultConnLogListener from iot_device_sdk_python.utils.iot_util import get_event_time from iot_device_sdk_python.rule.rule_manage_service import RuleManageService from iot_device_sdk_python.rule.model.action_handler import ActionHandler from iot_device_sdk_python.rule.model.actions import Action class AbstractDevice: """ 抽象设备类 """ _logger = logging.getLogger(__name__) def __init__(self, client_conf: ClientConf): connect_auth_info = client_conf.connect_auth_info mqtt_connect_conf = client_conf.mqtt_connect_conf if mqtt_connect_conf is None: mqtt_connect_conf = MqttConnectConf() self._client: DeviceClient = DeviceClient(connect_auth_info, mqtt_connect_conf, self) self._device_id = connect_auth_info.id self._services = dict() self._ota_service: Optional[OTAService] = None self._file_manager: Optional[FileManagerService] = None self._time_sync_service: Optional[TimeSyncService] = None self._device_log_service: Optional[DeviceLogService] = None self._rule_manage_service: Optional[RuleManageService] = None self._init_sys_services() def _init_sys_services(self): """ 初始化系统默认service,系统service以$作为开头。 当前系统默认的服务有:OTA,时间同步服务,文件管理服务(文件上传/下载),设备日志服务 """ self._ota_service = OTAService() self.add_service(service_id="$ota", device_service=self._ota_service) self._time_sync_service = TimeSyncService() self.add_service(service_id="$time_sync", device_service=self._time_sync_service) self._file_manager = FileManagerService() self.add_service(service_id="$file_manager", device_service=self._file_manager) self._device_log_service = DeviceLogService() self.add_service(service_id="$log", device_service=self._device_log_service) self._rule_manage_service = RuleManageService(self._client) self.add_service(service_id="$device_rule", device_service=self._rule_manage_service) def connect(self): """ 初始化,创建到平台的连接 Returns: int: 如果连接成功,返回0;否则返回-1 """ # 如果日志上报开关是关闭状态或者已过日志收集结束时间,则取消上报设备连接状态相关日志 # TODO 需要优化 if self._device_log_service and self._device_log_service.can_report_log(): default_conn_log_listener = DefaultConnLogListener(self._device_log_service) self._client.add_connect_listener(default_conn_log_listener) default_conn_action_log_listener = DefaultConnActionLogListener(self._device_log_service) self._client.set_connect_action_listener(default_conn_action_log_listener) return self._client.connect() def get_client(self) -> DeviceClient: """ 获取设备客户端。获取到设备客户端后,可以直接调用客户端提供的消息、属性、命令等接口。 Returns: DeviceClient: 设备客户端实例 """ return self._client def add_service(self, service_id: str, device_service: AbstractService): """ 添加服务。用户基于AbstractService定义自己的设备服务,并添加到设备。 Args: service_id: 服务id,要和设备模型定义一致 device_service: 服务实例 """ device_service.service_id = service_id device_service.set_iot_device(self) self._services[service_id] = device_service def get_service(self, service_id: str): """ 通过服务id获取服务实例 Args: service_id: 服务id Returns: AbstractService: 服务实例。若服务不存在,则返回None """ if service_id in self._services.keys(): return self._services.get(service_id) else: self._logger.debug("device have no service named: %s", service_id) return None def fire_properties_changed(self, service_id: str, properties: list): """ 触发属性变化,SDK会上报变化的属性 Args: service_id: 服务id properties: 属性列表 """ device_service: AbstractService = self.get_service(service_id) if device_service is None: self._logger.warning("device_service is None: %s", service_id) return props: dict = device_service.on_read(properties) service_property: ServiceProperty = ServiceProperty() service_property.service_id = device_service.service_id service_property.properties = props services = [service_property] self._client.report_properties(services) def fire_services_changed(self, service_ids: List[str]): """ 触发多个服务的属性变化,SDK自动上报变化的属性到平台 Args: service_ids: 发生变化的服务id列表 """ services = list() for service_id in service_ids: device_service = self.get_service(service_id) if device_service is None: self._logger.warning("device_service is None: %s", service_id) continue props: dict = device_service.on_read([]) service_property = ServiceProperty() service_property.service_id = device_service.service_id service_property.properties = props service_property.event_time = get_event_time() services.append(service_property) if len(services) == 0: return self.get_client().report_properties(services) def get_device_id(self): """ 获取设备id Returns: str: 设备id """ return self._device_id def on_command(self, request_id: str, command: Command): """ 命令回调函数,由SDK自动调用 Args: request_id: 请求id command: 命令 """ service: AbstractService = self.get_service(command.service_id) if service is not None: rsp: CommandRsp = service.on_command(command) self._client.respond_command(request_id, rsp) else: self._logger.warning("service is None: %s", command.service_id) def on_properties_set(self, request_id: str, props_set: PropSet): """ 属性设置回调,由SDK自动调用 Args: request_id: 请求id props_set: 属性设置请求 """ for service_property in props_set.services: service_property: ServiceProperty device_service: IService = self.get_service(service_property.service_id) if device_service is not None: # 如果部分失败直接返回 result: IotResult = device_service.on_write(service_property.properties) if result.result_code != SUCCESS.result_code: self._client.respond_properties_set(request_id, result) return self._client.respond_properties_set(request_id, SUCCESS) def on_properties_get(self, request_id: str, props_get: PropsGet): """ 属性查询回调,由SDK自动调用 Args: request_id: 请求id props_get: 属性查询请求 """ services: List[ServiceProperty] = list() if props_get.service_id == "": # 查询所有 for ss in iter(self._services): device_service: IService = self.get_service(ss) if device_service is not None: props = device_service.on_read([]) service_property: ServiceProperty = ServiceProperty() service_property.service_id = ss service_property.properties = props services.append(service_property) else: device_service: IService = self.get_service(props_get.service_id) if device_service is not None: props = device_service.on_read([]) service_property: ServiceProperty = ServiceProperty() service_property.service_id = props_get.service_id service_property.properties = props services.append(service_property) self._client.respond_properties_get(request_id, services) 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._logger.debug("receive event for sub devices: %s", device_events.device_id) return for event in device_events.services: event: DeviceEvent if event.service_id == "$sub_device_manager": # 网关相关的服务,在AbstractGateway的on_event()方法中处理 continue device_service: IService = self.get_service(event.service_id) if device_service is not None: # 对于系统定义的事件,sdk都有对应的服务进行处理 device_service.on_event(event) else: self._logger.warning("device_service is None: %s", event.service_id) def on_rule_action_handler(self, action_list: List[Action]): if action_list: for action in action_list: if self.get_device_id() != action.device_id: self._logger.warning("action device is not match: target: %s, action: %s", self.get_device_id(), action.device_id) continue self.get_rule_manage_service().handle_action(action) return self._logger.warning("rule action list is empty.") def on_device_message(self, message: DeviceMessage): """ 消息回调,由SDK自动调用 Args: message: 消息 """ # 不实现 def get_ota_service(self) -> OTAService: """ 获取OTA服务 Returns: OTAService: OTA服务 """ return self._ota_service def get_time_sync_service(self) -> TimeSyncService: """ 获取时间同步服务 Returns: TimeSyncService: 时间同步服务 """ return self._time_sync_service def get_device_log_service(self) -> DeviceLogService: """ 获取设备日志服务 Returns: DeviceLogService: 设备日志服务 """ return self._device_log_service def get_file_manager_service(self) -> FileManagerService: """ 获取文件管理服务 Returns: FileManagerService: 文件管理服务 """ return self._file_manager def get_rule_manage_service(self) -> RuleManageService: return self._rule_manage_service def destroy(self): """ 释放连接 """ self._client.close()