meshcore-cli 1.2.14__py3-none-any.whl → 1.3.1__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_cli/meshcore_cli.py +275 -85
- {meshcore_cli-1.2.14.dist-info → meshcore_cli-1.3.1.dist-info}/METADATA +114 -45
- meshcore_cli-1.3.1.dist-info/RECORD +8 -0
- meshcore_cli-1.2.14.dist-info/RECORD +0 -8
- {meshcore_cli-1.2.14.dist-info → meshcore_cli-1.3.1.dist-info}/WHEEL +0 -0
- {meshcore_cli-1.2.14.dist-info → meshcore_cli-1.3.1.dist-info}/entry_points.txt +0 -0
- {meshcore_cli-1.2.14.dist-info → meshcore_cli-1.3.1.dist-info}/licenses/LICENSE +0 -0
meshcore_cli/meshcore_cli.py
CHANGED
|
@@ -32,7 +32,7 @@ import re
|
|
|
32
32
|
from meshcore import MeshCore, EventType, logger
|
|
33
33
|
|
|
34
34
|
# Version
|
|
35
|
-
VERSION = "v1.
|
|
35
|
+
VERSION = "v1.3.1"
|
|
36
36
|
|
|
37
37
|
# default ble address is stored in a config file
|
|
38
38
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -40,6 +40,10 @@ MCCLI_ADDRESS = MCCLI_CONFIG_DIR + "default_address"
|
|
|
40
40
|
MCCLI_HISTORY_FILE = MCCLI_CONFIG_DIR + "history"
|
|
41
41
|
MCCLI_INIT_SCRIPT = MCCLI_CONFIG_DIR + "init"
|
|
42
42
|
|
|
43
|
+
PAYLOAD_TYPENAMES = ["REQ", "RESPONSE", "TEXT_MSG", "ACK", "ADVERT", "GRP_TXT", "GRP_DATA", "ANON_REQ", "PATH", "TRACE", "MULTIPART", "CONTROL"]
|
|
44
|
+
ROUTE_TYPENAMES = ["TC_FLOOD", "FLOOD", "DIRECT", "TC_DIRECT"]
|
|
45
|
+
CONTACT_TYPENAMES = ["NONE","CLI","REP","ROOM","SENS"]
|
|
46
|
+
|
|
43
47
|
# Fallback address if config file not found
|
|
44
48
|
# if None or "" then a scan is performed
|
|
45
49
|
ADDRESS = ""
|
|
@@ -205,27 +209,57 @@ process_event_message.last_node=None
|
|
|
205
209
|
|
|
206
210
|
async def handle_log_rx(event):
|
|
207
211
|
mc = handle_log_rx.mc
|
|
208
|
-
if handle_log_rx.json_log_rx: # json mode ... raw dump
|
|
209
|
-
msg = json.dumps(event.payload)
|
|
210
|
-
if handle_message.above:
|
|
211
|
-
print_above(msg)
|
|
212
|
-
else :
|
|
213
|
-
print(msg)
|
|
214
|
-
return
|
|
215
212
|
|
|
216
213
|
pkt = bytes().fromhex(event.payload["payload"])
|
|
217
214
|
pbuf = io.BytesIO(pkt)
|
|
218
215
|
header = pbuf.read(1)[0]
|
|
216
|
+
route_type = header & 0x03
|
|
217
|
+
payload_type = (header & 0x3c) >> 2
|
|
218
|
+
payload_ver = (header & 0xc0) >> 6
|
|
219
|
+
|
|
220
|
+
transport_code = None
|
|
221
|
+
if route_type == 0x00 or route_type == 0x03: # has transport code
|
|
222
|
+
transport_code = pbuf.read(4) # discard transport code
|
|
223
|
+
|
|
224
|
+
path_len = pbuf.read(1)[0]
|
|
225
|
+
path = pbuf.read(path_len).hex() # Beware of traces where pathes are mixed
|
|
226
|
+
|
|
227
|
+
try :
|
|
228
|
+
route_typename = ROUTE_TYPENAMES[route_type]
|
|
229
|
+
except IndexError:
|
|
230
|
+
logger.debug(f"Unknown route type {route_type}")
|
|
231
|
+
route_typename = "UNK"
|
|
232
|
+
|
|
233
|
+
try :
|
|
234
|
+
payload_typename = PAYLOAD_TYPENAMES[payload_type]
|
|
235
|
+
except IndexError:
|
|
236
|
+
logger.debug(f"Unknown payload type {payload_type}")
|
|
237
|
+
payload_typename = "UNK"
|
|
238
|
+
|
|
239
|
+
pkt_payload = pbuf.read()
|
|
240
|
+
|
|
241
|
+
event.payload["header"] = header
|
|
242
|
+
event.payload["route_type"] = route_type
|
|
243
|
+
event.payload["route_typename"] = route_typename
|
|
244
|
+
event.payload["payload_type"] = payload_type
|
|
245
|
+
event.payload["payload_typename"]= payload_typename
|
|
219
246
|
|
|
220
|
-
|
|
247
|
+
event.payload["payload_ver"] = payload_ver
|
|
248
|
+
|
|
249
|
+
if not transport_code is None:
|
|
250
|
+
event.payload["transport_code"] = transport_code.hex()
|
|
251
|
+
|
|
252
|
+
event.payload["path_len"] = path_len
|
|
253
|
+
event.payload["path"] = path
|
|
254
|
+
|
|
255
|
+
event.payload["pkt_payload"] = pkt_payload.hex()
|
|
256
|
+
|
|
257
|
+
if payload_type == 0x05: # flood msg / channel
|
|
221
258
|
if handle_log_rx.channel_echoes:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
chan_hash = pbuf.read(1).hex()
|
|
227
|
-
cipher_mac = pbuf.read(2)
|
|
228
|
-
msg = pbuf.read() # until the end of buffer
|
|
259
|
+
pk_buf = io.BytesIO(pkt_payload)
|
|
260
|
+
chan_hash = pk_buf.read(1).hex()
|
|
261
|
+
cipher_mac = pk_buf.read(2)
|
|
262
|
+
msg = pk_buf.read() # until the end of buffer
|
|
229
263
|
|
|
230
264
|
channel = None
|
|
231
265
|
for c in await get_channels(mc):
|
|
@@ -258,6 +292,14 @@ async def handle_log_rx(event):
|
|
|
258
292
|
else:
|
|
259
293
|
print(txt)
|
|
260
294
|
|
|
295
|
+
if handle_log_rx.json_log_rx: # json mode ... raw dump
|
|
296
|
+
msg = json.dumps(event.payload)
|
|
297
|
+
if handle_message.above:
|
|
298
|
+
print_above(msg)
|
|
299
|
+
else :
|
|
300
|
+
print(msg)
|
|
301
|
+
|
|
302
|
+
|
|
261
303
|
handle_log_rx.json_log_rx = False
|
|
262
304
|
handle_log_rx.channel_echoes = False
|
|
263
305
|
handle_log_rx.mc = None
|
|
@@ -390,7 +432,7 @@ class MyNestedCompleter(NestedCompleter):
|
|
|
390
432
|
opts = self.options.keys()
|
|
391
433
|
completer = WordCompleter(
|
|
392
434
|
opts, ignore_case=self.ignore_case,
|
|
393
|
-
pattern=re.compile(r"([a-zA-Z0-9_
|
|
435
|
+
pattern=re.compile(r"([a-zA-Z0-9_\\/\#\?]+|[^a-zA-Z0-9_\s\#\?]+)"))
|
|
394
436
|
yield from completer.get_completions(document, complete_event)
|
|
395
437
|
else: # normal behavior for remainder
|
|
396
438
|
yield from super().get_completions(document, complete_event)
|
|
@@ -541,11 +583,21 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
541
583
|
"flood_after":None,
|
|
542
584
|
"custom":None,
|
|
543
585
|
},
|
|
586
|
+
"?get":None,
|
|
587
|
+
"?set":None,
|
|
588
|
+
"?scope":None,
|
|
589
|
+
"?contact_info":None,
|
|
590
|
+
"?apply_to":None,
|
|
591
|
+
"?at":None,
|
|
592
|
+
"?node_discover":None,
|
|
593
|
+
"?nd":None,
|
|
544
594
|
}
|
|
545
595
|
|
|
546
596
|
contact_completion_list = {
|
|
547
597
|
"contact_info": None,
|
|
548
598
|
"contact_name": None,
|
|
599
|
+
"contact_key": None,
|
|
600
|
+
"contact_type": None,
|
|
549
601
|
"contact_lastmod": None,
|
|
550
602
|
"export_contact" : None,
|
|
551
603
|
"share_contact" : None,
|
|
@@ -708,7 +760,7 @@ make_completion_dict.custom_vars = {}
|
|
|
708
760
|
async def interactive_loop(mc, to=None) :
|
|
709
761
|
print("""Interactive mode, most commands from terminal chat should work.
|
|
710
762
|
Use \"to\" to select recipient, use Tab to complete name ...
|
|
711
|
-
|
|
763
|
+
Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
|
|
712
764
|
\"quit\", \"q\", CTRL+D will end interactive mode""")
|
|
713
765
|
|
|
714
766
|
contact = to
|
|
@@ -1061,6 +1113,26 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1061
1113
|
await process_cmds(mc, args)
|
|
1062
1114
|
return True
|
|
1063
1115
|
|
|
1116
|
+
if line.startswith("contact_key") or line.startswith("ck"):
|
|
1117
|
+
print(contact['public_key'],end="")
|
|
1118
|
+
if " " in line:
|
|
1119
|
+
print(" ", end="", flush=True)
|
|
1120
|
+
secline = line.split(" ", 1)[1]
|
|
1121
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1122
|
+
else:
|
|
1123
|
+
print("")
|
|
1124
|
+
return True
|
|
1125
|
+
|
|
1126
|
+
if line.startswith("contact_type") or line.startswith("ct"):
|
|
1127
|
+
print(f"{CONTACT_TYPENAMES[contact['type']]:4}",end="")
|
|
1128
|
+
if " " in line:
|
|
1129
|
+
print(" ", end="", flush=True)
|
|
1130
|
+
secline = line.split(" ", 1)[1]
|
|
1131
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1132
|
+
else:
|
|
1133
|
+
print("")
|
|
1134
|
+
return True
|
|
1135
|
+
|
|
1064
1136
|
if line.startswith("contact_name") or line.startswith("cn"):
|
|
1065
1137
|
print(contact['adv_name'],end="")
|
|
1066
1138
|
if " " in line:
|
|
@@ -1071,6 +1143,44 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1071
1143
|
print("")
|
|
1072
1144
|
return True
|
|
1073
1145
|
|
|
1146
|
+
if line.startswith("path") :
|
|
1147
|
+
if contact['out_path_len'] == -1:
|
|
1148
|
+
print("Flood", end="")
|
|
1149
|
+
elif contact['out_path_len'] == 0:
|
|
1150
|
+
print("0 hop", end="")
|
|
1151
|
+
else:
|
|
1152
|
+
print(contact['out_path'],end="")
|
|
1153
|
+
if " " in line:
|
|
1154
|
+
print(" ", end="", flush=True)
|
|
1155
|
+
secline = line.split(" ", 1)[1]
|
|
1156
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1157
|
+
else:
|
|
1158
|
+
print("")
|
|
1159
|
+
return True
|
|
1160
|
+
|
|
1161
|
+
if line.startswith("sleep") or line.startswith("s"):
|
|
1162
|
+
try:
|
|
1163
|
+
sleeptime = int(line.split(" ",2)[1])
|
|
1164
|
+
cmd_pos = 2
|
|
1165
|
+
except IndexError: # nothing arg after sleep
|
|
1166
|
+
sleeptime = 1
|
|
1167
|
+
cmd_pos = 0
|
|
1168
|
+
except ValueError:
|
|
1169
|
+
sleeptime = 1
|
|
1170
|
+
cmd_pos = 1
|
|
1171
|
+
|
|
1172
|
+
try:
|
|
1173
|
+
if cmd_pos > 0:
|
|
1174
|
+
secline = line.split(" ",cmd_pos)[cmd_pos]
|
|
1175
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1176
|
+
except IndexError:
|
|
1177
|
+
pass
|
|
1178
|
+
|
|
1179
|
+
# will sleep after executed command if there is a command
|
|
1180
|
+
await asyncio.sleep(sleeptime)
|
|
1181
|
+
|
|
1182
|
+
return True
|
|
1183
|
+
|
|
1074
1184
|
if line == "contact_lastmod":
|
|
1075
1185
|
timestamp = contact["lastmod"]
|
|
1076
1186
|
print(f"{contact['adv_name']} updated"
|
|
@@ -1079,20 +1189,24 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1079
1189
|
return True
|
|
1080
1190
|
|
|
1081
1191
|
# commands that take contact as second arg will be sent to recipient
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
line
|
|
1085
|
-
line
|
|
1086
|
-
line
|
|
1087
|
-
line
|
|
1088
|
-
line
|
|
1089
|
-
line
|
|
1090
|
-
line
|
|
1091
|
-
line
|
|
1092
|
-
line
|
|
1093
|
-
line
|
|
1094
|
-
|
|
1192
|
+
# and can be chained ...
|
|
1193
|
+
if line.startswith("sc") or line.startswith("share_contact") or\
|
|
1194
|
+
line.startswith("ec") or line.startswith("export_contact") or\
|
|
1195
|
+
line.startswith("uc") or line.startswith("upload_contact") or\
|
|
1196
|
+
line.startswith("rp") or line.startswith("reset_path") or\
|
|
1197
|
+
line.startswith("dp") or line.startswith("disc_path") or\
|
|
1198
|
+
line.startswith("contact_info") or line.startswith("ci") or\
|
|
1199
|
+
line.startswith("req_status") or line.startswith("rs") or\
|
|
1200
|
+
line.startswith("req_neighbours") or line.startswith("rn") or\
|
|
1201
|
+
line.startswith("req_telemetry") or line.startswith("rt") or\
|
|
1202
|
+
line.startswith("req_acl") or\
|
|
1203
|
+
line.startswith("path") or\
|
|
1204
|
+
line.startswith("logout") :
|
|
1205
|
+
args = [line.split()[0], contact['adv_name']]
|
|
1095
1206
|
await process_cmds(mc, args)
|
|
1207
|
+
if " " in line:
|
|
1208
|
+
secline = line.split(" ", 1)[1]
|
|
1209
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1096
1210
|
return True
|
|
1097
1211
|
|
|
1098
1212
|
# special case for rp that can be chained from cmdline
|
|
@@ -1242,12 +1356,13 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1242
1356
|
|
|
1243
1357
|
return False
|
|
1244
1358
|
|
|
1245
|
-
async def apply_command_to_contacts(mc, contact_filter, line):
|
|
1359
|
+
async def apply_command_to_contacts(mc, contact_filter, line, json_output=False):
|
|
1246
1360
|
upd_before = None
|
|
1247
1361
|
upd_after = None
|
|
1248
1362
|
contact_type = None
|
|
1249
1363
|
min_hops = None
|
|
1250
1364
|
max_hops = None
|
|
1365
|
+
count = 0
|
|
1251
1366
|
|
|
1252
1367
|
await mc.ensure_contacts()
|
|
1253
1368
|
|
|
@@ -1302,6 +1417,9 @@ async def apply_command_to_contacts(mc, contact_filter, line):
|
|
|
1302
1417
|
(upd_after is None or contact["lastmod"] > upd_after) and\
|
|
1303
1418
|
(min_hops is None or contact["out_path_len"] >= min_hops) and\
|
|
1304
1419
|
(max_hops is None or contact["out_path_len"] <= max_hops):
|
|
1420
|
+
|
|
1421
|
+
count = count + 1
|
|
1422
|
+
|
|
1305
1423
|
if await process_contact_chat_line(mc, contact, line):
|
|
1306
1424
|
pass
|
|
1307
1425
|
|
|
@@ -1326,6 +1444,9 @@ async def apply_command_to_contacts(mc, contact_filter, line):
|
|
|
1326
1444
|
else:
|
|
1327
1445
|
logger.error(f"Can't send {line} to {contact['adv_name']}")
|
|
1328
1446
|
|
|
1447
|
+
if not json_output:
|
|
1448
|
+
print(f"> {count} matches in contacts")
|
|
1449
|
+
|
|
1329
1450
|
async def send_cmd (mc, contact, cmd) :
|
|
1330
1451
|
res = await mc.commands.send_cmd(contact, cmd)
|
|
1331
1452
|
if not res is None and not res.type == EventType.ERROR:
|
|
@@ -1716,26 +1837,14 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1716
1837
|
|
|
1717
1838
|
case "apply_to"|"at":
|
|
1718
1839
|
argnum = 2
|
|
1719
|
-
await apply_command_to_contacts(mc, cmds[1], cmds[2])
|
|
1840
|
+
await apply_command_to_contacts(mc, cmds[1], cmds[2], json_output=json_output)
|
|
1720
1841
|
|
|
1721
1842
|
case "set":
|
|
1722
1843
|
argnum = 2
|
|
1723
1844
|
match cmds[1]:
|
|
1724
1845
|
case "help" :
|
|
1725
1846
|
argnum = 1
|
|
1726
|
-
|
|
1727
|
-
pin <pin> : ble pin
|
|
1728
|
-
radio <freq,bw,sf,cr> : radio params
|
|
1729
|
-
tuning <rx_dly,af> : tuning params
|
|
1730
|
-
tx <dbm> : tx power
|
|
1731
|
-
name <name> : node name
|
|
1732
|
-
lat <lat> : latitude
|
|
1733
|
-
lon <lon> : longitude
|
|
1734
|
-
coords <lat,lon> : coordinates
|
|
1735
|
-
print_snr <on/off> : toggle snr display in messages
|
|
1736
|
-
print_adverts <on/off> : display adverts as they come
|
|
1737
|
-
print_new_contacts <on/off> : display new pending contacts when available
|
|
1738
|
-
print_path_updates <on/off> : display path updates as they come""")
|
|
1847
|
+
get_help_for("set")
|
|
1739
1848
|
case "max_flood_attempts":
|
|
1740
1849
|
msg_ack.max_flood_attempts=int(cmds[2])
|
|
1741
1850
|
case "max_attempts":
|
|
@@ -1959,21 +2068,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1959
2068
|
argnum = 1
|
|
1960
2069
|
match cmds[1]:
|
|
1961
2070
|
case "help":
|
|
1962
|
-
|
|
1963
|
-
name : node name
|
|
1964
|
-
bat : battery level in mV
|
|
1965
|
-
fstats : fs statistics
|
|
1966
|
-
coords : adv coordinates
|
|
1967
|
-
lat : latitude
|
|
1968
|
-
lon : longitude
|
|
1969
|
-
radio : radio parameters
|
|
1970
|
-
tx : tx power
|
|
1971
|
-
print_snr : snr display in messages
|
|
1972
|
-
print_adverts : display adverts as they come
|
|
1973
|
-
print_new_contacts : display new pending contacts when available
|
|
1974
|
-
print_path_updates : display path updates as they come
|
|
1975
|
-
custom : all custom variables in json format
|
|
1976
|
-
each custom var can also be get/set directly""")
|
|
2071
|
+
get_help_for("get")
|
|
1977
2072
|
case "max_flood_attempts":
|
|
1978
2073
|
if json_output :
|
|
1979
2074
|
print(json.dumps({"max_flood_attempts" : msg_ack.max_flood_attempts}))
|
|
@@ -2340,6 +2435,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2340
2435
|
else :
|
|
2341
2436
|
color = process_event_message.color
|
|
2342
2437
|
classic = interactive_loop.classic or not color
|
|
2438
|
+
print(" ", end="")
|
|
2343
2439
|
for t in ev.payload["path"]:
|
|
2344
2440
|
if classic :
|
|
2345
2441
|
print("→",end="")
|
|
@@ -2493,18 +2589,14 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2493
2589
|
await mc.ensure_contacts()
|
|
2494
2590
|
print(f"Discovered {len(dn)} nodes:")
|
|
2495
2591
|
for n in dn:
|
|
2496
|
-
|
|
2497
|
-
|
|
2592
|
+
try :
|
|
2593
|
+
name = f"{n['pubkey'][0:2]} {mc.get_contact_by_key_prefix(n['pubkey'])['adv_name']}"
|
|
2594
|
+
except TypeError:
|
|
2498
2595
|
name = n["pubkey"][0:16]
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
type = "REP"
|
|
2504
|
-
elif n['node_type'] == 3:
|
|
2505
|
-
type = "ROOM"
|
|
2506
|
-
elif n['node_type'] == 4:
|
|
2507
|
-
type = "SENS"
|
|
2596
|
+
if n['node_type'] >= len(CONTACT_TYPENAMES):
|
|
2597
|
+
type = f"t:{n['node_type']}"
|
|
2598
|
+
else:
|
|
2599
|
+
type = CONTACT_TYPENAMES[n['node_type']]
|
|
2508
2600
|
|
|
2509
2601
|
print(f" {name:16} {type:>4} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}")
|
|
2510
2602
|
|
|
@@ -2522,7 +2614,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2522
2614
|
else :
|
|
2523
2615
|
print(json.dumps({
|
|
2524
2616
|
"name": contact["adv_name"],
|
|
2525
|
-
"pubkey_pre": contact["public_key"][0:
|
|
2617
|
+
"pubkey_pre": contact["public_key"][0:16],
|
|
2526
2618
|
"lpp": res,
|
|
2527
2619
|
}, indent = 4))
|
|
2528
2620
|
|
|
@@ -2610,15 +2702,30 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2610
2702
|
if json_output:
|
|
2611
2703
|
print(json.dumps(res, indent=4))
|
|
2612
2704
|
else:
|
|
2705
|
+
width = os.get_terminal_size().columns
|
|
2613
2706
|
print(f"Got {res['results_count']} neighbours out of {res['neighbours_count']} from {contact['adv_name']}:")
|
|
2614
2707
|
for n in res['neighbours']:
|
|
2615
2708
|
ct = mc.get_contact_by_key_prefix(n["pubkey"])
|
|
2616
|
-
if ct :
|
|
2709
|
+
if ct and width > 60 :
|
|
2617
2710
|
name = f"[{n['pubkey'][0:8]}] {ct['adv_name']}"
|
|
2711
|
+
name = f"{name:30}"
|
|
2712
|
+
elif ct :
|
|
2713
|
+
name = f"{ct['adv_name']}"
|
|
2714
|
+
name = f"{name:20}"
|
|
2618
2715
|
else:
|
|
2619
2716
|
name = f"[{n['pubkey']}]"
|
|
2620
2717
|
|
|
2621
|
-
|
|
2718
|
+
t_s = n['secs_ago']
|
|
2719
|
+
time_ago = f"{t_s}s"
|
|
2720
|
+
if t_s / 86400 >= 1 : # result in days
|
|
2721
|
+
time_ago = f"{int(t_s/86400)}d ago{f' ({time_ago})' if width > 62 else ''}"
|
|
2722
|
+
elif t_s / 3600 >= 1 : # result in days
|
|
2723
|
+
time_ago = f"{int(t_s/3600)}h ago{f' ({time_ago})' if width > 62 else ''}"
|
|
2724
|
+
elif t_s / 60 >= 1 : # result in min
|
|
2725
|
+
time_ago = f"{int(t_s/60)}m ago{f' ({time_ago})' if width > 62 else ''}"
|
|
2726
|
+
|
|
2727
|
+
|
|
2728
|
+
print(f" {name} {time_ago}, {n['snr']}dB{' SNR' if width > 66 else ''}")
|
|
2622
2729
|
|
|
2623
2730
|
case "req_binary" :
|
|
2624
2731
|
argnum = 2
|
|
@@ -2641,7 +2748,13 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2641
2748
|
print(json.dumps(res, indent=4))
|
|
2642
2749
|
else :
|
|
2643
2750
|
for c in res.items():
|
|
2644
|
-
|
|
2751
|
+
if c[1]['out_path_len'] == -1:
|
|
2752
|
+
path_str = "Flood"
|
|
2753
|
+
elif c[1]['out_path_len'] == 0:
|
|
2754
|
+
path_str = "0 hop"
|
|
2755
|
+
else:
|
|
2756
|
+
path_str = f"{c[1]['out_path']}"
|
|
2757
|
+
print(f"{c[1]['adv_name']:30} {CONTACT_TYPENAMES[c[1]['type']]:4} {c[1]['public_key'][:12]} {path_str}")
|
|
2645
2758
|
print(f"> {len(mc.contacts)} contacts in device")
|
|
2646
2759
|
|
|
2647
2760
|
case "reload_contacts" | "rc":
|
|
@@ -2709,7 +2822,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2709
2822
|
if (path_len == 0) :
|
|
2710
2823
|
print("0 hop")
|
|
2711
2824
|
elif (path_len == -1) :
|
|
2712
|
-
print("
|
|
2825
|
+
print("Flood")
|
|
2713
2826
|
else:
|
|
2714
2827
|
print(path)
|
|
2715
2828
|
|
|
@@ -2736,6 +2849,8 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2736
2849
|
print(f"Unknown contact {cmds[1]}")
|
|
2737
2850
|
else:
|
|
2738
2851
|
path = cmds[2].replace(",","") # we'll accept path with ,
|
|
2852
|
+
if path == "0":
|
|
2853
|
+
path = ""
|
|
2739
2854
|
try:
|
|
2740
2855
|
res = await mc.commands.change_contact_path(contact, path)
|
|
2741
2856
|
logger.debug(res)
|
|
@@ -3068,8 +3183,8 @@ def command_help():
|
|
|
3068
3183
|
reboot : reboots node
|
|
3069
3184
|
sleep <secs> : sleeps for a given amount of secs s
|
|
3070
3185
|
wait_key : wait until user presses <Enter> wk
|
|
3071
|
-
apply_to <
|
|
3072
|
-
|
|
3186
|
+
apply_to <f> <cmds> : sends cmds to contacts matching f at
|
|
3187
|
+
Messaging
|
|
3073
3188
|
msg <name> <msg> : send message to node by name m {
|
|
3074
3189
|
wait_ack : wait an ack wa }
|
|
3075
3190
|
chan <nb> <msg> : send message to channel number <nb> ch
|
|
@@ -3082,6 +3197,7 @@ def command_help():
|
|
|
3082
3197
|
get_channel <n> : get info for channel (by number or name)
|
|
3083
3198
|
set_channel n nm k : set channel info (nb, name, key)
|
|
3084
3199
|
remove_channel <n> : remove channel (by number or name)
|
|
3200
|
+
scope <s> : sets scope for flood messages
|
|
3085
3201
|
Management
|
|
3086
3202
|
advert : sends advert a
|
|
3087
3203
|
floodadv : flood advert
|
|
@@ -3133,8 +3249,6 @@ def usage () :
|
|
|
3133
3249
|
-D : debug
|
|
3134
3250
|
-S : scan for devices and show a selector
|
|
3135
3251
|
-l : list available ble/serial devices and exit
|
|
3136
|
-
-C : toggles classic mode for prompt
|
|
3137
|
-
-c <on/off> : disables most of color output if off
|
|
3138
3252
|
-T <timeout> : timeout for the ble scan (-S and -l) default 2s
|
|
3139
3253
|
-a <address> : specifies device address (can be a name)
|
|
3140
3254
|
-d <name> : filter meshcore devices with name or address
|
|
@@ -3143,14 +3257,16 @@ def usage () :
|
|
|
3143
3257
|
-p <port> : specifies tcp port (default 5000)
|
|
3144
3258
|
-s <port> : use serial port <port>
|
|
3145
3259
|
-b <baudrate> : specify baudrate
|
|
3260
|
+
-C : toggles classic mode for prompt
|
|
3261
|
+
-c <on/off> : disables most of color output if off
|
|
3146
3262
|
|
|
3147
3263
|
Available Commands and shorcuts (can be chained) :""")
|
|
3148
3264
|
command_help()
|
|
3149
3265
|
|
|
3150
3266
|
def get_help_for (cmdname, context="line") :
|
|
3151
3267
|
if cmdname == "apply_to" or cmdname == "at" :
|
|
3152
|
-
print("""apply_to <
|
|
3153
|
-
|
|
3268
|
+
print("""apply_to <f> <cmd> : applies cmd to contacts matching filter <f>
|
|
3269
|
+
Filter is constructed with comma separated fields :
|
|
3154
3270
|
- u, matches modification time < or > than a timestamp
|
|
3155
3271
|
(can also be days hours or minutes ago if followed by d,h or m)
|
|
3156
3272
|
- t, matches the type (1: client, 2: repeater, 3: room, 4: sensor)
|
|
@@ -3158,7 +3274,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3158
3274
|
- d, direct, similar to h>-1
|
|
3159
3275
|
- f, flood, similar to h<0 or h=-1
|
|
3160
3276
|
|
|
3161
|
-
Note: Some commands like contact_name (aka cn), reset_path (aka rp), forget_password (aka fp) can be chained.
|
|
3277
|
+
Note: Some commands like contact_name (aka cn), contact_key (aka ck), contact_type (aka ct), reset_path (aka rp), forget_password (aka fp) can be chained. There is also a sleep command taking an optional event. The sleep will be issued after the command, it helps limiting rate through repeaters ...
|
|
3162
3278
|
|
|
3163
3279
|
Examples:
|
|
3164
3280
|
# removes all clients that have not been updated in last 2 days
|
|
@@ -3169,7 +3285,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3169
3285
|
at t=2 rp login
|
|
3170
3286
|
""")
|
|
3171
3287
|
|
|
3172
|
-
|
|
3288
|
+
elif cmdname == "node_discover" or cmdname == "nd" :
|
|
3173
3289
|
print("""node_discover <filter> : discovers 0-hop nodes and displays signal info
|
|
3174
3290
|
|
|
3175
3291
|
filter can be "all" for all types or nodes or a comma separated list consisting of :
|
|
@@ -3181,6 +3297,80 @@ def get_help_for (cmdname, context="line") :
|
|
|
3181
3297
|
nd can be used with no filter parameter ... !!! BEWARE WITH CHAINING !!!
|
|
3182
3298
|
""")
|
|
3183
3299
|
|
|
3300
|
+
elif cmdname == "get" :
|
|
3301
|
+
print("""Gets parameters from node
|
|
3302
|
+
Please see also help for set command, which is more up to date ...
|
|
3303
|
+
name : node name
|
|
3304
|
+
bat : battery level in mV
|
|
3305
|
+
fstats : fs statistics
|
|
3306
|
+
coords : adv coordinates
|
|
3307
|
+
lat : latitude
|
|
3308
|
+
lon : longitude
|
|
3309
|
+
radio : radio parameters
|
|
3310
|
+
tx : tx power
|
|
3311
|
+
print_snr : snr display in messages
|
|
3312
|
+
print_adverts : display adverts as they come
|
|
3313
|
+
print_new_contacts : display new pending contacts when available
|
|
3314
|
+
print_path_updates : display path updates as they come
|
|
3315
|
+
custom : all custom variables in json format
|
|
3316
|
+
each custom var can also be get/set directly
|
|
3317
|
+
""")
|
|
3318
|
+
|
|
3319
|
+
elif cmdname == "set" :
|
|
3320
|
+
print("""Available parameters :
|
|
3321
|
+
device:
|
|
3322
|
+
pin <pin> : ble pin
|
|
3323
|
+
radio <freq,bw,sf,cr> : radio params
|
|
3324
|
+
tuning <rx_dly,af> : tuning params
|
|
3325
|
+
tx <dbm> : tx power
|
|
3326
|
+
name <name> : node name
|
|
3327
|
+
lat <lat> : latitude
|
|
3328
|
+
lon <lon> : longitude
|
|
3329
|
+
coords <lat,lon> : coordinates
|
|
3330
|
+
auto_update_contacts <> : automatically updates contact list
|
|
3331
|
+
multi_ack <on/off> : multi-acks feature
|
|
3332
|
+
telemetry_mode_base <mode> : set basic telemetry mode all/selected/off
|
|
3333
|
+
telemetry_mode_loc <mode> : set location telemetry mode all/selected/off
|
|
3334
|
+
telemetry_mode_env <mode> : set env telemetry mode all/selected/off
|
|
3335
|
+
advert_loc_policy <policy> : "share" means loc will be shared in adv
|
|
3336
|
+
display:
|
|
3337
|
+
print_snr <on/off> : toggle snr display in messages
|
|
3338
|
+
print_adverts <on/off> : display adverts as they come
|
|
3339
|
+
print_new_contacts <on/off> : display new pending contacts when available
|
|
3340
|
+
print_path_updates <on/off> : display path updates as they come
|
|
3341
|
+
json_log_rx <on/off> : logs packets incoming to device as json
|
|
3342
|
+
channel_echoes <on/off> : print repeats for channel data
|
|
3343
|
+
echo_unk_channels <on/off> : also dump unk channels (encrypted)
|
|
3344
|
+
color <on/off> : color off should remove ANSI codes from output
|
|
3345
|
+
prompt:
|
|
3346
|
+
classic_prompt <on/off> : activates less fancier prompt
|
|
3347
|
+
arrow_head <string> : change arrow head in prompt
|
|
3348
|
+
slash_start <string> : idem for slash start
|
|
3349
|
+
slash_end <string> : slash end
|
|
3350
|
+
invert_slash <on/off> : apply color inversion to slash
|
|
3351
|
+
""")
|
|
3352
|
+
|
|
3353
|
+
elif cmdname == "scope":
|
|
3354
|
+
print("""scope <scope> : changes flood scope of the node
|
|
3355
|
+
|
|
3356
|
+
The scope command can be used from command line or interactive mode to set the region in which flood packets will be transmitted.
|
|
3357
|
+
|
|
3358
|
+
Managing Flood Scope in interactive mode
|
|
3359
|
+
Flood scope has recently been introduced in meshcore (from v1.10.0). It limits the scope of packets to regions, using transport codes in the frame.
|
|
3360
|
+
When entering chat mode, scope will be reset to *, meaning classic flood.
|
|
3361
|
+
You can switch scope using the scope command, or postfixing the to command with %<scope>.
|
|
3362
|
+
Scope can also be applied to a command using % before the scope name. For instance login%#Morbihan will limit diffusion of the login command (which is usually sent flood to get the path to a repeater) to the #Morbihan region.
|
|
3363
|
+
""")
|
|
3364
|
+
|
|
3365
|
+
elif cmdname == "contact_info":
|
|
3366
|
+
print("""contact_info <ct> : displays contact info
|
|
3367
|
+
|
|
3368
|
+
in interactive mode, there are some lighter commands that can be chained to give more compact information
|
|
3369
|
+
- contact_name (cn)
|
|
3370
|
+
- contact_key (ck)
|
|
3371
|
+
- contact_type (ct)
|
|
3372
|
+
""")
|
|
3373
|
+
|
|
3184
3374
|
else:
|
|
3185
3375
|
print(f"Sorry, no help yet for {cmdname}")
|
|
3186
3376
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: Command line interface to meshcore companion radios
|
|
5
5
|
Project-URL: Homepage, https://github.com/fdlamotte/meshcore-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/fdlamotte/meshcore-cli/issues
|
|
@@ -58,22 +58,25 @@ Init files can also be defined for a given device, meshcore-cli will look for `&
|
|
|
58
58
|
|
|
59
59
|
### Arguments
|
|
60
60
|
|
|
61
|
-
Arguments mostly deals with
|
|
61
|
+
Arguments mostly deals with connection to the node
|
|
62
62
|
|
|
63
63
|
<pre>
|
|
64
64
|
-h : prints this help
|
|
65
65
|
-v : prints version
|
|
66
66
|
-j : json output (disables init file)
|
|
67
67
|
-D : debug
|
|
68
|
-
-S :
|
|
69
|
-
-l : list available ble devices and exit
|
|
70
|
-
-T <timeout
|
|
71
|
-
-a <address
|
|
72
|
-
-d <name
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
68
|
+
-S : scan for devices and show a selector
|
|
69
|
+
-l : list available ble/serial devices and exit
|
|
70
|
+
-T <timeout> : timeout for the ble scan (-S and -l) default 2s
|
|
71
|
+
-a <address> : specifies device address (can be a name)
|
|
72
|
+
-d <name> : filter meshcore devices with name or address
|
|
73
|
+
-P : forces pairing via the OS
|
|
74
|
+
-t <hostname> : connects via tcp/ip
|
|
75
|
+
-p <port> : specifies tcp port (default 5000)
|
|
76
|
+
-s <port> : use serial port <port>
|
|
77
|
+
-b <baudrate> : specify baudrate
|
|
78
|
+
-C : toggles classic mode for prompt
|
|
79
|
+
-c <on/off> : disables most of color output if off
|
|
77
80
|
</pre>
|
|
78
81
|
|
|
79
82
|
### Available Commands
|
|
@@ -81,60 +84,70 @@ Arguments mostly deals with ble connection
|
|
|
81
84
|
Commands are given after arguments, they can be chained and some have shortcuts. Also prefixing a command with a dot `.` will force it to output json instead of synthetic result.
|
|
82
85
|
|
|
83
86
|
<pre>
|
|
87
|
+
?<cmd> may give you some more help about cmd
|
|
84
88
|
General commands
|
|
85
89
|
chat : enter the chat (interactive) mode
|
|
86
|
-
chat_to <ct
|
|
87
|
-
script <filename
|
|
90
|
+
chat_to <ct> : enter chat with contact to
|
|
91
|
+
script <filename> : execute commands in filename
|
|
88
92
|
infos : print informations about the node i
|
|
89
93
|
self_telemetry : print own telemtry t
|
|
90
94
|
card : export this node URI e
|
|
91
95
|
ver : firmware version v
|
|
92
96
|
reboot : reboots node
|
|
93
|
-
sleep <secs
|
|
94
|
-
wait_key : wait until user presses <Enter
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
sleep <secs> : sleeps for a given amount of secs s
|
|
98
|
+
wait_key : wait until user presses <Enter> wk
|
|
99
|
+
apply_to <f> <cmds> : sends cmds to contacts matching f at
|
|
100
|
+
Messaging
|
|
101
|
+
msg <name> <msg> : send message to node by name m {
|
|
97
102
|
wait_ack : wait an ack wa }
|
|
98
|
-
chan <nb
|
|
99
|
-
public <msg
|
|
103
|
+
chan <nb> <msg> : send message to channel number <nb> ch
|
|
104
|
+
public <msg> : send message to public channel (0) dch
|
|
100
105
|
recv : reads next msg r
|
|
101
106
|
wait_msg : wait for a message and read it wm
|
|
102
107
|
sync_msgs : gets all unread msgs from the node sm
|
|
103
108
|
msgs_subscribe : display msgs as they arrive ms
|
|
104
|
-
|
|
109
|
+
get_channels : prints all channel info
|
|
110
|
+
get_channel <n> : get info for channel (by number or name)
|
|
105
111
|
set_channel n nm k : set channel info (nb, name, key)
|
|
112
|
+
remove_channel <n> : remove channel (by number or name)
|
|
113
|
+
scope <s> : sets node's flood scope
|
|
106
114
|
Management
|
|
107
115
|
advert : sends advert a
|
|
108
116
|
floodadv : flood advert
|
|
109
|
-
get <param
|
|
110
|
-
set <param
|
|
111
|
-
time <epoch
|
|
117
|
+
get <param> : gets a param, \"get help\" for more
|
|
118
|
+
set <param> <value> : sets a param, \"set help\" for more
|
|
119
|
+
time <epoch> : sets time to given epoch
|
|
112
120
|
clock : get current time
|
|
113
121
|
clock sync : sync device clock st
|
|
122
|
+
node_discover <filter> : discovers nodes based on their type nd
|
|
114
123
|
Contacts
|
|
115
124
|
contacts / list : gets contact list lc
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
reload_contacts : force reloading all contacts rc
|
|
126
|
+
contact_info <ct> : prints information for contact ct ci
|
|
127
|
+
contact_timeout <ct> v : sets temp default timeout for contact
|
|
128
|
+
share_contact <ct> : share a contact with others sc
|
|
129
|
+
export_contact <ct> : get a contact's URI ec
|
|
130
|
+
import_contact <URI> : import a contact from its URI ic
|
|
131
|
+
remove_contact <ct> : removes a contact from this node
|
|
132
|
+
path <ct> : diplays path for a contact
|
|
133
|
+
disc_path <ct> : discover new path and display dp
|
|
134
|
+
reset_path <ct> : resets path to a contact to flood rp
|
|
135
|
+
change_path <ct> <pth> : change the path to a contact cp
|
|
136
|
+
change_flags <ct> <f> : change contact flags (tel_l|tel_a|star)cf
|
|
137
|
+
req_telemetry <ct> : prints telemetry data as json rt
|
|
138
|
+
req_mma <ct> : requests min/max/avg for a sensor rm
|
|
139
|
+
req_acl <ct> : requests access control list for sensor
|
|
129
140
|
pending_contacts : show pending contacts
|
|
130
|
-
add_pending <
|
|
131
|
-
flush_pending : flush pending contact
|
|
141
|
+
add_pending <pending> : manually add pending contact
|
|
142
|
+
flush_pending : flush pending contact list
|
|
132
143
|
Repeaters
|
|
133
|
-
login <name
|
|
134
|
-
logout <name
|
|
135
|
-
cmd <name
|
|
144
|
+
login <name> <pwd> : log into a node (rep) with given pwd l
|
|
145
|
+
logout <name> : log out of a repeater
|
|
146
|
+
cmd <name> <cmd> : sends a command to a repeater (no ack) c [
|
|
136
147
|
wmt8 : wait for a msg (reply) with a timeout ]
|
|
137
|
-
req_status <name
|
|
148
|
+
req_status <name> : requests status from a node rs
|
|
149
|
+
req_neighbours <name> : requests for neighbours in binary form rn
|
|
150
|
+
trace <path> : run a trace, path is comma separated
|
|
138
151
|
</pre>
|
|
139
152
|
|
|
140
153
|
### Interactive Mode
|
|
@@ -147,14 +160,70 @@ You'll get a prompt with the name of your node. From here you can type meshcore-
|
|
|
147
160
|
|
|
148
161
|
The `to` command is specific to chat mode, it lets you enter the recipient for next command. By default you're on your node but you can enter other nodes or public rooms. Here are some examples :
|
|
149
162
|
|
|
150
|
-
- `to <
|
|
163
|
+
- `to <dest>` : will enter dest (node or channel)
|
|
151
164
|
- `to /`, `to ~` : will go to the root (your node)
|
|
152
165
|
- `to ..` : will go to the last node (it will switch between the two last nodes, this is just a 1-depth history)
|
|
153
166
|
- `to !` : will switch to the node you received last message from
|
|
154
167
|
|
|
155
|
-
When you are
|
|
168
|
+
When you are in a node, the behaviour will depend on the node type, if you're on a chat node, it will send messages by default and you can chat. On a repeater or a room server, it will send commands (autocompletion has been set to comply with the CommonCli class of meshcore). To send a message through a room you'll have to prefix the message with a quote or use the send command.
|
|
156
169
|
|
|
157
|
-
|
|
170
|
+
The `/` character is used to bypass the node you have currently selected using `to`:
|
|
171
|
+
- `/<cmd>` issues cmd command on the root
|
|
172
|
+
- `/<node>/<cmd>` will send cmd to selected node
|
|
173
|
+
- `/<dest> <msg>` will send msg to dest (channel or node)
|
|
174
|
+
|
|
175
|
+
#### Flood Scope in interactive mode
|
|
176
|
+
|
|
177
|
+
Flood scope has recently been introduced in meshcore (from `v1.10.0`). It limits the scope of packets to regions, using transport codes in the frame.
|
|
178
|
+
|
|
179
|
+
When entering chat mode, scope will be reset to `*`, meaning classic flood.
|
|
180
|
+
|
|
181
|
+
You can switch scope using the `scope` command, or postfixing the `to` command with `%<scope>`.
|
|
182
|
+
|
|
183
|
+
Scope can also be applied to a command using `%` before the scope name. For instance `login%#Morbihan` will limit diffusion of the login command (which is usually sent flood to get the path to a repeater) to the `#Morbihan` region.
|
|
184
|
+
|
|
185
|
+
#### Channel echoes
|
|
186
|
+
|
|
187
|
+
It's sometimes interesting to know the path taken by a message received from a channel or which repeaters have repeated a sent message.
|
|
188
|
+
|
|
189
|
+
The app give you the information by listening `rx_log` from the device, when obtained the information is attached to the message and can be read.
|
|
190
|
+
|
|
191
|
+
In meshcore-cli I went lower-level by implementing channel echoes. When activated (with `/set channel_echoes on`), all the channel messages will be printed on the terminal along with the SNR and path taken. When sending a message, you'll have all the repeats from 0-hop repeaters as echoes, and when a message is received, you should see information about the received message, but also all the instances of the same message that might have reached you from another path.
|
|
192
|
+
|
|
193
|
+
In the example below, a msg has been sent between two repeaters, 21 and 25. 25 repeated the message and 21 the repeat and both echoes came back to the node with different SNRs.
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
f1down/#fdl|*> 8
|
|
197
|
+
#fdl f1down: 8 [25] -4.75-112
|
|
198
|
+
#fdl f1down: 8 [2521] 1.00-109
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Issuing batch commands to contacts with apply to
|
|
202
|
+
|
|
203
|
+
`apply_to <f> <cmd>` : applies cmd to contacts matching filter `<f>` it can be used to apply the same command to a pool of repeaters, or remove some contacts matching a condition.
|
|
204
|
+
|
|
205
|
+
Filter is constructed with comma separated fields :
|
|
206
|
+
|
|
207
|
+
- `u`, matches modification time `<` or `>` than a timestamp (can also be days hours or minutes ago if followed by `d`,`h` or `m`)
|
|
208
|
+
- `t`, matches the type (1: client, 2: repeater, 3: room, 4: sensor)
|
|
209
|
+
- `h`, matches number of hops
|
|
210
|
+
- `d`, direct, similar to `h>-1`
|
|
211
|
+
- `f`, flood, similar to `h<0` or `h=-1`
|
|
212
|
+
|
|
213
|
+
Commands should be written as if in interactive mode, if writing from the commandline don't forget to use commas to clearly delimit fields.
|
|
214
|
+
|
|
215
|
+
Note: Some commands like `contact_name` (aka `cn`), `reset_path` (aka `rp`), `forget_password` (aka `fp`) can be chained. There is also a `sleep` command taking an optional time parameter. The sleep will be issued after the command, it helps limiting rate through repeaters ...
|
|
216
|
+
|
|
217
|
+
#### Examples
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
# removes all clients that have not been updated in last 2 days
|
|
221
|
+
at u<2d,t=1 remove_contact
|
|
222
|
+
# gives traces to repeaters that have been updated in the last 24h and are direct
|
|
223
|
+
at t=2,u>1d,d cn trace
|
|
224
|
+
# tries to do flood login to all repeaters
|
|
225
|
+
at t=2 rp login
|
|
226
|
+
```
|
|
158
227
|
|
|
159
228
|
## Examples
|
|
160
229
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
meshcore_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
meshcore_cli/__main__.py,sha256=PfYgibmu2LEtC-OV7L1UgmvV3swJ5rQ4bbXHlwUFlgE,83
|
|
3
|
+
meshcore_cli/meshcore_cli.py,sha256=ZuBGD-H_nXLmHSP3ffat8ZtKNfieKbLmL3TQgv8B4_I,149253
|
|
4
|
+
meshcore_cli-1.3.1.dist-info/METADATA,sha256=k3NMG9h1qPemlm8if4A_7JD7yH6Wi8wzhABH_0ijtJo,15873
|
|
5
|
+
meshcore_cli-1.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
meshcore_cli-1.3.1.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
+
meshcore_cli-1.3.1.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
+
meshcore_cli-1.3.1.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
meshcore_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
meshcore_cli/__main__.py,sha256=PfYgibmu2LEtC-OV7L1UgmvV3swJ5rQ4bbXHlwUFlgE,83
|
|
3
|
-
meshcore_cli/meshcore_cli.py,sha256=NbE3xX0GE3dtq0WnlBdF79iwyeGnxHerFGGoQNLeDSc,141395
|
|
4
|
-
meshcore_cli-1.2.14.dist-info/METADATA,sha256=DDHsEo0JnY32-PX5_tr3qUYagxHJfJ9eSqYn5FJkrdw,11658
|
|
5
|
-
meshcore_cli-1.2.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
meshcore_cli-1.2.14.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
-
meshcore_cli-1.2.14.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
-
meshcore_cli-1.2.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|