meshcore 2.1.24__py3-none-any.whl → 2.2.3__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.
- meshcore/commands/base.py +4 -0
- meshcore/commands/contact.py +2 -2
- meshcore/commands/device.py +27 -7
- meshcore/commands/messaging.py +16 -4
- meshcore/events.py +3 -0
- meshcore/packets.py +1 -0
- meshcore/reader.py +87 -1
- {meshcore-2.1.24.dist-info → meshcore-2.2.3.dist-info}/METADATA +1 -1
- meshcore-2.2.3.dist-info/RECORD +22 -0
- {meshcore-2.1.24.dist-info → meshcore-2.2.3.dist-info}/WHEEL +1 -1
- meshcore/commands/.binary.py.swp +0 -0
- meshcore-2.1.24.dist-info/RECORD +0 -23
- {meshcore-2.1.24.dist-info → meshcore-2.2.3.dist-info}/licenses/LICENSE +0 -0
meshcore/commands/base.py
CHANGED
|
@@ -32,10 +32,14 @@ def _validate_destination(dst: DestinationType, prefix_length: int = 6) -> bytes
|
|
|
32
32
|
"""
|
|
33
33
|
if isinstance(dst, bytes):
|
|
34
34
|
# Already bytes, use directly
|
|
35
|
+
if len(dst)<prefix_length:
|
|
36
|
+
raise ValueError(f"Invalid prefix len, expecting {prefix_length}, got {len(dst)}")
|
|
35
37
|
return dst[:prefix_length]
|
|
36
38
|
elif isinstance(dst, str):
|
|
37
39
|
# Hex string, convert to bytes
|
|
38
40
|
try:
|
|
41
|
+
if len(dst)<2*prefix_length:
|
|
42
|
+
raise ValueError(f"Invalid prefix len, expecting {prefix_length}, got {len(dst)/2}")
|
|
39
43
|
return bytes.fromhex(dst)[:prefix_length]
|
|
40
44
|
except ValueError:
|
|
41
45
|
raise ValueError(f"Invalid public key hex string: {dst}")
|
meshcore/commands/contact.py
CHANGED
|
@@ -124,8 +124,8 @@ class ContactCommands(CommandHandlerBase):
|
|
|
124
124
|
data = (
|
|
125
125
|
b"\x09"
|
|
126
126
|
+ bytes.fromhex(contact["public_key"])
|
|
127
|
-
+ contact["type"].to_bytes(1)
|
|
128
|
-
+ flags.to_bytes(1)
|
|
127
|
+
+ contact["type"].to_bytes(1, "little")
|
|
128
|
+
+ flags.to_bytes(1, "little")
|
|
129
129
|
+ out_path_len.to_bytes(1, "little", signed=True)
|
|
130
130
|
+ bytes.fromhex(out_path_hex)
|
|
131
131
|
+ bytes.fromhex(adv_name_hex)
|
meshcore/commands/device.py
CHANGED
|
@@ -106,9 +106,9 @@ class DeviceCommands(CommandHandlerBase):
|
|
|
106
106
|
)
|
|
107
107
|
data = (
|
|
108
108
|
b"\x26"
|
|
109
|
-
+ manual_add_contacts.to_bytes(1)
|
|
110
|
-
+ telemetry_mode.to_bytes(1)
|
|
111
|
-
+ advert_loc_policy.to_bytes(1)
|
|
109
|
+
+ manual_add_contacts.to_bytes(1, "little")
|
|
110
|
+
+ telemetry_mode.to_bytes(1, "little")
|
|
111
|
+
+ advert_loc_policy.to_bytes(1, "little")
|
|
112
112
|
)
|
|
113
113
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
114
114
|
|
|
@@ -120,10 +120,10 @@ class DeviceCommands(CommandHandlerBase):
|
|
|
120
120
|
)
|
|
121
121
|
data = (
|
|
122
122
|
b"\x26"
|
|
123
|
-
+ infos["manual_add_contacts"].to_bytes(1)
|
|
124
|
-
+ telemetry_mode.to_bytes(1)
|
|
125
|
-
+ infos["adv_loc_policy"].to_bytes(1)
|
|
126
|
-
+ infos["multi_acks"].to_bytes(1)
|
|
123
|
+
+ infos["manual_add_contacts"].to_bytes(1, "little")
|
|
124
|
+
+ telemetry_mode.to_bytes(1, "little")
|
|
125
|
+
+ infos["adv_loc_policy"].to_bytes(1, "little")
|
|
126
|
+
+ infos["multi_acks"].to_bytes(1, "little")
|
|
127
127
|
)
|
|
128
128
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
129
129
|
|
|
@@ -205,3 +205,23 @@ class DeviceCommands(CommandHandlerBase):
|
|
|
205
205
|
async def export_private_key(self) -> Event:
|
|
206
206
|
logger.debug("Requesting private key export")
|
|
207
207
|
return await self.send(b"\x17", [EventType.PRIVATE_KEY, EventType.DISABLED, EventType.ERROR])
|
|
208
|
+
|
|
209
|
+
async def import_private_key(self, key) -> Event:
|
|
210
|
+
logger.debug("Requesting private key import")
|
|
211
|
+
data = b"\x18" + key
|
|
212
|
+
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
213
|
+
|
|
214
|
+
async def get_stats_core(self) -> Event:
|
|
215
|
+
logger.debug("Getting core statistics")
|
|
216
|
+
# CMD_GET_STATS (56) + STATS_TYPE_CORE (0)
|
|
217
|
+
return await self.send(b"\x38\x00", [EventType.STATS_CORE, EventType.ERROR])
|
|
218
|
+
|
|
219
|
+
async def get_stats_radio(self) -> Event:
|
|
220
|
+
logger.debug("Getting radio statistics")
|
|
221
|
+
# CMD_GET_STATS (56) + STATS_TYPE_RADIO (1)
|
|
222
|
+
return await self.send(b"\x38\x01", [EventType.STATS_RADIO, EventType.ERROR])
|
|
223
|
+
|
|
224
|
+
async def get_stats_packets(self) -> Event:
|
|
225
|
+
logger.debug("Getting packet statistics")
|
|
226
|
+
# CMD_GET_STATS (56) + STATS_TYPE_PACKETS (2)
|
|
227
|
+
return await self.send(b"\x38\x02", [EventType.STATS_PACKETS, EventType.ERROR])
|
meshcore/commands/messaging.py
CHANGED
|
@@ -86,21 +86,33 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
86
86
|
max_attempts=3, max_flood_attempts=2, flood_after=2, timeout=0, min_timeout=0
|
|
87
87
|
) -> Event:
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
try:
|
|
90
|
+
dst_bytes = _validate_destination(dst, prefix_length=32)
|
|
91
|
+
# with 32 bytes we can reset to flood
|
|
92
|
+
except ValueError:
|
|
93
|
+
# but if we can't, we'll assume we're flood
|
|
94
|
+
dst_bytes = _validate_destination(dst, prefix_length=6)
|
|
90
95
|
contact = self._get_contact_by_prefix(dst_bytes.hex())
|
|
91
96
|
|
|
92
97
|
attempts = 0
|
|
93
98
|
flood_attempts = 0
|
|
94
99
|
if not contact is None :
|
|
95
100
|
flood = contact["out_path_len"] == -1
|
|
101
|
+
if len(dst_bytes) < 32:
|
|
102
|
+
# if we have a contact, then we can get a 32 bytes key !
|
|
103
|
+
dst_bytes = _validate_destination(contact, prefix_length=32)
|
|
96
104
|
else:
|
|
97
|
-
flood
|
|
105
|
+
# we can't know if we're flood without fetching all contacts
|
|
106
|
+
# if we have a full key (meaning we can reset path) consider direct
|
|
107
|
+
# else consider flood
|
|
108
|
+
flood = len(dst_bytes) < 32
|
|
109
|
+
logger.info(f"send_msg_with_retry: can't determine if flood, assume {flood}")
|
|
98
110
|
res = None
|
|
99
111
|
while attempts < max_attempts and res is None \
|
|
100
112
|
and (not flood or flood_attempts < max_flood_attempts):
|
|
101
|
-
if attempts == flood_after : # change path to flood
|
|
113
|
+
if attempts == flood_after and not flood: # change path to flood
|
|
102
114
|
logger.info("Resetting path")
|
|
103
|
-
rp_res = await self.reset_path(
|
|
115
|
+
rp_res = await self.reset_path(dst_bytes)
|
|
104
116
|
if rp_res.type == EventType.ERROR:
|
|
105
117
|
logger.error(f"Couldn't reset path {rp_res} continuing ...")
|
|
106
118
|
else:
|
meshcore/events.py
CHANGED
|
@@ -40,6 +40,9 @@ class EventType(Enum):
|
|
|
40
40
|
MMA_RESPONSE = "mma_response"
|
|
41
41
|
ACL_RESPONSE = "acl_response"
|
|
42
42
|
CUSTOM_VARS = "custom_vars"
|
|
43
|
+
STATS_CORE = "stats_core"
|
|
44
|
+
STATS_RADIO = "stats_radio"
|
|
45
|
+
STATS_PACKETS = "stats_packets"
|
|
43
46
|
CHANNEL_INFO = "channel_info"
|
|
44
47
|
PATH_RESPONSE = "path_response"
|
|
45
48
|
PRIVATE_KEY = "private_key"
|
meshcore/packets.py
CHANGED
meshcore/reader.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import json
|
|
3
|
+
import struct
|
|
3
4
|
import time
|
|
4
5
|
import io
|
|
5
6
|
from typing import Any, Dict
|
|
@@ -51,7 +52,11 @@ class MessageReader:
|
|
|
51
52
|
|
|
52
53
|
async def handle_rx(self, data: bytearray):
|
|
53
54
|
dbuf = io.BytesIO(data)
|
|
54
|
-
|
|
55
|
+
try:
|
|
56
|
+
packet_type_value = dbuf.read(1)[0]
|
|
57
|
+
except IndexError as e:
|
|
58
|
+
logger.warning(f"Received empty packet: {e}")
|
|
59
|
+
return
|
|
55
60
|
logger.debug(f"Received data: {data.hex()}")
|
|
56
61
|
|
|
57
62
|
# Handle command responses
|
|
@@ -286,6 +291,87 @@ class MessageReader:
|
|
|
286
291
|
logger.debug(f"got custom vars : {res}")
|
|
287
292
|
await self.dispatcher.dispatch(Event(EventType.CUSTOM_VARS, res))
|
|
288
293
|
|
|
294
|
+
elif packet_type_value == PacketType.STATS.value: # RESP_CODE_STATS (24)
|
|
295
|
+
logger.debug(f"received stats response: {data.hex()}")
|
|
296
|
+
# RESP_CODE_STATS: All stats responses use code 24 with sub-type byte
|
|
297
|
+
# Byte 0: response_code (24), Byte 1: stats_type (0=core, 1=radio, 2=packets)
|
|
298
|
+
if len(data) < 2:
|
|
299
|
+
logger.error(f"Stats response too short: {len(data)} bytes, need at least 2 for header")
|
|
300
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
stats_type = data[1]
|
|
304
|
+
|
|
305
|
+
if stats_type == 0: # STATS_TYPE_CORE
|
|
306
|
+
# RESP_CODE_STATS + STATS_TYPE_CORE: 11 bytes total
|
|
307
|
+
# Format: <B B H I H B (response_code, stats_type, battery_mv, uptime_secs, errors, queue_len)
|
|
308
|
+
if len(data) < 11:
|
|
309
|
+
logger.error(f"Stats core response too short: {len(data)} bytes, expected 11")
|
|
310
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
|
311
|
+
else:
|
|
312
|
+
try:
|
|
313
|
+
battery_mv, uptime_secs, errors, queue_len = struct.unpack('<H I H B', data[2:11])
|
|
314
|
+
res = {
|
|
315
|
+
'battery_mv': battery_mv,
|
|
316
|
+
'uptime_secs': uptime_secs,
|
|
317
|
+
'errors': errors,
|
|
318
|
+
'queue_len': queue_len
|
|
319
|
+
}
|
|
320
|
+
logger.debug(f"parsed stats core: {res}")
|
|
321
|
+
await self.dispatcher.dispatch(Event(EventType.STATS_CORE, res))
|
|
322
|
+
except struct.error as e:
|
|
323
|
+
logger.error(f"Error parsing stats core binary frame: {e}, data: {data.hex()}")
|
|
324
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"binary_parse_error: {e}"}))
|
|
325
|
+
|
|
326
|
+
elif stats_type == 1: # STATS_TYPE_RADIO
|
|
327
|
+
# RESP_CODE_STATS + STATS_TYPE_RADIO: 14 bytes total
|
|
328
|
+
# Format: <B B h b b I I (response_code, stats_type, noise_floor, last_rssi, last_snr, tx_air_secs, rx_air_secs)
|
|
329
|
+
if len(data) < 14:
|
|
330
|
+
logger.error(f"Stats radio response too short: {len(data)} bytes, expected 14")
|
|
331
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
|
332
|
+
else:
|
|
333
|
+
try:
|
|
334
|
+
noise_floor, last_rssi, last_snr_scaled, tx_air_secs, rx_air_secs = struct.unpack('<h b b I I', data[2:14])
|
|
335
|
+
res = {
|
|
336
|
+
'noise_floor': noise_floor,
|
|
337
|
+
'last_rssi': last_rssi,
|
|
338
|
+
'last_snr': last_snr_scaled / 4.0, # Unscale SNR (was multiplied by 4)
|
|
339
|
+
'tx_air_secs': tx_air_secs,
|
|
340
|
+
'rx_air_secs': rx_air_secs
|
|
341
|
+
}
|
|
342
|
+
logger.debug(f"parsed stats radio: {res}")
|
|
343
|
+
await self.dispatcher.dispatch(Event(EventType.STATS_RADIO, res))
|
|
344
|
+
except struct.error as e:
|
|
345
|
+
logger.error(f"Error parsing stats radio binary frame: {e}, data: {data.hex()}")
|
|
346
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"binary_parse_error: {e}"}))
|
|
347
|
+
|
|
348
|
+
elif stats_type == 2: # STATS_TYPE_PACKETS
|
|
349
|
+
# RESP_CODE_STATS + STATS_TYPE_PACKETS: 26 bytes total
|
|
350
|
+
# Format: <B B I I I I I I (response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx)
|
|
351
|
+
if len(data) < 26:
|
|
352
|
+
logger.error(f"Stats packets response too short: {len(data)} bytes, expected 26")
|
|
353
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
|
354
|
+
else:
|
|
355
|
+
try:
|
|
356
|
+
recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = struct.unpack('<I I I I I I', data[2:26])
|
|
357
|
+
res = {
|
|
358
|
+
'recv': recv,
|
|
359
|
+
'sent': sent,
|
|
360
|
+
'flood_tx': flood_tx,
|
|
361
|
+
'direct_tx': direct_tx,
|
|
362
|
+
'flood_rx': flood_rx,
|
|
363
|
+
'direct_rx': direct_rx
|
|
364
|
+
}
|
|
365
|
+
logger.debug(f"parsed stats packets: {res}")
|
|
366
|
+
await self.dispatcher.dispatch(Event(EventType.STATS_PACKETS, res))
|
|
367
|
+
except struct.error as e:
|
|
368
|
+
logger.error(f"Error parsing stats packets binary frame: {e}, data: {data.hex()}")
|
|
369
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"binary_parse_error: {e}"}))
|
|
370
|
+
|
|
371
|
+
else:
|
|
372
|
+
logger.error(f"Unknown stats type: {stats_type}, data: {data.hex()}")
|
|
373
|
+
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"unknown_stats_type: {stats_type}"}))
|
|
374
|
+
|
|
289
375
|
elif packet_type_value == PacketType.CHANNEL_INFO.value:
|
|
290
376
|
logger.debug(f"received channel info response: {data.hex()}")
|
|
291
377
|
res = {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.3
|
|
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
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
meshcore/__init__.py,sha256=55PdT8gZ9Lf727s7BdSuCt3lIBlAhRzX28A8i0DHaRc,602
|
|
2
|
+
meshcore/ble_cx.py,sha256=S5uanBI88Mxwcs1zWbdQGVJ6GC4kdWz8-Z_2aQVpeGI,6864
|
|
3
|
+
meshcore/connection_manager.py,sha256=3U0TWuHDvL5FfwEwXRy5HjlCyV3nsWhhrEC7TVxFzZI,5899
|
|
4
|
+
meshcore/events.py,sha256=w1Ug5ugO2hAcMs7cV3WRi_4-hv0Lw_Y7NuZzZv0LPl8,8238
|
|
5
|
+
meshcore/lpp_json_encoder.py,sha256=vyn7z3VYWOo_B9xmCzqblh5xCa0QKWKcMi2eOqw18RE,1875
|
|
6
|
+
meshcore/meshcore.py,sha256=n8fH7AEx7DR8BHOYL_Qex9ns6ACKklNNqYbPnNnvqyE,16110
|
|
7
|
+
meshcore/packets.py,sha256=3irQww-HBAe_O03oHPAdp8Z6pwenjsVX_u3F19fHKyc,1294
|
|
8
|
+
meshcore/parsing.py,sha256=48PQkig-sqvsRlkF9zkvWhJoSq6ERCbGb_aRuCND5NI,4044
|
|
9
|
+
meshcore/reader.py,sha256=YNCP4-ll5JEKW6cvbRMfjFLdG4RtVKt-npEDr8hnpwM,33939
|
|
10
|
+
meshcore/serial_cx.py,sha256=-kaqnqk7Ydufu2DECFfPDv4Xs-7gHUBuz8v0xf8fvvE,3969
|
|
11
|
+
meshcore/tcp_cx.py,sha256=05YRVMnjY5aVBTJcHa0uG4VfFKGbV6hQ1pPIsJg4CDI,4227
|
|
12
|
+
meshcore/commands/__init__.py,sha256=qfGPzrha7pZQYHOXLZyDsK-QVRjiz5zxAEj8kMxO9uU,536
|
|
13
|
+
meshcore/commands/base.py,sha256=yKfKSmdnIh0DhAZ34TtioE38huxXc0QGB5Hm_wbcOqg,7398
|
|
14
|
+
meshcore/commands/binary.py,sha256=MihRjG4IppPYPeu2KMpctcBPac_hdClp6PgGirQfydg,8412
|
|
15
|
+
meshcore/commands/contact.py,sha256=7X4e17M2YxUZsfUhaN18XZG2inTuXknXJOBAoPblydw,5880
|
|
16
|
+
meshcore/commands/control_data.py,sha256=sXp1RoEw6Z0zPr0Nn5XBovEY6r9sePbWQwsbY0iYyXc,1512
|
|
17
|
+
meshcore/commands/device.py,sha256=KFI9bTSaBcQEJewkGqJW3YVgfNv8f6MyIznWwgDoAlQ,9645
|
|
18
|
+
meshcore/commands/messaging.py,sha256=Mglog1xCz_DhKJU1vEv0AD7bBo5_OhEul1MpSY7dnXc,9806
|
|
19
|
+
meshcore-2.2.3.dist-info/METADATA,sha256=-jhRE5JOtTYi1_wzua8IXrzpo5vYnNbAAF0RLg2Uh48,25316
|
|
20
|
+
meshcore-2.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
21
|
+
meshcore-2.2.3.dist-info/licenses/LICENSE,sha256=o62-JWT_C-ZqEtzb1Gl_PPtPr0pVT8KDmgji_Y_bejI,1075
|
|
22
|
+
meshcore-2.2.3.dist-info/RECORD,,
|
meshcore/commands/.binary.py.swp
DELETED
|
Binary file
|
meshcore-2.1.24.dist-info/RECORD
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
meshcore/__init__.py,sha256=55PdT8gZ9Lf727s7BdSuCt3lIBlAhRzX28A8i0DHaRc,602
|
|
2
|
-
meshcore/ble_cx.py,sha256=S5uanBI88Mxwcs1zWbdQGVJ6GC4kdWz8-Z_2aQVpeGI,6864
|
|
3
|
-
meshcore/connection_manager.py,sha256=3U0TWuHDvL5FfwEwXRy5HjlCyV3nsWhhrEC7TVxFzZI,5899
|
|
4
|
-
meshcore/events.py,sha256=P40oq3C9j2cEg6iD9_-SxWK927C3ykWBTpjL8ixygFQ,8140
|
|
5
|
-
meshcore/lpp_json_encoder.py,sha256=vyn7z3VYWOo_B9xmCzqblh5xCa0QKWKcMi2eOqw18RE,1875
|
|
6
|
-
meshcore/meshcore.py,sha256=n8fH7AEx7DR8BHOYL_Qex9ns6ACKklNNqYbPnNnvqyE,16110
|
|
7
|
-
meshcore/packets.py,sha256=FT6GwH8rveLBWuFVMkbqnAuaymrk6uhd_t46Qj98uf4,1279
|
|
8
|
-
meshcore/parsing.py,sha256=48PQkig-sqvsRlkF9zkvWhJoSq6ERCbGb_aRuCND5NI,4044
|
|
9
|
-
meshcore/reader.py,sha256=inhn0Xsc7J0VKKyPVWwoM1LXfAKO-SJ4Th1c3sN4yy8,28645
|
|
10
|
-
meshcore/serial_cx.py,sha256=-kaqnqk7Ydufu2DECFfPDv4Xs-7gHUBuz8v0xf8fvvE,3969
|
|
11
|
-
meshcore/tcp_cx.py,sha256=05YRVMnjY5aVBTJcHa0uG4VfFKGbV6hQ1pPIsJg4CDI,4227
|
|
12
|
-
meshcore/commands/.binary.py.swp,sha256=SXeYOAsKheJnezxgvzUUHL43qUQWEPyc-VZUMBRsGV8,24576
|
|
13
|
-
meshcore/commands/__init__.py,sha256=qfGPzrha7pZQYHOXLZyDsK-QVRjiz5zxAEj8kMxO9uU,536
|
|
14
|
-
meshcore/commands/base.py,sha256=jO6KWSxBciEMaP0weE8OAbN6Tf-kWSaljb9py3gvihg,7126
|
|
15
|
-
meshcore/commands/binary.py,sha256=MihRjG4IppPYPeu2KMpctcBPac_hdClp6PgGirQfydg,8412
|
|
16
|
-
meshcore/commands/contact.py,sha256=-dRY68xZkDFP6-i53WujyWhEsXZsFRk98B3j3zBzaL4,5860
|
|
17
|
-
meshcore/commands/control_data.py,sha256=sXp1RoEw6Z0zPr0Nn5XBovEY6r9sePbWQwsbY0iYyXc,1512
|
|
18
|
-
meshcore/commands/device.py,sha256=V-gxA88We1KLDtzJ-rsmxv5LH04sUOacqnBc6TwB_gg,8662
|
|
19
|
-
meshcore/commands/messaging.py,sha256=5h0JecOT3KJV1AFdasVLs889xtXY4J5gguJ7KbUoZOE,9073
|
|
20
|
-
meshcore-2.1.24.dist-info/METADATA,sha256=9Ks9G4ws_jzHVCUps3JJmpWjWZ7LoKj9pbCpSViIA2c,25317
|
|
21
|
-
meshcore-2.1.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
22
|
-
meshcore-2.1.24.dist-info/licenses/LICENSE,sha256=o62-JWT_C-ZqEtzb1Gl_PPtPr0pVT8KDmgji_Y_bejI,1075
|
|
23
|
-
meshcore-2.1.24.dist-info/RECORD,,
|
|
File without changes
|