pymammotion 0.4.0a2__py3-none-any.whl → 0.5.51__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymammotion might be problematic. Click here for more details.
- pymammotion/__init__.py +5 -4
- pymammotion/aliyun/client.py +235 -0
- pymammotion/aliyun/cloud_gateway.py +312 -64
- pymammotion/aliyun/model/aep_response.py +1 -2
- pymammotion/aliyun/model/dev_by_account_response.py +170 -23
- pymammotion/aliyun/model/login_by_oauth_response.py +2 -3
- pymammotion/aliyun/model/regions_response.py +3 -3
- pymammotion/aliyun/model/session_by_authcode_response.py +2 -2
- pymammotion/aliyun/model/thing_response.py +12 -0
- pymammotion/aliyun/regions.py +62 -0
- pymammotion/aliyun/tea/core.py +297 -0
- pymammotion/bluetooth/ble.py +7 -9
- pymammotion/bluetooth/ble_message.py +10 -14
- pymammotion/const.py +3 -0
- pymammotion/data/model/__init__.py +1 -2
- pymammotion/data/model/device.py +95 -27
- pymammotion/data/model/device_config.py +4 -4
- pymammotion/data/model/device_info.py +35 -0
- pymammotion/data/model/device_limits.py +10 -10
- pymammotion/data/model/enums.py +12 -2
- pymammotion/data/model/errors.py +12 -0
- pymammotion/data/model/events.py +14 -0
- pymammotion/data/model/generate_geojson.py +521 -0
- pymammotion/data/model/generate_route_information.py +2 -2
- pymammotion/data/model/hash_list.py +370 -57
- pymammotion/data/model/location.py +4 -4
- pymammotion/data/model/mowing_modes.py +17 -1
- pymammotion/data/model/raw_data.py +2 -10
- pymammotion/data/model/region_data.py +10 -11
- pymammotion/data/model/report_info.py +31 -5
- pymammotion/data/model/work.py +27 -0
- pymammotion/data/mower_state_manager.py +316 -0
- pymammotion/data/mqtt/event.py +73 -28
- pymammotion/data/mqtt/mammotion_properties.py +257 -0
- pymammotion/data/mqtt/properties.py +93 -78
- pymammotion/data/mqtt/status.py +18 -17
- pymammotion/event/event.py +27 -6
- pymammotion/homeassistant/__init__.py +3 -0
- pymammotion/homeassistant/mower_api.py +484 -0
- pymammotion/homeassistant/rtk_api.py +54 -0
- pymammotion/http/encryption.py +5 -6
- pymammotion/http/http.py +574 -28
- pymammotion/http/model/__init__.py +0 -0
- pymammotion/{aliyun/model/stream_subscription_response.py → http/model/camera_stream.py} +14 -2
- pymammotion/http/model/http.py +129 -4
- pymammotion/http/model/response_factory.py +61 -0
- pymammotion/http/model/rtk.py +16 -0
- pymammotion/mammotion/commands/abstract_message.py +7 -5
- pymammotion/mammotion/commands/mammotion_command.py +30 -1
- pymammotion/mammotion/commands/messages/basestation.py +43 -0
- pymammotion/mammotion/commands/messages/driver.py +61 -29
- pymammotion/mammotion/commands/messages/media.py +68 -15
- pymammotion/mammotion/commands/messages/navigation.py +61 -25
- pymammotion/mammotion/commands/messages/network.py +17 -23
- pymammotion/mammotion/commands/messages/ota.py +18 -18
- pymammotion/mammotion/commands/messages/system.py +32 -49
- pymammotion/mammotion/commands/messages/video.py +15 -16
- pymammotion/mammotion/devices/__init__.py +27 -3
- pymammotion/mammotion/devices/base.py +40 -131
- pymammotion/mammotion/devices/mammotion.py +436 -201
- pymammotion/mammotion/devices/mammotion_bluetooth.py +57 -47
- pymammotion/mammotion/devices/mammotion_cloud.py +134 -105
- pymammotion/mammotion/devices/mammotion_mower_ble.py +49 -0
- pymammotion/mammotion/devices/mammotion_mower_cloud.py +39 -0
- pymammotion/mammotion/devices/managers/managers.py +81 -0
- pymammotion/mammotion/devices/mower_device.py +124 -0
- pymammotion/mammotion/devices/mower_manager.py +107 -0
- pymammotion/mammotion/devices/rtk_ble.py +89 -0
- pymammotion/mammotion/devices/rtk_cloud.py +113 -0
- pymammotion/mammotion/devices/rtk_device.py +50 -0
- pymammotion/mammotion/devices/rtk_manager.py +122 -0
- pymammotion/mqtt/__init__.py +2 -1
- pymammotion/mqtt/aliyun_mqtt.py +232 -0
- pymammotion/mqtt/linkkit/__init__.py +5 -0
- pymammotion/mqtt/linkkit/h2client.py +585 -0
- pymammotion/mqtt/linkkit/linkkit.py +3023 -0
- pymammotion/mqtt/mammotion_mqtt.py +176 -169
- pymammotion/mqtt/mqtt_models.py +66 -0
- pymammotion/proto/__init__.py +4839 -4
- pymammotion/proto/basestation.proto +8 -0
- pymammotion/proto/basestation_pb2.py +11 -9
- pymammotion/proto/basestation_pb2.pyi +16 -2
- pymammotion/proto/dev_net.proto +79 -55
- pymammotion/proto/dev_net_pb2.py +60 -56
- pymammotion/proto/dev_net_pb2.pyi +49 -6
- pymammotion/proto/luba_msg.proto +2 -1
- pymammotion/proto/luba_msg_pb2.py +6 -6
- pymammotion/proto/luba_msg_pb2.pyi +1 -0
- pymammotion/proto/luba_mul.proto +62 -1
- pymammotion/proto/luba_mul_pb2.py +38 -22
- pymammotion/proto/luba_mul_pb2.pyi +94 -7
- pymammotion/proto/mctrl_driver.proto +44 -4
- pymammotion/proto/mctrl_driver_pb2.py +26 -14
- pymammotion/proto/mctrl_driver_pb2.pyi +66 -11
- pymammotion/proto/mctrl_nav.proto +93 -52
- pymammotion/proto/mctrl_nav_pb2.py +75 -67
- pymammotion/proto/mctrl_nav_pb2.pyi +142 -56
- pymammotion/proto/mctrl_ota.proto +40 -2
- pymammotion/proto/mctrl_ota_pb2.py +23 -13
- pymammotion/proto/mctrl_ota_pb2.pyi +67 -4
- pymammotion/proto/mctrl_pept.proto +8 -3
- pymammotion/proto/mctrl_pept_pb2.py +8 -6
- pymammotion/proto/mctrl_pept_pb2.pyi +14 -6
- pymammotion/proto/mctrl_sys.proto +325 -86
- pymammotion/proto/mctrl_sys_pb2.py +162 -98
- pymammotion/proto/mctrl_sys_pb2.pyi +451 -25
- pymammotion/proto/message_pool.py +3 -0
- pymammotion/proto/py.typed +0 -0
- pymammotion/utility/constant/device_constant.py +29 -5
- pymammotion/utility/datatype_converter.py +13 -12
- pymammotion/utility/device_config.py +522 -130
- pymammotion/utility/device_type.py +218 -21
- pymammotion/utility/map.py +238 -51
- pymammotion/utility/mur_mur_hash.py +159 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/METADATA +26 -31
- pymammotion-0.5.51.dist-info/RECORD +152 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info}/WHEEL +1 -1
- pymammotion/aliyun/cloud_service.py +0 -65
- pymammotion/data/model/plan.py +0 -58
- pymammotion/data/state_manager.py +0 -129
- pymammotion/proto/basestation.py +0 -59
- pymammotion/proto/common.py +0 -12
- pymammotion/proto/dev_net.py +0 -381
- pymammotion/proto/luba_msg.py +0 -81
- pymammotion/proto/luba_mul.py +0 -76
- pymammotion/proto/mctrl_driver.py +0 -100
- pymammotion/proto/mctrl_nav.py +0 -664
- pymammotion/proto/mctrl_ota.py +0 -48
- pymammotion/proto/mctrl_pept.py +0 -41
- pymammotion/proto/mctrl_sys.py +0 -574
- pymammotion-0.4.0a2.dist-info/RECORD +0 -131
- /pymammotion/http/{_init_.py → __init__.py} +0 -0
- {pymammotion-0.4.0a2.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""RTK Device Manager - manages RTK devices with cloud and BLE connectivity."""
|
|
2
|
+
|
|
3
|
+
from typing import override
|
|
4
|
+
|
|
5
|
+
from bleak import BLEDevice
|
|
6
|
+
|
|
7
|
+
from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
|
|
8
|
+
from pymammotion.aliyun.model.dev_by_account_response import Device
|
|
9
|
+
from pymammotion.data.model.device import RTKDevice
|
|
10
|
+
from pymammotion.data.model.enums import ConnectionPreference
|
|
11
|
+
from pymammotion.mammotion.devices.mammotion_cloud import MammotionCloud
|
|
12
|
+
from pymammotion.mammotion.devices.managers.managers import AbstractDeviceManager
|
|
13
|
+
from pymammotion.mammotion.devices.rtk_ble import MammotionRTKBLEDevice
|
|
14
|
+
from pymammotion.mammotion.devices.rtk_cloud import MammotionRTKCloudDevice
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MammotionRTKDeviceManager(AbstractDeviceManager):
|
|
18
|
+
"""Manages an RTK device with both cloud and BLE connectivity options."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
name: str,
|
|
23
|
+
iot_id: str,
|
|
24
|
+
cloud_client: CloudIOTGateway,
|
|
25
|
+
cloud_device: Device,
|
|
26
|
+
ble_device: BLEDevice | None = None,
|
|
27
|
+
mqtt: MammotionCloud | None = None,
|
|
28
|
+
preference: ConnectionPreference = ConnectionPreference.WIFI,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Initialize RTK device manager."""
|
|
31
|
+
super().__init__(name, iot_id, cloud_client, cloud_device, preference)
|
|
32
|
+
self._ble_device: MammotionRTKBLEDevice | None = None
|
|
33
|
+
self._cloud_device: MammotionRTKCloudDevice | None = None
|
|
34
|
+
self.name = name
|
|
35
|
+
self.iot_id = iot_id
|
|
36
|
+
self.cloud_client = cloud_client
|
|
37
|
+
self._device: Device = cloud_device
|
|
38
|
+
self.mammotion_http = cloud_client.mammotion_http
|
|
39
|
+
self.preference = preference
|
|
40
|
+
|
|
41
|
+
# Initialize RTK state
|
|
42
|
+
self._rtk_state = RTKDevice(
|
|
43
|
+
name=name,
|
|
44
|
+
iot_id=iot_id,
|
|
45
|
+
product_key=cloud_device.product_key,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Add connection types if provided
|
|
49
|
+
if ble_device:
|
|
50
|
+
self.add_ble(ble_device)
|
|
51
|
+
if mqtt:
|
|
52
|
+
self.add_cloud(mqtt)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def state(self) -> RTKDevice:
|
|
56
|
+
"""Return the RTK device state."""
|
|
57
|
+
return self._rtk_state
|
|
58
|
+
|
|
59
|
+
@state.setter
|
|
60
|
+
def state(self, value: RTKDevice) -> None:
|
|
61
|
+
"""Set the RTK device state."""
|
|
62
|
+
self._rtk_state = value
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def ble(self) -> MammotionRTKBLEDevice | None:
|
|
66
|
+
"""Return BLE device interface."""
|
|
67
|
+
return self._ble_device
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def cloud(self) -> MammotionRTKCloudDevice | None:
|
|
71
|
+
"""Return cloud device interface."""
|
|
72
|
+
return self._cloud_device
|
|
73
|
+
|
|
74
|
+
def has_queued_commands(self) -> bool:
|
|
75
|
+
"""Check if there are queued commands."""
|
|
76
|
+
if self.cloud and self.preference == ConnectionPreference.WIFI:
|
|
77
|
+
return not self.cloud.mqtt.command_queue.empty()
|
|
78
|
+
elif self.ble:
|
|
79
|
+
return not self.ble.command_queue.empty()
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
def add_ble(self, ble_device: BLEDevice) -> MammotionRTKBLEDevice:
|
|
83
|
+
"""Add BLE device."""
|
|
84
|
+
self._ble_device = MammotionRTKBLEDevice(
|
|
85
|
+
cloud_device=self._device, rtk_state=self._rtk_state, device=ble_device
|
|
86
|
+
)
|
|
87
|
+
return self._ble_device
|
|
88
|
+
|
|
89
|
+
@override
|
|
90
|
+
def add_cloud(self, mqtt: MammotionCloud) -> MammotionRTKCloudDevice:
|
|
91
|
+
"""Add cloud device."""
|
|
92
|
+
self._cloud_device = MammotionRTKCloudDevice(mqtt, cloud_device=self._device, rtk_state=self._rtk_state)
|
|
93
|
+
return self._cloud_device
|
|
94
|
+
|
|
95
|
+
def replace_cloud(self, cloud_device: MammotionRTKCloudDevice) -> None:
|
|
96
|
+
"""Replace cloud device."""
|
|
97
|
+
self._cloud_device = cloud_device
|
|
98
|
+
|
|
99
|
+
def remove_cloud(self) -> None:
|
|
100
|
+
"""Remove cloud device."""
|
|
101
|
+
self._cloud_device = None
|
|
102
|
+
|
|
103
|
+
def replace_ble(self, ble_device: MammotionRTKBLEDevice) -> None:
|
|
104
|
+
"""Replace BLE device."""
|
|
105
|
+
self._ble_device = ble_device
|
|
106
|
+
|
|
107
|
+
def remove_ble(self) -> None:
|
|
108
|
+
"""Remove BLE device."""
|
|
109
|
+
self._ble_device = None
|
|
110
|
+
|
|
111
|
+
def replace_mqtt(self, mqtt: MammotionCloud) -> None:
|
|
112
|
+
"""Replace MQTT connection."""
|
|
113
|
+
device = self._cloud_device.device
|
|
114
|
+
self._cloud_device = MammotionRTKCloudDevice(mqtt, cloud_device=device, rtk_state=self._rtk_state)
|
|
115
|
+
|
|
116
|
+
def has_cloud(self) -> bool:
|
|
117
|
+
"""Check if cloud connection is available."""
|
|
118
|
+
return self._cloud_device is not None
|
|
119
|
+
|
|
120
|
+
def has_ble(self) -> bool:
|
|
121
|
+
"""Check if BLE connection is available."""
|
|
122
|
+
return self._ble_device is not None
|
pymammotion/mqtt/__init__.py
CHANGED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""MammotionMQTT."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import base64
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
import hashlib
|
|
7
|
+
import hmac
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from logging import getLogger
|
|
11
|
+
|
|
12
|
+
import betterproto2
|
|
13
|
+
from paho.mqtt.client import MQTTMessage
|
|
14
|
+
|
|
15
|
+
from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
|
|
16
|
+
from pymammotion.data.mqtt.event import ThingEventMessage
|
|
17
|
+
from pymammotion.data.mqtt.properties import ThingPropertiesMessage
|
|
18
|
+
from pymammotion.data.mqtt.status import ThingStatusMessage
|
|
19
|
+
from pymammotion.mqtt.linkkit.linkkit import LinkKit
|
|
20
|
+
from pymammotion.proto import LubaMsg
|
|
21
|
+
|
|
22
|
+
logger = getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AliyunMQTT:
|
|
26
|
+
"""MQTT client for pymammotion."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
region_id: str,
|
|
31
|
+
product_key: str,
|
|
32
|
+
device_name: str,
|
|
33
|
+
device_secret: str,
|
|
34
|
+
iot_token: str,
|
|
35
|
+
cloud_client: CloudIOTGateway,
|
|
36
|
+
client_id: str | None = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Create instance of MammotionMQTT."""
|
|
39
|
+
super().__init__()
|
|
40
|
+
self._cloud_client = cloud_client
|
|
41
|
+
self.is_connected = False
|
|
42
|
+
self.is_ready = False
|
|
43
|
+
self.on_connected: Callable[[], Awaitable[None]] | None = None
|
|
44
|
+
self.on_ready: Callable[[], Awaitable[None]] | None = None
|
|
45
|
+
self.on_error: Callable[[str], Awaitable[None]] | None = None
|
|
46
|
+
self.on_disconnected: Callable[[], Awaitable[None]] | None = None
|
|
47
|
+
self.on_message: Callable[[str, str, str], Awaitable[None]] | None = None
|
|
48
|
+
|
|
49
|
+
self._product_key = product_key
|
|
50
|
+
self._device_name = device_name
|
|
51
|
+
self._device_secret = device_secret
|
|
52
|
+
self._iot_token = iot_token
|
|
53
|
+
self._mqtt_username = f"{device_name}&{product_key}"
|
|
54
|
+
# linkkit provides the correct MQTT service for all of this and uses paho under the hood
|
|
55
|
+
if client_id is None:
|
|
56
|
+
client_id = f"python-{device_name}"
|
|
57
|
+
self._mqtt_client_id = f"{client_id}|securemode=2,signmethod=hmacsha1|"
|
|
58
|
+
sign_content = f"clientId{client_id}deviceName{device_name}productKey{product_key}"
|
|
59
|
+
self._mqtt_password = hmac.new(
|
|
60
|
+
device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha1
|
|
61
|
+
).hexdigest()
|
|
62
|
+
|
|
63
|
+
self._client_id = client_id
|
|
64
|
+
self.loop = asyncio.get_running_loop()
|
|
65
|
+
|
|
66
|
+
self._linkkit_client = LinkKit(
|
|
67
|
+
region_id,
|
|
68
|
+
product_key,
|
|
69
|
+
device_name,
|
|
70
|
+
device_secret,
|
|
71
|
+
auth_type="",
|
|
72
|
+
client_id=client_id,
|
|
73
|
+
password=self._mqtt_password,
|
|
74
|
+
username=self._mqtt_username,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
self._linkkit_client.enable_logger(level=logging.ERROR)
|
|
78
|
+
self._linkkit_client.on_connect = self._thing_on_connect
|
|
79
|
+
self._linkkit_client.on_disconnect = self._on_disconnect
|
|
80
|
+
self._linkkit_client.on_thing_enable = self._thing_on_thing_enable
|
|
81
|
+
self._linkkit_client.on_topic_message = self._thing_on_topic_message
|
|
82
|
+
self._mqtt_host = f"{self._product_key}.iot-as-mqtt.{region_id}.aliyuncs.com"
|
|
83
|
+
|
|
84
|
+
def connect_async(self) -> None:
|
|
85
|
+
"""Connect async to MQTT Server."""
|
|
86
|
+
logger.info("Connecting...")
|
|
87
|
+
if self._linkkit_client.check_state() is LinkKit.LinkKitState.INITIALIZED:
|
|
88
|
+
self._linkkit_client.thing_setup()
|
|
89
|
+
self._linkkit_client.connect_async()
|
|
90
|
+
|
|
91
|
+
def disconnect(self) -> None:
|
|
92
|
+
"""Disconnect from MQTT Server."""
|
|
93
|
+
logger.info("Disconnecting...")
|
|
94
|
+
|
|
95
|
+
self._linkkit_client.disconnect()
|
|
96
|
+
|
|
97
|
+
def _thing_on_thing_enable(self, user_data) -> None:
|
|
98
|
+
"""Is called when Thing is enabled."""
|
|
99
|
+
logger.debug("on_thing_enable")
|
|
100
|
+
self.is_connected = True
|
|
101
|
+
# logger.debug('subscribe_topic, topic:%s' % echo_topic)
|
|
102
|
+
# self._linkkit_client.subscribe_topic(echo_topic, 0)
|
|
103
|
+
self._linkkit_client.subscribe_topic(
|
|
104
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
|
|
105
|
+
)
|
|
106
|
+
self._linkkit_client.subscribe_topic(
|
|
107
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
|
|
108
|
+
)
|
|
109
|
+
self._linkkit_client.subscribe_topic(
|
|
110
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/status/notify"
|
|
111
|
+
)
|
|
112
|
+
self._linkkit_client.subscribe_topic(
|
|
113
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/connect/event/notify"
|
|
114
|
+
)
|
|
115
|
+
self._linkkit_client.subscribe_topic(
|
|
116
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/_thing/event/notify"
|
|
117
|
+
)
|
|
118
|
+
self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
|
|
119
|
+
self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
|
|
120
|
+
self._linkkit_client.subscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties")
|
|
121
|
+
self._linkkit_client.subscribe_topic(
|
|
122
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
self._linkkit_client.publish_topic(
|
|
126
|
+
f"/sys/{self._product_key}/{self._device_name}/app/up/account/bind",
|
|
127
|
+
json.dumps(
|
|
128
|
+
{
|
|
129
|
+
"id": "msgid1",
|
|
130
|
+
"version": "1.0",
|
|
131
|
+
"request": {"clientId": self._mqtt_username},
|
|
132
|
+
"params": {"iotToken": self._iot_token},
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if self.on_ready:
|
|
138
|
+
self.is_ready = True
|
|
139
|
+
future = asyncio.run_coroutine_threadsafe(self.on_ready(), self.loop)
|
|
140
|
+
asyncio.wrap_future(future, loop=self.loop)
|
|
141
|
+
|
|
142
|
+
def unsubscribe(self) -> None:
|
|
143
|
+
self._linkkit_client.unsubscribe_topic(
|
|
144
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/account/bind_reply"
|
|
145
|
+
)
|
|
146
|
+
self._linkkit_client.unsubscribe_topic(
|
|
147
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/event/property/post_reply"
|
|
148
|
+
)
|
|
149
|
+
self._linkkit_client.unsubscribe_topic(
|
|
150
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/status/notify"
|
|
151
|
+
)
|
|
152
|
+
self._linkkit_client.unsubscribe_topic(
|
|
153
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/wifi/connect/event/notify"
|
|
154
|
+
)
|
|
155
|
+
self._linkkit_client.unsubscribe_topic(
|
|
156
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/_thing/event/notify"
|
|
157
|
+
)
|
|
158
|
+
self._linkkit_client.unsubscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/events")
|
|
159
|
+
self._linkkit_client.unsubscribe_topic(f"/sys/{self._product_key}/{self._device_name}/app/down/thing/status")
|
|
160
|
+
self._linkkit_client.unsubscribe_topic(
|
|
161
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/properties"
|
|
162
|
+
)
|
|
163
|
+
self._linkkit_client.unsubscribe_topic(
|
|
164
|
+
f"/sys/{self._product_key}/{self._device_name}/app/down/thing/model/down_raw"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def _thing_on_topic_message(self, topic: str, payload: str, qos, user_data) -> None:
|
|
168
|
+
"""Is called when thing topic comes in."""
|
|
169
|
+
logger.debug(
|
|
170
|
+
"on_topic_message, receive message, topic:%s, payload:%s, qos:%d",
|
|
171
|
+
topic,
|
|
172
|
+
payload,
|
|
173
|
+
qos,
|
|
174
|
+
)
|
|
175
|
+
json_payload = json.loads(payload)
|
|
176
|
+
iot_id = json_payload.get("params", {}).get("iotId", "")
|
|
177
|
+
if iot_id != "" and self.on_message is not None:
|
|
178
|
+
future = asyncio.run_coroutine_threadsafe(self.on_message(topic, payload, iot_id), self.loop)
|
|
179
|
+
asyncio.wrap_future(future, loop=self.loop)
|
|
180
|
+
|
|
181
|
+
def _thing_on_connect(self, session_flag, rc, user_data) -> None:
|
|
182
|
+
"""Handle connection event and execute callback if set."""
|
|
183
|
+
self.is_connected = True
|
|
184
|
+
if self.on_connected is not None:
|
|
185
|
+
future = asyncio.run_coroutine_threadsafe(self.on_connected(), self.loop)
|
|
186
|
+
asyncio.wrap_future(future, loop=self.loop)
|
|
187
|
+
|
|
188
|
+
logger.debug("on_connect, session_flag:%d, rc:%d", session_flag, rc)
|
|
189
|
+
|
|
190
|
+
def _on_disconnect(self, _client, _userdata) -> None:
|
|
191
|
+
"""Is called on disconnect."""
|
|
192
|
+
if self._linkkit_client.check_state() is LinkKit.LinkKitState.DISCONNECTED:
|
|
193
|
+
logger.info("Disconnected")
|
|
194
|
+
self.is_connected = False
|
|
195
|
+
self.is_ready = False
|
|
196
|
+
if self.on_disconnected:
|
|
197
|
+
future = asyncio.run_coroutine_threadsafe(self.on_disconnected(), self.loop)
|
|
198
|
+
asyncio.wrap_future(future, loop=self.loop)
|
|
199
|
+
|
|
200
|
+
def _on_message(self, _client, _userdata, message: MQTTMessage) -> None:
|
|
201
|
+
"""Is called when message is received."""
|
|
202
|
+
logger.debug("Message on topic %s", message.topic)
|
|
203
|
+
|
|
204
|
+
payload = json.loads(message.payload)
|
|
205
|
+
if message.topic.endswith("/app/down/thing/events"):
|
|
206
|
+
event = ThingEventMessage(**payload)
|
|
207
|
+
params = event.params
|
|
208
|
+
if params.identifier == "device_protobuf_msg_event":
|
|
209
|
+
content = LubaMsg().parse(base64.b64decode(params.value.content))
|
|
210
|
+
|
|
211
|
+
logger.info("Unhandled protobuf event: %s", betterproto2.which_one_of(content, "LubaSubMsg"))
|
|
212
|
+
elif params.identifier == "device_warning_event":
|
|
213
|
+
logger.debug("identifier event: %s", params.identifier)
|
|
214
|
+
else:
|
|
215
|
+
logger.info("Unhandled event: %s", params.identifier)
|
|
216
|
+
elif message.topic.endswith("/app/down/thing/status"):
|
|
217
|
+
# the tell if a device has come back online
|
|
218
|
+
# lastStatus
|
|
219
|
+
# 1 online?
|
|
220
|
+
# 3 offline?
|
|
221
|
+
status = ThingStatusMessage(**payload)
|
|
222
|
+
logger.debug(status.params.status.value)
|
|
223
|
+
elif message.topic.endswith("/app/down/thing/properties"):
|
|
224
|
+
properties = ThingPropertiesMessage(**payload)
|
|
225
|
+
logger.debug("properties: %s", properties)
|
|
226
|
+
else:
|
|
227
|
+
logger.debug("Unhandled topic: %s", message.topic)
|
|
228
|
+
logger.debug(payload)
|
|
229
|
+
|
|
230
|
+
async def send_cloud_command(self, iot_id: str, command: bytes) -> str:
|
|
231
|
+
"""Return internal cloud client."""
|
|
232
|
+
return await self._cloud_client.send_cloud_command(iot_id, command)
|