pymammotion 0.2.62__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 +9 -6
- pymammotion/aliyun/client.py +235 -0
- pymammotion/aliyun/cloud_gateway.py +320 -69
- 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 +11 -15
- pymammotion/bluetooth/ble_message.py +389 -106
- pymammotion/bluetooth/model/atomic_integer.py +54 -0
- pymammotion/const.py +3 -0
- pymammotion/data/model/__init__.py +1 -2
- pymammotion/data/model/device.py +92 -240
- pymammotion/data/model/device_config.py +10 -24
- pymammotion/data/model/device_info.py +35 -0
- pymammotion/data/model/device_limits.py +49 -0
- 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 +3 -4
- pymammotion/data/model/hash_list.py +384 -48
- pymammotion/data/model/location.py +4 -4
- pymammotion/data/model/mowing_modes.py +24 -1
- pymammotion/data/model/raw_data.py +215 -0
- pymammotion/data/model/region_data.py +10 -11
- pymammotion/data/model/report_info.py +62 -6
- 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 +32 -8
- pymammotion/homeassistant/__init__.py +3 -0
- pymammotion/homeassistant/mower_api.py +484 -0
- pymammotion/homeassistant/rtk_api.py +54 -0
- pymammotion/http/__init__.py +0 -0
- pymammotion/http/encryption.py +220 -0
- pymammotion/http/http.py +652 -44
- 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 +160 -9
- 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 +32 -3
- 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 +93 -100
- pymammotion/mammotion/commands/messages/ota.py +18 -18
- pymammotion/mammotion/commands/messages/system.py +97 -72
- pymammotion/mammotion/commands/messages/video.py +17 -12
- pymammotion/mammotion/devices/__init__.py +27 -3
- pymammotion/mammotion/devices/base.py +50 -127
- pymammotion/mammotion/devices/mammotion.py +447 -212
- pymammotion/mammotion/devices/mammotion_bluetooth.py +105 -60
- pymammotion/mammotion/devices/mammotion_cloud.py +157 -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 +97 -51
- 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 +65 -21
- pymammotion/utility/datatype_converter.py +13 -12
- pymammotion/utility/device_config.py +755 -0
- pymammotion/utility/device_type.py +218 -21
- pymammotion/utility/map.py +238 -51
- pymammotion/utility/mur_mur_hash.py +159 -0
- {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info}/METADATA +27 -31
- pymammotion-0.5.51.dist-info/RECORD +152 -0
- {pymammotion-0.2.62.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 -130
- 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 -660
- pymammotion/proto/mctrl_ota.py +0 -48
- pymammotion/proto/mctrl_pept.py +0 -41
- pymammotion/proto/mctrl_sys.py +0 -574
- pymammotion-0.2.62.dist-info/RECORD +0 -125
- /pymammotion/{http/_init_.py → bluetooth/model/__init__.py} +0 -0
- {pymammotion-0.2.62.dist-info → pymammotion-0.5.51.dist-info/licenses}/LICENSE +0 -0
|
@@ -4,160 +4,155 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
-
from
|
|
8
|
-
from typing import Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
9
8
|
|
|
10
|
-
from bleak
|
|
9
|
+
from bleak import BLEDevice
|
|
11
10
|
|
|
11
|
+
from pymammotion import MammotionMQTT
|
|
12
12
|
from pymammotion.aliyun.cloud_gateway import CloudIOTGateway
|
|
13
13
|
from pymammotion.aliyun.model.dev_by_account_response import Device
|
|
14
|
-
from pymammotion.data.model.account import Credentials
|
|
15
14
|
from pymammotion.data.model.device import MowingDevice
|
|
16
|
-
from pymammotion.
|
|
17
|
-
from pymammotion.
|
|
18
|
-
from pymammotion.
|
|
19
|
-
from pymammotion.
|
|
15
|
+
from pymammotion.data.model.enums import ConnectionPreference
|
|
16
|
+
from pymammotion.http.http import MammotionHTTP
|
|
17
|
+
from pymammotion.http.model.camera_stream import StreamSubscriptionResponse, VideoResourceResponse
|
|
18
|
+
from pymammotion.http.model.http import DeviceRecord, Response
|
|
19
|
+
from pymammotion.mammotion.devices.mammotion_cloud import MammotionCloud
|
|
20
|
+
from pymammotion.mammotion.devices.mammotion_mower_ble import MammotionMowerBLEDevice
|
|
21
|
+
from pymammotion.mammotion.devices.managers.managers import AbstractDeviceManager
|
|
22
|
+
from pymammotion.mammotion.devices.mower_manager import MammotionMowerDeviceManager
|
|
23
|
+
from pymammotion.mammotion.devices.rtk_manager import MammotionRTKDeviceManager
|
|
24
|
+
from pymammotion.mqtt import AliyunMQTT
|
|
25
|
+
from pymammotion.utility.device_type import DeviceType
|
|
26
|
+
|
|
27
|
+
# RTK imports - imported here for type hints, full import in add_cloud_devices
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from pymammotion.mammotion.devices.rtk_ble import MammotionRTKBLEDevice
|
|
20
30
|
|
|
21
31
|
TIMEOUT_CLOUD_RESPONSE = 10
|
|
22
32
|
|
|
23
33
|
_LOGGER = logging.getLogger(__name__)
|
|
24
34
|
|
|
25
35
|
|
|
26
|
-
class
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
EITHER = 0
|
|
30
|
-
WIFI = 1
|
|
31
|
-
BLUETOOTH = 2
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class MammotionMixedDeviceManager:
|
|
35
|
-
_ble_device: MammotionBaseBLEDevice | None = None
|
|
36
|
-
_cloud_device: MammotionBaseCloudDevice | None = None
|
|
37
|
-
preference: ConnectionPreference
|
|
38
|
-
|
|
39
|
-
def __init__(
|
|
40
|
-
self,
|
|
41
|
-
name: str,
|
|
42
|
-
cloud_device: Device | None = None,
|
|
43
|
-
ble_device: BLEDevice | None = None,
|
|
44
|
-
mqtt: MammotionCloud | None = None,
|
|
45
|
-
preference: ConnectionPreference = ConnectionPreference.BLUETOOTH,
|
|
46
|
-
) -> None:
|
|
47
|
-
self.name = name
|
|
48
|
-
self._mower_state = MowingDevice()
|
|
49
|
-
self.add_ble(ble_device)
|
|
50
|
-
self.add_cloud(cloud_device, mqtt)
|
|
51
|
-
self.preference = preference
|
|
52
|
-
|
|
53
|
-
@property
|
|
54
|
-
def mower_state(self):
|
|
55
|
-
return self._mower_state
|
|
56
|
-
|
|
57
|
-
@mower_state.setter
|
|
58
|
-
def mower_state(self, value: MowingDevice) -> None:
|
|
59
|
-
if self._cloud_device:
|
|
60
|
-
self._cloud_device.state_manager.set_device(value)
|
|
61
|
-
if self._ble_device:
|
|
62
|
-
self._ble_device.state_manager.set_device(value)
|
|
63
|
-
self._mower_state = value
|
|
64
|
-
|
|
65
|
-
def ble(self) -> MammotionBaseBLEDevice | None:
|
|
66
|
-
return self._ble_device
|
|
67
|
-
|
|
68
|
-
def cloud(self) -> MammotionBaseCloudDevice | None:
|
|
69
|
-
return self._cloud_device
|
|
70
|
-
|
|
71
|
-
def has_queued_commands(self) -> bool:
|
|
72
|
-
if self.has_cloud() and self.preference == ConnectionPreference.WIFI:
|
|
73
|
-
return not self.cloud()._mqtt.command_queue.empty()
|
|
74
|
-
else:
|
|
75
|
-
return False
|
|
36
|
+
class MammotionDeviceManager:
|
|
37
|
+
"""Manage devices - both mowers and RTK."""
|
|
76
38
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
self.devices: dict[str, MammotionMowerDeviceManager] = {}
|
|
41
|
+
self.rtk_devices: dict[str, MammotionRTKDeviceManager] = {}
|
|
80
42
|
|
|
81
|
-
def
|
|
82
|
-
if
|
|
83
|
-
self._cloud_device = MammotionBaseCloudDevice(
|
|
84
|
-
mqtt, cloud_device=cloud_device, mowing_state=self.mower_state
|
|
85
|
-
)
|
|
43
|
+
def _should_disconnect_mqtt(self, device_for_removal: AbstractDeviceManager) -> bool:
|
|
44
|
+
"""Check if MQTT connection should be disconnected.
|
|
86
45
|
|
|
87
|
-
|
|
88
|
-
|
|
46
|
+
Returns True if no other devices share the same MQTT connection.
|
|
47
|
+
"""
|
|
48
|
+
if not device_for_removal.cloud:
|
|
49
|
+
return False
|
|
89
50
|
|
|
90
|
-
|
|
91
|
-
self._ble_device = ble_device
|
|
51
|
+
mqtt_to_check = device_for_removal.cloud.mqtt
|
|
92
52
|
|
|
93
|
-
|
|
94
|
-
|
|
53
|
+
# Check if any mower device shares this MQTT connection
|
|
54
|
+
shared_devices: set[AbstractDeviceManager] = {
|
|
55
|
+
device
|
|
56
|
+
for device in self.devices.values()
|
|
57
|
+
if device.cloud is not None and device.cloud.mqtt == mqtt_to_check
|
|
58
|
+
}
|
|
95
59
|
|
|
96
|
-
|
|
97
|
-
|
|
60
|
+
# Also check RTK devices for shared MQTT
|
|
61
|
+
shared_devices.update(
|
|
62
|
+
{
|
|
63
|
+
device
|
|
64
|
+
for device in self.rtk_devices.values()
|
|
65
|
+
if device.cloud is not None and device.cloud.mqtt == mqtt_to_check
|
|
66
|
+
}
|
|
67
|
+
)
|
|
98
68
|
|
|
99
|
-
|
|
100
|
-
return self._ble_device is not None
|
|
69
|
+
return len(shared_devices) == 0
|
|
101
70
|
|
|
71
|
+
def add_device(self, mammotion_device: MammotionMowerDeviceManager) -> None:
|
|
72
|
+
"""Add a mower device."""
|
|
73
|
+
exists: MammotionMowerDeviceManager | None = self.devices.get(mammotion_device.name)
|
|
74
|
+
if exists is None:
|
|
75
|
+
self.devices[mammotion_device.name] = mammotion_device
|
|
76
|
+
return
|
|
77
|
+
if mammotion_device.cloud is not None:
|
|
78
|
+
exists.replace_cloud(mammotion_device.cloud)
|
|
79
|
+
if mammotion_device.ble:
|
|
80
|
+
exists.replace_ble(mammotion_device.ble)
|
|
102
81
|
|
|
103
|
-
|
|
104
|
-
|
|
82
|
+
def add_rtk_device(self, rtk_device: MammotionRTKDeviceManager) -> None:
|
|
83
|
+
"""Add an RTK device."""
|
|
105
84
|
|
|
106
|
-
|
|
107
|
-
exists: MammotionMixedDeviceManager | None = self.devices.get(mammotion_device.name)
|
|
85
|
+
exists: MammotionRTKDeviceManager | None = self.rtk_devices.get(rtk_device.name)
|
|
108
86
|
if exists is None:
|
|
109
|
-
self.
|
|
87
|
+
self.rtk_devices[rtk_device.name] = rtk_device
|
|
110
88
|
return
|
|
111
|
-
if
|
|
112
|
-
exists.replace_cloud(
|
|
113
|
-
if
|
|
114
|
-
exists.replace_ble(
|
|
115
|
-
|
|
116
|
-
def
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
async def
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
89
|
+
if rtk_device.cloud:
|
|
90
|
+
exists.replace_cloud(rtk_device.cloud)
|
|
91
|
+
if rtk_device.ble:
|
|
92
|
+
exists.replace_ble(rtk_device.ble)
|
|
93
|
+
|
|
94
|
+
def has_device(self, mammotion_device_name: str) -> bool:
|
|
95
|
+
"""Check if a mower device exists."""
|
|
96
|
+
if self.devices.get(mammotion_device_name, None) is not None:
|
|
97
|
+
return True
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
def has_rtk_device(self, rtk_device_name: str) -> bool:
|
|
101
|
+
"""Check if an RTK device exists."""
|
|
102
|
+
if self.rtk_devices.get(rtk_device_name, None) is not None:
|
|
103
|
+
return True
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def get_device(self, mammotion_device_name: str) -> MammotionMowerDeviceManager:
|
|
107
|
+
"""Get a mower device."""
|
|
108
|
+
return self.devices[mammotion_device_name]
|
|
109
|
+
|
|
110
|
+
def get_rtk_device(self, rtk_device_name: str) -> MammotionRTKDeviceManager:
|
|
111
|
+
"""Get an RTK device."""
|
|
112
|
+
return self.rtk_devices[rtk_device_name]
|
|
113
|
+
|
|
114
|
+
async def remove_device(self, name: str) -> None:
|
|
115
|
+
"""Remove a mower device."""
|
|
116
|
+
if self.devices.get(name):
|
|
117
|
+
device_for_removal = self.devices.pop(name)
|
|
118
|
+
loop = asyncio.get_running_loop()
|
|
119
|
+
|
|
120
|
+
if device_for_removal.cloud:
|
|
121
|
+
if self._should_disconnect_mqtt(device_for_removal):
|
|
122
|
+
await loop.run_in_executor(None, device_for_removal.cloud.mqtt.disconnect)
|
|
123
|
+
await device_for_removal.cloud.stop()
|
|
124
|
+
|
|
125
|
+
if device_for_removal.ble:
|
|
126
|
+
await device_for_removal.ble.stop()
|
|
127
|
+
|
|
128
|
+
del device_for_removal
|
|
148
129
|
|
|
149
|
-
|
|
130
|
+
async def remove_rtk_device(self, name: str) -> None:
|
|
131
|
+
"""Remove an RTK device."""
|
|
132
|
+
if self.rtk_devices.get(name):
|
|
133
|
+
device_for_removal = self.rtk_devices.pop(name)
|
|
134
|
+
loop = asyncio.get_running_loop()
|
|
135
|
+
|
|
136
|
+
if device_for_removal.cloud:
|
|
137
|
+
if self._should_disconnect_mqtt(device_for_removal):
|
|
138
|
+
await loop.run_in_executor(None, device_for_removal.cloud.mqtt.disconnect)
|
|
139
|
+
await device_for_removal.cloud.stop()
|
|
140
|
+
|
|
141
|
+
if device_for_removal.ble:
|
|
142
|
+
await device_for_removal.ble.stop()
|
|
143
|
+
|
|
144
|
+
del device_for_removal
|
|
150
145
|
|
|
151
146
|
|
|
152
147
|
class Mammotion:
|
|
153
148
|
"""Represents a Mammotion account and its devices."""
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
mqtt_list: dict[str, MammotionCloud] = dict()
|
|
150
|
+
device_manager = MammotionDeviceManager()
|
|
157
151
|
|
|
158
|
-
_instance = None
|
|
152
|
+
_instance: Mammotion | None = None
|
|
159
153
|
|
|
160
|
-
def __new__(cls
|
|
154
|
+
def __new__(cls) -> Mammotion:
|
|
155
|
+
"""Create a singleton."""
|
|
161
156
|
if not cls._instance:
|
|
162
157
|
cls._instance = super().__new__(cls)
|
|
163
158
|
return cls._instance
|
|
@@ -165,142 +160,382 @@ class Mammotion:
|
|
|
165
160
|
def __init__(self) -> None:
|
|
166
161
|
"""Initialize MammotionDevice."""
|
|
167
162
|
self._login_lock = asyncio.Lock()
|
|
168
|
-
|
|
169
|
-
def add_ble_device(
|
|
170
|
-
self, ble_device: BLEDevice, preference: ConnectionPreference = ConnectionPreference.BLUETOOTH
|
|
171
|
-
) -> None:
|
|
172
|
-
if ble_device:
|
|
173
|
-
self.devices.add_device(
|
|
174
|
-
MammotionMixedDeviceManager(name=ble_device.name, ble_device=ble_device, preference=preference)
|
|
175
|
-
)
|
|
163
|
+
self.mqtt_list: dict[str, MammotionCloud] = {}
|
|
176
164
|
|
|
177
165
|
async def login_and_initiate_cloud(self, account, password, force: bool = False) -> None:
|
|
178
166
|
async with self._login_lock:
|
|
179
|
-
|
|
180
|
-
|
|
167
|
+
exists_aliyun: MammotionCloud | None = self.mqtt_list.get(f"{account}_aliyun")
|
|
168
|
+
exists_mammotion: MammotionCloud | None = self.mqtt_list.get(f"{account}_mammotion")
|
|
169
|
+
if (not exists_aliyun and not exists_mammotion) or force:
|
|
181
170
|
cloud_client = await self.login(account, password)
|
|
182
171
|
await self.initiate_cloud_connection(account, cloud_client)
|
|
183
172
|
|
|
173
|
+
async def refresh_login(self, account: str) -> None:
|
|
174
|
+
"""Refresh login."""
|
|
175
|
+
async with self._login_lock:
|
|
176
|
+
exists_aliyun: MammotionCloud | None = self.mqtt_list.get(f"{account}_aliyun")
|
|
177
|
+
exists_mammotion: MammotionCloud | None = self.mqtt_list.get(f"{account}_mammotion")
|
|
178
|
+
|
|
179
|
+
if not exists_aliyun and not exists_mammotion:
|
|
180
|
+
return
|
|
181
|
+
mammotion_http = (
|
|
182
|
+
exists_aliyun.cloud_client.mammotion_http
|
|
183
|
+
if exists_aliyun
|
|
184
|
+
else exists_mammotion.cloud_client.mammotion_http
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
await mammotion_http.refresh_login()
|
|
188
|
+
|
|
189
|
+
await self.connect_iot(exists_aliyun.cloud_client)
|
|
190
|
+
if len(mammotion_http.device_records.records) != 0:
|
|
191
|
+
await mammotion_http.get_mqtt_credentials()
|
|
192
|
+
|
|
193
|
+
if exists_aliyun and not exists_aliyun.is_connected():
|
|
194
|
+
loop = asyncio.get_running_loop()
|
|
195
|
+
await loop.run_in_executor(None, exists_aliyun.connect_async)
|
|
196
|
+
if exists_mammotion and not exists_mammotion.is_connected():
|
|
197
|
+
loop = asyncio.get_running_loop()
|
|
198
|
+
await loop.run_in_executor(None, exists_mammotion.connect_async)
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def shim_cloud_devices(devices: list[DeviceRecord]) -> list[Device]:
|
|
202
|
+
device_list: list[Device] = []
|
|
203
|
+
for device in devices:
|
|
204
|
+
device_list.append(
|
|
205
|
+
Device(
|
|
206
|
+
gmt_modified=0,
|
|
207
|
+
product_name="",
|
|
208
|
+
status=0,
|
|
209
|
+
net_type="NET_WIFI",
|
|
210
|
+
is_edge_gateway=False,
|
|
211
|
+
category_name="",
|
|
212
|
+
owned=1,
|
|
213
|
+
identity_alias="UNKNOW",
|
|
214
|
+
thing_type="DEVICE",
|
|
215
|
+
identity_id=device.identity_id,
|
|
216
|
+
device_name=device.device_name,
|
|
217
|
+
product_key=device.product_key,
|
|
218
|
+
iot_id=device.iot_id,
|
|
219
|
+
bind_time=device.bind_time,
|
|
220
|
+
node_type="DEVICE",
|
|
221
|
+
category_key="LawnMower",
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return device_list
|
|
226
|
+
|
|
227
|
+
async def initiate_ble_connection(self, devices: dict[str, BLEDevice], cloud_devices: list[Device]) -> None:
|
|
228
|
+
"""Initiate BLE connection."""
|
|
229
|
+
for device in cloud_devices:
|
|
230
|
+
if ble_device := devices.get(device.device_name):
|
|
231
|
+
if device.device_name.startswith(("Luba-", "Yuka-")):
|
|
232
|
+
if not self.device_manager.has_device(device.device_name):
|
|
233
|
+
self.device_manager.add_device(
|
|
234
|
+
MammotionMowerDeviceManager(
|
|
235
|
+
name=device.device_name,
|
|
236
|
+
iot_id=device.iot_id,
|
|
237
|
+
cloud_device=device,
|
|
238
|
+
ble_device=ble_device,
|
|
239
|
+
preference=ConnectionPreference.BLUETOOTH,
|
|
240
|
+
cloud_client=CloudIOTGateway(MammotionHTTP()),
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
self.device_manager.get_device(device.device_name).add_ble(ble_device)
|
|
245
|
+
if device.device_name.startswith(("RTK", "RBS")):
|
|
246
|
+
if not self.device_manager.has_rtk_device(device.device_name):
|
|
247
|
+
self.device_manager.add_rtk_device(
|
|
248
|
+
MammotionRTKDeviceManager(
|
|
249
|
+
name=device.device_name,
|
|
250
|
+
iot_id=device.iot_id,
|
|
251
|
+
cloud_device=device,
|
|
252
|
+
ble_device=ble_device,
|
|
253
|
+
preference=ConnectionPreference.BLUETOOTH,
|
|
254
|
+
cloud_client=CloudIOTGateway(MammotionHTTP()),
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
self.device_manager.get_rtk_device(device.device_name).add_ble(ble_device)
|
|
259
|
+
|
|
184
260
|
async def initiate_cloud_connection(self, account: str, cloud_client: CloudIOTGateway) -> None:
|
|
261
|
+
"""Initiate cloud connection."""
|
|
185
262
|
loop = asyncio.get_running_loop()
|
|
186
|
-
if self.mqtt_list.get(account) is not None:
|
|
187
|
-
if self.mqtt_list.get(account).is_connected():
|
|
188
|
-
await loop.run_in_executor(None, self.mqtt_list.get(account).disconnect)
|
|
189
|
-
|
|
190
|
-
mammotion_cloud = MammotionCloud(
|
|
191
|
-
MammotionMQTT(
|
|
192
|
-
region_id=cloud_client.region_response.data.regionId,
|
|
193
|
-
product_key=cloud_client.aep_response.data.productKey,
|
|
194
|
-
device_name=cloud_client.aep_response.data.deviceName,
|
|
195
|
-
device_secret=cloud_client.aep_response.data.deviceSecret,
|
|
196
|
-
iot_token=cloud_client.session_by_authcode_response.data.iotToken,
|
|
197
|
-
client_id=cloud_client.client_id,
|
|
198
|
-
cloud_client=cloud_client,
|
|
199
|
-
),
|
|
200
|
-
cloud_client,
|
|
201
|
-
)
|
|
202
|
-
self.mqtt_list[account] = mammotion_cloud
|
|
203
|
-
self.add_cloud_devices(mammotion_cloud)
|
|
204
263
|
|
|
205
|
-
|
|
264
|
+
mammotion_http = cloud_client.mammotion_http
|
|
265
|
+
|
|
266
|
+
if mqtt := self.mqtt_list.get(f"{account}_aliyun"):
|
|
267
|
+
if mqtt.is_connected():
|
|
268
|
+
await loop.run_in_executor(None, mqtt.disconnect)
|
|
269
|
+
|
|
270
|
+
if mqtt := self.mqtt_list.get(f"{account}_mammotion"):
|
|
271
|
+
if mqtt.is_connected():
|
|
272
|
+
await loop.run_in_executor(None, mqtt.disconnect)
|
|
273
|
+
|
|
274
|
+
if len(cloud_client.devices_by_account_response.data.data) != 0:
|
|
275
|
+
mammotion_cloud = MammotionCloud(
|
|
276
|
+
AliyunMQTT(
|
|
277
|
+
region_id=cloud_client.region_response.data.regionId,
|
|
278
|
+
product_key=cloud_client.aep_response.data.productKey,
|
|
279
|
+
device_name=cloud_client.aep_response.data.deviceName,
|
|
280
|
+
device_secret=cloud_client.aep_response.data.deviceSecret,
|
|
281
|
+
iot_token=cloud_client.session_by_authcode_response.data.iotToken,
|
|
282
|
+
client_id=cloud_client.client_id,
|
|
283
|
+
cloud_client=cloud_client,
|
|
284
|
+
),
|
|
285
|
+
cloud_client,
|
|
286
|
+
)
|
|
287
|
+
self.mqtt_list[f"{account}_aliyun"] = mammotion_cloud
|
|
288
|
+
self.add_cloud_devices(mammotion_cloud)
|
|
289
|
+
|
|
290
|
+
await loop.run_in_executor(None, self.mqtt_list[f"{account}_aliyun"].connect_async)
|
|
291
|
+
if len(mammotion_http.device_records.records) != 0:
|
|
292
|
+
mammotion_cloud = MammotionCloud(
|
|
293
|
+
MammotionMQTT(
|
|
294
|
+
records=mammotion_http.device_records.records,
|
|
295
|
+
mammotion_http=mammotion_http,
|
|
296
|
+
mqtt_connection=mammotion_http.mqtt_credentials,
|
|
297
|
+
),
|
|
298
|
+
cloud_client,
|
|
299
|
+
)
|
|
300
|
+
self.mqtt_list[f"{account}_mammotion"] = mammotion_cloud
|
|
301
|
+
self.add_mammotion_devices(mammotion_cloud, mammotion_http.device_records.records)
|
|
302
|
+
|
|
303
|
+
await loop.run_in_executor(None, self.mqtt_list[f"{account}_mammotion"].connect_async)
|
|
304
|
+
|
|
305
|
+
def add_mammotion_devices(self, mqtt_client: MammotionCloud, devices: list[DeviceRecord]) -> None:
|
|
306
|
+
"""Add devices from mammotion cloud."""
|
|
307
|
+
for device in devices:
|
|
308
|
+
if device.device_name.startswith(("Luba-", "Yuka-")):
|
|
309
|
+
has_device = self.device_manager.has_device(device.device_name)
|
|
310
|
+
if has_device:
|
|
311
|
+
mower_device = self.device_manager.get_device(device.device_name)
|
|
312
|
+
if mower_device.cloud is None:
|
|
313
|
+
mower_device.add_cloud(mqtt=mqtt_client)
|
|
314
|
+
else:
|
|
315
|
+
mower_device.replace_mqtt(mqtt_client)
|
|
316
|
+
|
|
317
|
+
else:
|
|
318
|
+
cloud_device_shim = Device(
|
|
319
|
+
gmt_modified=0,
|
|
320
|
+
product_name="",
|
|
321
|
+
status=0,
|
|
322
|
+
net_type="NET_WIFI",
|
|
323
|
+
is_edge_gateway=False,
|
|
324
|
+
category_name="",
|
|
325
|
+
owned=1,
|
|
326
|
+
identity_alias="UNKNOW",
|
|
327
|
+
thing_type="DEVICE",
|
|
328
|
+
identity_id=device.identity_id,
|
|
329
|
+
device_name=device.device_name,
|
|
330
|
+
product_key=device.product_key,
|
|
331
|
+
iot_id=device.iot_id,
|
|
332
|
+
bind_time=device.bind_time,
|
|
333
|
+
node_type="DEVICE",
|
|
334
|
+
category_key="LawnMower",
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
mixed_device = MammotionMowerDeviceManager(
|
|
338
|
+
name=device.device_name,
|
|
339
|
+
iot_id=device.iot_id,
|
|
340
|
+
cloud_client=mqtt_client.cloud_client,
|
|
341
|
+
cloud_device=cloud_device_shim,
|
|
342
|
+
mqtt=mqtt_client,
|
|
343
|
+
preference=ConnectionPreference.WIFI,
|
|
344
|
+
)
|
|
345
|
+
mixed_device.state.mower_state.product_key = device.product_key
|
|
346
|
+
self.device_manager.add_device(mixed_device)
|
|
206
347
|
|
|
207
348
|
def add_cloud_devices(self, mqtt_client: MammotionCloud) -> None:
|
|
349
|
+
"""Add devices from cloud - both mowers and RTK."""
|
|
350
|
+
from pymammotion.mammotion.devices.rtk_manager import MammotionRTKDeviceManager
|
|
351
|
+
|
|
208
352
|
for device in mqtt_client.cloud_client.devices_by_account_response.data.data:
|
|
209
|
-
|
|
210
|
-
if device.
|
|
211
|
-
self.
|
|
212
|
-
|
|
213
|
-
|
|
353
|
+
# Handle mower devices (Luba, Yuka)
|
|
354
|
+
if device.device_name.startswith(("Luba-", "Yuka-")):
|
|
355
|
+
has_device = self.device_manager.has_device(device.device_name)
|
|
356
|
+
if not has_device:
|
|
357
|
+
mixed_device = MammotionMowerDeviceManager(
|
|
358
|
+
name=device.device_name,
|
|
359
|
+
iot_id=device.iot_id,
|
|
360
|
+
cloud_client=mqtt_client.cloud_client,
|
|
214
361
|
cloud_device=device,
|
|
215
362
|
mqtt=mqtt_client,
|
|
216
363
|
preference=ConnectionPreference.WIFI,
|
|
217
364
|
)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
365
|
+
mixed_device.state.mower_state.product_key = device.product_key
|
|
366
|
+
mixed_device.state.mower_state.model = (
|
|
367
|
+
device.product_name if device.product_model is None else device.product_model
|
|
368
|
+
)
|
|
369
|
+
self.device_manager.add_device(mixed_device)
|
|
222
370
|
else:
|
|
223
|
-
mower_device.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
371
|
+
mower_device = self.device_manager.get_device(device.device_name)
|
|
372
|
+
if mower_device.cloud is None:
|
|
373
|
+
mower_device.add_cloud(mqtt=mqtt_client)
|
|
374
|
+
else:
|
|
375
|
+
mower_device.replace_mqtt(mqtt_client)
|
|
376
|
+
|
|
377
|
+
# Handle RTK devices
|
|
378
|
+
elif device.device_name.startswith(("RTK", "RBS")):
|
|
379
|
+
has_rtk_device = self.device_manager.has_rtk_device(device.device_name)
|
|
380
|
+
if not has_rtk_device:
|
|
381
|
+
rtk_device = MammotionRTKDeviceManager(
|
|
382
|
+
name=device.device_name,
|
|
383
|
+
iot_id=device.iot_id,
|
|
384
|
+
cloud_client=mqtt_client.cloud_client,
|
|
385
|
+
cloud_device=device,
|
|
386
|
+
mqtt=mqtt_client,
|
|
387
|
+
preference=ConnectionPreference.WIFI,
|
|
388
|
+
)
|
|
389
|
+
self.device_manager.add_rtk_device(rtk_device)
|
|
390
|
+
else:
|
|
391
|
+
rtk_device = self.device_manager.get_rtk_device(device.device_name)
|
|
392
|
+
if rtk_device.cloud is None:
|
|
393
|
+
rtk_device.add_cloud(mqtt=mqtt_client)
|
|
394
|
+
else:
|
|
395
|
+
rtk_device.replace_mqtt(mqtt_client)
|
|
396
|
+
|
|
397
|
+
def set_disconnect_strategy(self, *, disconnect: bool) -> None:
|
|
398
|
+
"""Set disconnect strategy for all BLE devices (mowers and RTK)."""
|
|
399
|
+
for device in self.device_manager.devices.values():
|
|
400
|
+
if device.ble is not None:
|
|
401
|
+
ble_device: MammotionMowerBLEDevice = device.ble
|
|
402
|
+
ble_device.set_disconnect_strategy(disconnect=disconnect)
|
|
403
|
+
|
|
404
|
+
for rtk_device in self.device_manager.rtk_devices.values():
|
|
405
|
+
if rtk_device.ble is not None:
|
|
406
|
+
ble_rtk_device: MammotionRTKBLEDevice = rtk_device.ble
|
|
407
|
+
ble_rtk_device.set_disconnect_strategy(disconnect=disconnect)
|
|
230
408
|
|
|
231
409
|
async def login(self, account: str, password: str) -> CloudIOTGateway:
|
|
232
410
|
"""Login to mammotion cloud."""
|
|
233
|
-
|
|
234
|
-
|
|
411
|
+
mammotion_http = MammotionHTTP()
|
|
412
|
+
await mammotion_http.login_v2(account, password)
|
|
413
|
+
await mammotion_http.get_user_device_page()
|
|
414
|
+
device_list = await mammotion_http.get_user_device_list()
|
|
415
|
+
_LOGGER.debug("device_list: %s", device_list)
|
|
416
|
+
await mammotion_http.get_mqtt_credentials()
|
|
417
|
+
cloud_client = CloudIOTGateway(mammotion_http)
|
|
418
|
+
await self.connect_iot(cloud_client)
|
|
419
|
+
return cloud_client
|
|
420
|
+
|
|
421
|
+
@staticmethod
|
|
422
|
+
async def connect_iot(cloud_client: CloudIOTGateway) -> None:
|
|
423
|
+
"""Connect to aliyun cloud and fetch device info."""
|
|
424
|
+
mammotion_http = cloud_client.mammotion_http
|
|
235
425
|
country_code = mammotion_http.login_info.userInformation.domainAbbreviation
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
cloud_client.set_http(mammotion_http)
|
|
239
|
-
loop = asyncio.get_running_loop()
|
|
240
|
-
await loop.run_in_executor(
|
|
241
|
-
None, cloud_client.get_region, country_code, mammotion_http.login_info.authorization_code
|
|
242
|
-
)
|
|
426
|
+
if cloud_client.region_response is None:
|
|
427
|
+
await cloud_client.get_region(country_code)
|
|
243
428
|
await cloud_client.connect()
|
|
244
|
-
await cloud_client.login_by_oauth(country_code
|
|
245
|
-
await
|
|
246
|
-
await
|
|
247
|
-
|
|
248
|
-
await loop.run_in_executor(None, cloud_client.list_binding_by_account)
|
|
249
|
-
return cloud_client
|
|
429
|
+
await cloud_client.login_by_oauth(country_code)
|
|
430
|
+
await cloud_client.aep_handle()
|
|
431
|
+
await cloud_client.session_by_auth_code()
|
|
432
|
+
await cloud_client.list_binding_by_account()
|
|
250
433
|
|
|
251
434
|
async def remove_device(self, name: str) -> None:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
435
|
+
"""Remove a mower device."""
|
|
436
|
+
await self.device_manager.remove_device(name)
|
|
437
|
+
|
|
438
|
+
async def remove_rtk_device(self, name: str) -> None:
|
|
439
|
+
"""Remove an RTK device."""
|
|
440
|
+
await self.device_manager.remove_rtk_device(name)
|
|
441
|
+
|
|
442
|
+
def get_device_by_name(self, name: str) -> MammotionMowerDeviceManager:
|
|
443
|
+
"""Get a mower device by name."""
|
|
444
|
+
return self.device_manager.get_device(name)
|
|
445
|
+
|
|
446
|
+
def get_rtk_device_by_name(self, name: str) -> MammotionRTKDeviceManager:
|
|
447
|
+
"""Get an RTK device by name."""
|
|
448
|
+
return self.device_manager.get_rtk_device(name)
|
|
449
|
+
|
|
450
|
+
def get_or_create_device_by_name(
|
|
451
|
+
self, device: Device, mqtt_client: MammotionCloud | None, ble_device: BLEDevice | None
|
|
452
|
+
) -> MammotionMowerDeviceManager:
|
|
453
|
+
"""Get or create a mower device by name."""
|
|
454
|
+
if self.device_manager.has_device(device.device_name):
|
|
455
|
+
return self.device_manager.get_device(device.device_name)
|
|
456
|
+
mow_device = MammotionMowerDeviceManager(
|
|
457
|
+
name=device.device_name,
|
|
458
|
+
iot_id=device.iot_id,
|
|
459
|
+
cloud_client=mqtt_client.cloud_client if mqtt_client else CloudIOTGateway(MammotionHTTP()),
|
|
460
|
+
mqtt=mqtt_client,
|
|
461
|
+
cloud_device=device,
|
|
462
|
+
ble_device=ble_device,
|
|
463
|
+
preference=ConnectionPreference.WIFI if mqtt_client else ConnectionPreference.BLUETOOTH,
|
|
464
|
+
)
|
|
465
|
+
self.device_manager.add_device(mow_device)
|
|
466
|
+
return mow_device
|
|
256
467
|
|
|
257
468
|
async def send_command(self, name: str, key: str):
|
|
258
469
|
"""Send a command to the device."""
|
|
259
470
|
device = self.get_device_by_name(name)
|
|
260
471
|
if device:
|
|
261
|
-
if device.preference is ConnectionPreference.BLUETOOTH:
|
|
262
|
-
return await device.ble
|
|
263
|
-
if device.preference is ConnectionPreference.WIFI:
|
|
264
|
-
return await device.cloud
|
|
472
|
+
if device.preference is ConnectionPreference.BLUETOOTH and device.ble:
|
|
473
|
+
return await device.ble.command(key)
|
|
474
|
+
if device.preference is ConnectionPreference.WIFI and device.cloud:
|
|
475
|
+
return await device.cloud.command(key)
|
|
265
476
|
# TODO work with both with EITHER
|
|
477
|
+
return None
|
|
266
478
|
|
|
267
479
|
async def send_command_with_args(self, name: str, key: str, **kwargs: Any):
|
|
268
480
|
"""Send a command with args to the device."""
|
|
269
481
|
device = self.get_device_by_name(name)
|
|
270
482
|
if device:
|
|
271
|
-
if device.preference is ConnectionPreference.BLUETOOTH:
|
|
272
|
-
return await device.ble
|
|
273
|
-
if device.preference is ConnectionPreference.WIFI:
|
|
274
|
-
return await device.cloud
|
|
483
|
+
if device.preference is ConnectionPreference.BLUETOOTH and device.ble:
|
|
484
|
+
return await device.ble.command(key, **kwargs)
|
|
485
|
+
if device.preference is ConnectionPreference.WIFI and device.cloud:
|
|
486
|
+
return await device.cloud.command(key, **kwargs)
|
|
275
487
|
# TODO work with both with EITHER
|
|
488
|
+
return None
|
|
276
489
|
|
|
277
|
-
async def
|
|
490
|
+
async def start_map_sync(self, name: str) -> None:
|
|
491
|
+
"""Start map sync."""
|
|
278
492
|
device = self.get_device_by_name(name)
|
|
279
493
|
if device:
|
|
280
|
-
if device.preference is ConnectionPreference.BLUETOOTH:
|
|
281
|
-
return await device.ble
|
|
282
|
-
if device.preference is ConnectionPreference.WIFI:
|
|
283
|
-
return await device.cloud
|
|
494
|
+
if device.preference is ConnectionPreference.BLUETOOTH and device.ble:
|
|
495
|
+
return await device.ble.start_map_sync()
|
|
496
|
+
if device.preference is ConnectionPreference.WIFI and device.cloud:
|
|
497
|
+
return await device.cloud.start_map_sync()
|
|
284
498
|
# TODO work with both with EITHER
|
|
499
|
+
return None
|
|
285
500
|
|
|
286
|
-
async def
|
|
501
|
+
async def start_schedule_sync(self, name: str) -> None:
|
|
502
|
+
"""Start map sync."""
|
|
287
503
|
device = self.get_device_by_name(name)
|
|
288
504
|
if device:
|
|
289
|
-
if device.preference is ConnectionPreference.BLUETOOTH:
|
|
290
|
-
return await device.ble
|
|
291
|
-
if device.preference is ConnectionPreference.WIFI:
|
|
292
|
-
return await device.cloud
|
|
505
|
+
if device.preference is ConnectionPreference.BLUETOOTH and device.ble:
|
|
506
|
+
return await device.ble.start_schedule_sync()
|
|
507
|
+
if device.preference is ConnectionPreference.WIFI and device.cloud:
|
|
508
|
+
return await device.cloud.start_schedule_sync()
|
|
293
509
|
# TODO work with both with EITHER
|
|
510
|
+
return None
|
|
511
|
+
|
|
512
|
+
async def get_stream_subscription(self, name: str, iot_id: str) -> Response[StreamSubscriptionResponse] | Any:
|
|
513
|
+
"""Get stream subscription."""
|
|
514
|
+
device = self.get_device_by_name(name)
|
|
515
|
+
if DeviceType.is_mini_or_x_series(name):
|
|
516
|
+
_stream_response = await device.mammotion_http.get_stream_subscription_mini_or_x_series(
|
|
517
|
+
iot_id, DeviceType.is_yuka(name) and not DeviceType.is_yuka_mini(name)
|
|
518
|
+
)
|
|
519
|
+
_LOGGER.debug(_stream_response)
|
|
520
|
+
return _stream_response
|
|
521
|
+
else:
|
|
522
|
+
_stream_response = await device.mammotion_http.get_stream_subscription(iot_id)
|
|
523
|
+
_LOGGER.debug(_stream_response)
|
|
524
|
+
return _stream_response
|
|
294
525
|
|
|
295
|
-
async def
|
|
526
|
+
async def get_video_resource(self, name: str, iot_id: str) -> Response[VideoResourceResponse] | None:
|
|
527
|
+
"""Get video resource."""
|
|
296
528
|
device = self.get_device_by_name(name)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
529
|
+
|
|
530
|
+
if DeviceType.is_mini_or_x_series(name):
|
|
531
|
+
_video_resource_response = await device.mammotion_http.get_video_resource(iot_id)
|
|
532
|
+
_LOGGER.debug(_video_resource_response)
|
|
533
|
+
return _video_resource_response
|
|
534
|
+
return None
|
|
302
535
|
|
|
303
536
|
def mower(self, name: str) -> MowingDevice | None:
|
|
537
|
+
"""Get a mower device by name."""
|
|
304
538
|
device = self.get_device_by_name(name)
|
|
305
539
|
if device:
|
|
306
|
-
return device.
|
|
540
|
+
return device.state
|
|
541
|
+
return None
|