meshcore 2.3.4__tar.gz → 2.3.6__tar.gz
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.
- {meshcore-2.3.4 → meshcore-2.3.6}/PKG-INFO +1 -1
- {meshcore-2.3.4 → meshcore-2.3.6}/pyproject.toml +1 -1
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/commands/base.py +1 -0
- meshcore-2.3.6/src/meshcore/commands/binary.py +357 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/commands/messaging.py +48 -10
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/meshcore_parser.py +5 -8
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/unit/test_commands.py +33 -43
- meshcore-2.3.6/tests/unit/test_lpp_parsing.py +39 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/unit/test_private_key_export.py +18 -9
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/unit/test_reader.py +4 -3
- meshcore-2.3.4/src/meshcore/commands/binary.py +0 -356
- {meshcore-2.3.4 → meshcore-2.3.6}/.github/python-test.yml +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/.gitignore +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/LICENSE +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/README.md +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/.pubsub_example.py.swp +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_chat.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_pin_pairing_example.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_private_key_export.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_sign_example.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_stats.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_t1000_chan_msg.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_t1000_custom_vars.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_t1000_infos.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_t1000_msg.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_t1000_msg_retries.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/ble_t1000_set_cv.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/chan_recv_with_path.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/connection_events_example.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/mepo_mc_gps.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/pubsub_example.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/rf_packet_monitor.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_battery_monitor.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_channel_manager.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_chat.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_contacts.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_infos.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_meshcore_ollama.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_msg.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_pingbot.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_repeater_status.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_repeater_telemetry.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_rss_bot.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/serial_trace.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/tcp_chat.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/tcp_login_status.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/tcp_mchome_contacts.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/tcp_mchome_infos.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/tcp_mchome_msg.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/examples/tcp_mchome_readmsgs.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/pytest.ini +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/__init__.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/ble_cx.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/commands/__init__.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/commands/contact.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/commands/control_data.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/commands/device.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/connection_manager.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/events.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/lpp_json_encoder.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/meshcore.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/packets.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/parsing.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/reader.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/serial_cx.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/src/meshcore/tcp_cx.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/README.md +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/test_ble_connection.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/test_ble_pin_pairing.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/test_meshcore_ble_pin.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/unit/test_events.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/unit/test_path_discovery_response.py +0 -0
- {meshcore-2.3.4 → meshcore-2.3.6}/tests/unit/test_serial_connection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: Base classes for communicating with meshcore companion radios
|
|
5
5
|
Project-URL: Homepage, https://github.com/fdlamotte/meshcore_py
|
|
6
6
|
Project-URL: Issues, https://github.com/fdlamotte/meshcore_py/issues
|
|
@@ -64,6 +64,7 @@ class CommandHandlerBase:
|
|
|
64
64
|
self._sender_func: Optional[Callable[[bytes], Coroutine[Any, Any, None]]] = None
|
|
65
65
|
self._reader: Optional[MessageReader] = None
|
|
66
66
|
self.dispatcher: Optional[EventDispatcher] = None
|
|
67
|
+
self._mesh_request_lock = asyncio.Lock()
|
|
67
68
|
self.default_timeout = (
|
|
68
69
|
default_timeout if default_timeout is not None else self.DEFAULT_TIMEOUT
|
|
69
70
|
)
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import random
|
|
4
|
+
import io
|
|
5
|
+
|
|
6
|
+
from .base import CommandHandlerBase
|
|
7
|
+
from ..events import EventType
|
|
8
|
+
from ..packets import BinaryReqType
|
|
9
|
+
from ..packets import AnonReqType
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("meshcore")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BinaryCommandHandler(CommandHandlerBase):
|
|
15
|
+
"""Helper functions to handle binary requests through binary commands"""
|
|
16
|
+
|
|
17
|
+
async def req_status(self, contact, timeout=0, min_timeout=0):
|
|
18
|
+
logger.error("*** please consider using req_status_sync instead of req_status")
|
|
19
|
+
return await self.req_status_sync(contact, timeout, min_timeout)
|
|
20
|
+
|
|
21
|
+
async def req_status_sync(self, contact, timeout=0, min_timeout=0):
|
|
22
|
+
async with self._mesh_request_lock:
|
|
23
|
+
res = await self.send_binary_req(
|
|
24
|
+
contact,
|
|
25
|
+
BinaryReqType.STATUS,
|
|
26
|
+
timeout=timeout,
|
|
27
|
+
min_timeout=min_timeout
|
|
28
|
+
)
|
|
29
|
+
if res.type == EventType.ERROR:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
exp_tag = res.payload["expected_ack"].hex()
|
|
33
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
34
|
+
timeout = timeout if min_timeout < timeout else min_timeout
|
|
35
|
+
|
|
36
|
+
if self.dispatcher is None:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
status_event = await self.dispatcher.wait_for_event(
|
|
40
|
+
EventType.STATUS_RESPONSE,
|
|
41
|
+
attribute_filters={"tag": exp_tag},
|
|
42
|
+
timeout=timeout,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return status_event.payload if status_event else None
|
|
46
|
+
|
|
47
|
+
async def req_telemetry(self, contact, timeout=0, min_timeout=0):
|
|
48
|
+
logger.error("*** please consider using req_telemetry_sync instead of req_telemetry")
|
|
49
|
+
return await self.req_telemetry_sync(contact, timeout, min_timeout)
|
|
50
|
+
|
|
51
|
+
async def req_telemetry_sync(self, contact, timeout=0, min_timeout=0):
|
|
52
|
+
async with self._mesh_request_lock:
|
|
53
|
+
res = await self.send_binary_req(
|
|
54
|
+
contact,
|
|
55
|
+
BinaryReqType.TELEMETRY,
|
|
56
|
+
timeout=timeout,
|
|
57
|
+
min_timeout=min_timeout
|
|
58
|
+
)
|
|
59
|
+
if res.type == EventType.ERROR:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
63
|
+
timeout = timeout if min_timeout < timeout else min_timeout
|
|
64
|
+
|
|
65
|
+
if self.dispatcher is None:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
telem_event = await self.dispatcher.wait_for_event(
|
|
69
|
+
EventType.TELEMETRY_RESPONSE,
|
|
70
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
71
|
+
timeout=timeout,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return telem_event.payload["lpp"] if telem_event else None
|
|
75
|
+
|
|
76
|
+
async def req_mma(self, contact, timeout=0, min_timeout=0):
|
|
77
|
+
logger.error("*** please consider using req_mma_sync instead of req_mma")
|
|
78
|
+
return await self.req_mma_sync(contact, start, end, timeout,min_timeout)
|
|
79
|
+
|
|
80
|
+
async def req_mma_sync(self, contact, start, end, timeout=0,min_timeout=0):
|
|
81
|
+
async with self._mesh_request_lock:
|
|
82
|
+
req = (
|
|
83
|
+
start.to_bytes(4, "little", signed=False)
|
|
84
|
+
+ end.to_bytes(4, "little", signed=False)
|
|
85
|
+
+ b"\0\0"
|
|
86
|
+
)
|
|
87
|
+
res = await self.send_binary_req(
|
|
88
|
+
contact,
|
|
89
|
+
BinaryReqType.MMA,
|
|
90
|
+
data=req,
|
|
91
|
+
timeout=timeout
|
|
92
|
+
)
|
|
93
|
+
if res.type == EventType.ERROR:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
97
|
+
timeout = timeout if min_timeout < timeout else min_timeout
|
|
98
|
+
|
|
99
|
+
if self.dispatcher is None:
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
mma_event = await self.dispatcher.wait_for_event(
|
|
103
|
+
EventType.MMA_RESPONSE,
|
|
104
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
105
|
+
timeout=timeout,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return mma_event.payload["mma_data"] if mma_event else None
|
|
109
|
+
|
|
110
|
+
async def req_acl(self, contact, timeout=0, min_timeout=0):
|
|
111
|
+
logger.error("*** please consider using req_acl_sync instead of req_acl")
|
|
112
|
+
return await self.req_acl_sync(contact, timeout, min_timeout)
|
|
113
|
+
|
|
114
|
+
async def req_acl_sync(self, contact, timeout=0, min_timeout=0):
|
|
115
|
+
async with self._mesh_request_lock:
|
|
116
|
+
req = b"\0\0"
|
|
117
|
+
res = await self.send_binary_req(
|
|
118
|
+
contact,
|
|
119
|
+
BinaryReqType.ACL,
|
|
120
|
+
data=req,
|
|
121
|
+
timeout=timeout
|
|
122
|
+
)
|
|
123
|
+
if res.type == EventType.ERROR:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
127
|
+
timeout = timeout if timeout > min_timeout else min_timeout
|
|
128
|
+
|
|
129
|
+
if self.dispatcher is None:
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
acl_event = await self.dispatcher.wait_for_event(
|
|
133
|
+
EventType.ACL_RESPONSE,
|
|
134
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
135
|
+
timeout=timeout,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return acl_event.payload["acl_data"] if acl_event else None
|
|
139
|
+
|
|
140
|
+
async def req_neighbours_async(self,
|
|
141
|
+
contact,
|
|
142
|
+
count=255,
|
|
143
|
+
offset=0,
|
|
144
|
+
order_by=0,
|
|
145
|
+
pubkey_prefix_length=4,
|
|
146
|
+
timeout=0,
|
|
147
|
+
min_timeout=0
|
|
148
|
+
):
|
|
149
|
+
req = (b"\x00" # version : 0
|
|
150
|
+
+ count.to_bytes(1, "little", signed=False)
|
|
151
|
+
+ offset.to_bytes(2, "little", signed=False)
|
|
152
|
+
+ order_by.to_bytes(1, "little", signed=False)
|
|
153
|
+
+ pubkey_prefix_length.to_bytes(1, "little", signed=False)
|
|
154
|
+
+ random.randint(1, 0xFFFFFFFF).to_bytes(4, "little", signed=False)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
logger.debug(f"Sending binary neighbours req, count: {count}, offset: {offset} {req.hex()}")
|
|
158
|
+
|
|
159
|
+
return await self.send_binary_req (
|
|
160
|
+
contact,
|
|
161
|
+
BinaryReqType.NEIGHBOURS,
|
|
162
|
+
data=req,
|
|
163
|
+
timeout=timeout,
|
|
164
|
+
context={"pubkey_prefix_length": pubkey_prefix_length}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
async def req_neighbours_sync(self,
|
|
168
|
+
contact,
|
|
169
|
+
count=255,
|
|
170
|
+
offset=0,
|
|
171
|
+
order_by=0,
|
|
172
|
+
pubkey_prefix_length=4,
|
|
173
|
+
timeout=0,
|
|
174
|
+
min_timeout=0
|
|
175
|
+
):
|
|
176
|
+
async with self._mesh_request_lock:
|
|
177
|
+
res = await self.req_neighbours_async(contact,
|
|
178
|
+
count=count,
|
|
179
|
+
offset=offset,
|
|
180
|
+
order_by=order_by,
|
|
181
|
+
pubkey_prefix_length=pubkey_prefix_length,
|
|
182
|
+
timeout=timeout,
|
|
183
|
+
min_timeout=min_timeout)
|
|
184
|
+
|
|
185
|
+
if res is None or res.type == EventType.ERROR:
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
189
|
+
timeout = timeout if min_timeout < timeout else min_timeout
|
|
190
|
+
|
|
191
|
+
if self.dispatcher is None:
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
neighbours_event = await self.dispatcher.wait_for_event(
|
|
195
|
+
EventType.NEIGHBOURS_RESPONSE,
|
|
196
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
197
|
+
timeout=timeout,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return neighbours_event.payload if neighbours_event else None
|
|
201
|
+
|
|
202
|
+
# do several queries if not all neighbours have been obtained
|
|
203
|
+
async def fetch_all_neighbours(self,
|
|
204
|
+
contact,
|
|
205
|
+
order_by=0,
|
|
206
|
+
pubkey_prefix_length=4,
|
|
207
|
+
timeout=0,
|
|
208
|
+
min_timeout=0
|
|
209
|
+
):
|
|
210
|
+
|
|
211
|
+
# Initial request
|
|
212
|
+
res = await self.req_neighbours_sync(contact,
|
|
213
|
+
count=255,
|
|
214
|
+
offset=0,
|
|
215
|
+
order_by=order_by,
|
|
216
|
+
pubkey_prefix_length=pubkey_prefix_length,
|
|
217
|
+
timeout=timeout,
|
|
218
|
+
min_timeout=min_timeout)
|
|
219
|
+
|
|
220
|
+
if res is None:
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
neighbours_count = res["neighbours_count"] # total neighbours
|
|
224
|
+
results_count = res["results_count"] # obtained neighbours
|
|
225
|
+
|
|
226
|
+
del res["tag"]
|
|
227
|
+
|
|
228
|
+
tries = 0
|
|
229
|
+
while results_count < neighbours_count:
|
|
230
|
+
#await asyncio.sleep(2) # wait 2s before next fetch
|
|
231
|
+
next_res = await self.req_neighbours_sync(contact,
|
|
232
|
+
count=255,
|
|
233
|
+
offset=results_count,
|
|
234
|
+
order_by=order_by,
|
|
235
|
+
pubkey_prefix_length=pubkey_prefix_length,
|
|
236
|
+
timeout=timeout,
|
|
237
|
+
min_timeout=min_timeout+25) # requests are close and responses big,
|
|
238
|
+
#so let's have some more timeout for duty cycle
|
|
239
|
+
if next_res is None :
|
|
240
|
+
if tries > 0:
|
|
241
|
+
return res # caller should check it has everything
|
|
242
|
+
else:
|
|
243
|
+
tries += 1
|
|
244
|
+
else:
|
|
245
|
+
tries = 0
|
|
246
|
+
results_count += next_res["results_count"]
|
|
247
|
+
res["results_count"] = results_count
|
|
248
|
+
res["neighbours"] += next_res["neighbours"]
|
|
249
|
+
|
|
250
|
+
return res
|
|
251
|
+
|
|
252
|
+
async def req_regions_async(self, contact, timeout=0, min_timeout=0):
|
|
253
|
+
req = b"\0" # The return path, I currently do nothing with, so direct only
|
|
254
|
+
return await self.send_anon_req(
|
|
255
|
+
contact,
|
|
256
|
+
AnonReqType.REGIONS,
|
|
257
|
+
data=req,
|
|
258
|
+
timeout=timeout
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
async def req_regions_sync(self, contact, timeout=0, min_timeout=0):
|
|
262
|
+
async with self._mesh_request_lock:
|
|
263
|
+
res = await self.req_regions_async(contact, timeout, min_timeout)
|
|
264
|
+
|
|
265
|
+
if res.type == EventType.ERROR:
|
|
266
|
+
return None
|
|
267
|
+
|
|
268
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
269
|
+
timeout = timeout if timeout > min_timeout else min_timeout
|
|
270
|
+
|
|
271
|
+
if self.dispatcher is None:
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
region_event = await self.dispatcher.wait_for_event(
|
|
275
|
+
EventType.BINARY_RESPONSE,
|
|
276
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
277
|
+
timeout=timeout,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if region_event is None:
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
pkt = bytes().fromhex(region_event.payload["data"])
|
|
284
|
+
pbuf = io.BytesIO(pkt)
|
|
285
|
+
tag_again = pbuf.read(4)
|
|
286
|
+
return pbuf.read().decode("utf-8", "ignore").strip("\x00")
|
|
287
|
+
|
|
288
|
+
async def req_owner_async(self, contact, timeout=0, min_timeout=0):
|
|
289
|
+
req = b"\0"
|
|
290
|
+
return await self.send_anon_req(
|
|
291
|
+
contact,
|
|
292
|
+
AnonReqType.OWNER,
|
|
293
|
+
data=req,
|
|
294
|
+
timeout=timeout
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
async def req_owner_sync(self, contact, timeout=0, min_timeout=0):
|
|
298
|
+
async with self._mesh_request_lock:
|
|
299
|
+
res = await self.req_owner_async(contact, timeout, min_timeout)
|
|
300
|
+
|
|
301
|
+
if res.type == EventType.ERROR:
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
305
|
+
timeout = timeout if timeout > min_timeout else min_timeout
|
|
306
|
+
|
|
307
|
+
if self.dispatcher is None:
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
owner_event = await self.dispatcher.wait_for_event(
|
|
311
|
+
EventType.BINARY_RESPONSE,
|
|
312
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
313
|
+
timeout=timeout,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if owner_event is None:
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
pkt = bytes().fromhex(owner_event.payload["data"])
|
|
320
|
+
pbuf = io.BytesIO(pkt)
|
|
321
|
+
tag_again = pbuf.read(4)
|
|
322
|
+
strings = pbuf.read().decode("utf-8", "ignore").split("\n", 1)
|
|
323
|
+
|
|
324
|
+
return dict(name=strings[0], owner=strings[1].strip("\x00"))
|
|
325
|
+
|
|
326
|
+
async def req_basic_async(self, contact, timeout=0, min_timeout=0):
|
|
327
|
+
req = b"\0"
|
|
328
|
+
return await self.send_anon_req(
|
|
329
|
+
contact,
|
|
330
|
+
AnonReqType.BASIC,
|
|
331
|
+
data=req,
|
|
332
|
+
timeout=timeout
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
async def req_basic_sync(self, contact, timeout=0, min_timeout=0):
|
|
336
|
+
async with self._mesh_request_lock:
|
|
337
|
+
res = await self.req_basic_async(contact, timeout, min_timeout)
|
|
338
|
+
|
|
339
|
+
if res.type == EventType.ERROR:
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
343
|
+
timeout = timeout if timeout > min_timeout else min_timeout
|
|
344
|
+
|
|
345
|
+
if self.dispatcher is None:
|
|
346
|
+
return None
|
|
347
|
+
|
|
348
|
+
basic_event = await self.dispatcher.wait_for_event(
|
|
349
|
+
EventType.BINARY_RESPONSE,
|
|
350
|
+
attribute_filters={"tag": res.payload["expected_ack"].hex()},
|
|
351
|
+
timeout=timeout,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
if basic_event is None:
|
|
355
|
+
return None
|
|
356
|
+
|
|
357
|
+
return basic_event.payload
|
|
@@ -24,18 +24,37 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
24
24
|
timeout,
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
-
async def
|
|
27
|
+
async def _send_login_raw(self, dst: DestinationType, pwd: str) -> Event:
|
|
28
28
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
29
29
|
logger.debug(f"Sending login request to: {dst_bytes.hex()}")
|
|
30
30
|
data = b"\x1a" + dst_bytes + pwd.encode("utf-8")
|
|
31
31
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
|
32
32
|
|
|
33
|
+
async def send_login(self, dst: DestinationType, pwd: str) -> Event:
|
|
34
|
+
logger.warning("*** please consider using send_login_sync instead of send_login")
|
|
35
|
+
return await self._send_login_raw(dst, pwd)
|
|
36
|
+
|
|
37
|
+
async def send_login_sync(self, dst: DestinationType, pwd: str, timeout=0, min_timeout=0) -> Optional[Event]:
|
|
38
|
+
"""Send login to a remote node and wait for the response."""
|
|
39
|
+
async with self._mesh_request_lock:
|
|
40
|
+
result = await self._send_login_raw(dst, pwd)
|
|
41
|
+
if result is None or result.type == EventType.ERROR:
|
|
42
|
+
return None
|
|
43
|
+
timeout = result.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
44
|
+
timeout = timeout if timeout > min_timeout else min_timeout
|
|
45
|
+
login_event = await self.dispatcher.wait_for_event(
|
|
46
|
+
EventType.LOGIN_SUCCESS,
|
|
47
|
+
timeout=timeout,
|
|
48
|
+
)
|
|
49
|
+
return login_event
|
|
50
|
+
|
|
33
51
|
async def send_logout(self, dst: DestinationType) -> Event:
|
|
34
52
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
35
53
|
data = b"\x1d" + dst_bytes
|
|
36
54
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
37
55
|
|
|
38
56
|
async def send_statusreq(self, dst: DestinationType) -> Event:
|
|
57
|
+
logger.warning("*** please consider using req_status_sync instead of send_statusreq")
|
|
39
58
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
40
59
|
logger.debug(f"Sending status request to: {dst_bytes.hex()}")
|
|
41
60
|
data = b"\x1b" + dst_bytes
|
|
@@ -85,7 +104,7 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
85
104
|
self, dst: DestinationType, msg: str, timestamp: Optional[int] = None,
|
|
86
105
|
max_attempts=3, max_flood_attempts=2, flood_after=2, timeout=0, min_timeout=0
|
|
87
106
|
) -> Event:
|
|
88
|
-
|
|
107
|
+
|
|
89
108
|
try:
|
|
90
109
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
91
110
|
# with 32 bytes we can reset to flood
|
|
@@ -105,8 +124,8 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
105
124
|
# we can't know if we're flood without fetching all contacts
|
|
106
125
|
# if we have a full key (meaning we can reset path) consider direct
|
|
107
126
|
# else consider flood
|
|
108
|
-
flood = len(dst_bytes) < 32
|
|
109
|
-
logger.info(f"send_msg_with_retry: can't determine if flood, assume
|
|
127
|
+
flood = len(dst_bytes) < 32
|
|
128
|
+
logger.info(f"send_msg_with_retry: can't determine if flood, assume {flood}")
|
|
110
129
|
res = None
|
|
111
130
|
while attempts < max_attempts and res is None \
|
|
112
131
|
and (not flood or flood_attempts < max_flood_attempts):
|
|
@@ -122,8 +141,8 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
122
141
|
contact["out_path_len"] = -1
|
|
123
142
|
|
|
124
143
|
if attempts > 0:
|
|
125
|
-
logger.info(f"Retry sending msg:
|
|
126
|
-
|
|
144
|
+
logger.info(f"Retry sending msg: {attempts + 1}")
|
|
145
|
+
|
|
127
146
|
result = await self.send_msg(dst, msg, timestamp, attempt=attempts)
|
|
128
147
|
if result.type == EventType.ERROR:
|
|
129
148
|
logger.error(f"⚠️ Failed to send message: {result.payload}")
|
|
@@ -131,14 +150,14 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
131
150
|
exp_ack = result.payload["expected_ack"].hex()
|
|
132
151
|
timeout = result.payload["suggested_timeout"] / 1000 * 1.2 if timeout==0 else timeout
|
|
133
152
|
timeout = timeout if timeout > min_timeout else min_timeout
|
|
134
|
-
res = await self.dispatcher.wait_for_event(EventType.ACK,
|
|
135
|
-
attribute_filters={"code": exp_ack},
|
|
153
|
+
res = await self.dispatcher.wait_for_event(EventType.ACK,
|
|
154
|
+
attribute_filters={"code": exp_ack},
|
|
136
155
|
timeout=timeout)
|
|
137
156
|
|
|
138
157
|
attempts = attempts + 1
|
|
139
158
|
if flood :
|
|
140
159
|
flood_attempts = flood_attempts + 1
|
|
141
|
-
|
|
160
|
+
|
|
142
161
|
return None if res is None else result
|
|
143
162
|
|
|
144
163
|
async def send_chan_msg(self, chan: int, msg: str, timestamp: Optional[int|bytes] = None) -> Event:
|
|
@@ -166,17 +185,36 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
166
185
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
167
186
|
|
|
168
187
|
async def send_telemetry_req(self, dst: DestinationType) -> Event:
|
|
188
|
+
logger.warning("*** please consider using req_telemetry_sync instead of send_telemetry_req")
|
|
169
189
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
170
190
|
logger.debug(f"Asking telemetry to {dst_bytes.hex()}")
|
|
171
191
|
data = b"\x27\x00\x00\x00" + dst_bytes
|
|
172
192
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
|
173
193
|
|
|
174
|
-
async def
|
|
194
|
+
async def _send_path_discovery_raw(self, dst: DestinationType) -> Event:
|
|
175
195
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
176
196
|
logger.debug(f"Path discovery request for {dst_bytes.hex()}")
|
|
177
197
|
data = b"\x34\x00" + dst_bytes
|
|
178
198
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
|
179
199
|
|
|
200
|
+
async def send_path_discovery(self, dst: DestinationType) -> Event:
|
|
201
|
+
logger.warning("*** please consider using send_path_discovery_sync instead of send_path_discovery")
|
|
202
|
+
return await self._send_path_discovery_raw(dst)
|
|
203
|
+
|
|
204
|
+
async def send_path_discovery_sync(self, dst: DestinationType, timeout=0, min_timeout=0) -> Optional[Event]:
|
|
205
|
+
"""Send path discovery request and wait for the response."""
|
|
206
|
+
async with self._mesh_request_lock:
|
|
207
|
+
result = await self._send_path_discovery_raw(dst)
|
|
208
|
+
if result is None or result.type == EventType.ERROR:
|
|
209
|
+
return None
|
|
210
|
+
timeout = result.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
|
|
211
|
+
timeout = timeout if timeout > min_timeout else min_timeout
|
|
212
|
+
path_event = await self.dispatcher.wait_for_event(
|
|
213
|
+
EventType.PATH_RESPONSE,
|
|
214
|
+
timeout=timeout,
|
|
215
|
+
)
|
|
216
|
+
return path_event
|
|
217
|
+
|
|
180
218
|
async def send_trace(
|
|
181
219
|
self,
|
|
182
220
|
auth_code: int = 0,
|
|
@@ -108,17 +108,14 @@ class MeshcorePacketParser:
|
|
|
108
108
|
channel = c
|
|
109
109
|
break
|
|
110
110
|
|
|
111
|
-
chan_name = ""
|
|
112
|
-
|
|
113
|
-
if channel is None :
|
|
114
|
-
chan_name = chan_hash
|
|
115
|
-
else:
|
|
116
|
-
chan_name = channel["channel_name"]
|
|
117
|
-
|
|
118
111
|
log_data["chan_hash"] = chan_hash
|
|
119
112
|
log_data["cipher_mac"] = cipher_mac.hex()
|
|
120
113
|
log_data["crypted"] = msg.hex()
|
|
121
|
-
|
|
114
|
+
|
|
115
|
+
chan_name = ""
|
|
116
|
+
if not channel is None :
|
|
117
|
+
chan_name = channel["channel_name"]
|
|
118
|
+
log_data["chan_name"] = chan_name
|
|
122
119
|
|
|
123
120
|
if not channel is None and self.decrypt_channels:
|
|
124
121
|
|