meshcore-cli 1.2.12__tar.gz → 1.2.14__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_cli-1.2.12 → meshcore_cli-1.2.14}/PKG-INFO +1 -1
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/pyproject.toml +1 -1
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/src/meshcore_cli/meshcore_cli.py +54 -92
- meshcore_cli-1.2.12/PXL_20251107_184348511.jpg +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/.gitignore +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/LICENSE +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/README.md +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/flake.lock +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/flake.nix +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/src/meshcore_cli/__init__.py +0 -0
- {meshcore_cli-1.2.12 → meshcore_cli-1.2.14}/src/meshcore_cli/__main__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.14
|
|
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
|
|
@@ -32,7 +32,7 @@ import re
|
|
|
32
32
|
from meshcore import MeshCore, EventType, logger
|
|
33
33
|
|
|
34
34
|
# Version
|
|
35
|
-
VERSION = "v1.2.
|
|
35
|
+
VERSION = "v1.2.14"
|
|
36
36
|
|
|
37
37
|
# default ble address is stored in a config file
|
|
38
38
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -76,13 +76,11 @@ ANSI_YELLOW = "\033[0;33m"
|
|
|
76
76
|
ANSI_BYELLOW = "\033[1;33m"
|
|
77
77
|
|
|
78
78
|
#Unicode chars
|
|
79
|
-
# some possible symbols for prompts
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
ARROW_TAIL = ""
|
|
85
|
-
ARROW_HEAD = " "
|
|
79
|
+
# some possible symbols for prompts 🭬🬛🬗🭬🬛🬃🬗🭬🬛🬃🬗🬏🭀🭋🭨🮋
|
|
80
|
+
ARROW_HEAD = ""
|
|
81
|
+
SLASH_END = ""
|
|
82
|
+
SLASH_START = ""
|
|
83
|
+
INVERT_SLASH = False
|
|
86
84
|
|
|
87
85
|
def escape_ansi(line):
|
|
88
86
|
ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
|
|
@@ -470,7 +468,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
470
468
|
"login" : contact_list,
|
|
471
469
|
"cmd" : contact_list,
|
|
472
470
|
"req_status" : contact_list,
|
|
473
|
-
"
|
|
471
|
+
"req_neighbours": contact_list,
|
|
474
472
|
"logout" : contact_list,
|
|
475
473
|
"req_telemetry" : contact_list,
|
|
476
474
|
"req_binary" : contact_list,
|
|
@@ -495,7 +493,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
495
493
|
"print_snr" : {"on":None, "off": None},
|
|
496
494
|
"json_msgs" : {"on":None, "off": None},
|
|
497
495
|
"color" : {"on":None, "off":None},
|
|
498
|
-
"print_name" : {"on":None, "off":None},
|
|
499
496
|
"print_adverts" : {"on":None, "off":None},
|
|
500
497
|
"json_log_rx" : {"on":None, "off":None},
|
|
501
498
|
"channel_echoes" : {"on":None, "off":None},
|
|
@@ -525,7 +522,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
525
522
|
"print_snr":None,
|
|
526
523
|
"json_msgs":None,
|
|
527
524
|
"color":None,
|
|
528
|
-
"print_name":None,
|
|
529
525
|
"print_adverts":None,
|
|
530
526
|
"json_log_rx":None,
|
|
531
527
|
"channel_echoes":None,
|
|
@@ -577,7 +573,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
577
573
|
"login" : None,
|
|
578
574
|
"logout" : None,
|
|
579
575
|
"req_status" : None,
|
|
580
|
-
"req_bstatus" : None,
|
|
581
576
|
"req_neighbours": None,
|
|
582
577
|
"cmd" : None,
|
|
583
578
|
"ver" : None,
|
|
@@ -765,26 +760,32 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
765
760
|
|
|
766
761
|
color = process_event_message.color
|
|
767
762
|
classic = interactive_loop.classic or not color
|
|
768
|
-
print_name = interactive_loop.print_name
|
|
769
763
|
|
|
770
764
|
if classic:
|
|
771
765
|
prompt = ""
|
|
772
766
|
else:
|
|
773
767
|
prompt = f"{ANSI_INVERT}"
|
|
774
768
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
769
|
+
prompt = prompt + f"{ANSI_BGRAY}"
|
|
770
|
+
prompt = prompt + f"{mc.self_info['name']}"
|
|
771
|
+
if contact is None: # display scope
|
|
772
|
+
if not scope is None:
|
|
773
|
+
prompt = prompt + f"|{scope}"
|
|
774
|
+
|
|
775
|
+
if contact is None :
|
|
782
776
|
if classic :
|
|
783
|
-
prompt = prompt + ">
|
|
777
|
+
prompt = prompt + ">"
|
|
784
778
|
else :
|
|
785
|
-
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}
|
|
786
|
-
|
|
787
|
-
|
|
779
|
+
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}"
|
|
780
|
+
else:
|
|
781
|
+
if classic :
|
|
782
|
+
prompt = prompt + "/"
|
|
783
|
+
else :
|
|
784
|
+
if INVERT_SLASH:
|
|
785
|
+
prompt = prompt + f"{ANSI_INVERT}"
|
|
786
|
+
else:
|
|
787
|
+
prompt = prompt + f"{ANSI_NORMAL}"
|
|
788
|
+
prompt = prompt + f"{SLASH_START}"
|
|
788
789
|
if not last_ack:
|
|
789
790
|
prompt = prompt + f"{ANSI_BRED}"
|
|
790
791
|
if classic :
|
|
@@ -800,11 +801,9 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
800
801
|
else :
|
|
801
802
|
prompt = prompt + f"{ANSI_BBLUE}"
|
|
802
803
|
if not classic:
|
|
804
|
+
prompt = prompt + f"{SLASH_END}"
|
|
803
805
|
prompt = prompt + f"{ANSI_INVERT}"
|
|
804
806
|
|
|
805
|
-
if print_name and not classic :
|
|
806
|
-
prompt = prompt + f"{ANSI_NORMAL}{ARROW_TAIL}{ANSI_INVERT}"
|
|
807
|
-
|
|
808
807
|
prompt = prompt + f"{contact['adv_name']}"
|
|
809
808
|
if contact["type"] == 0 or contact["out_path_len"]==-1:
|
|
810
809
|
if scope is None:
|
|
@@ -818,14 +817,15 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
818
817
|
prompt = prompt + "|" + contact["out_path"]
|
|
819
818
|
|
|
820
819
|
if classic :
|
|
821
|
-
prompt = prompt + f"{ANSI_NORMAL}>
|
|
820
|
+
prompt = prompt + f"{ANSI_NORMAL}>"
|
|
822
821
|
else:
|
|
823
822
|
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}"
|
|
824
823
|
|
|
825
824
|
prompt = prompt + f"{ANSI_END}"
|
|
826
825
|
|
|
827
|
-
|
|
828
|
-
|
|
826
|
+
prompt = prompt + " "
|
|
827
|
+
if not color :
|
|
828
|
+
prompt=escape_ansi(prompt)
|
|
829
829
|
|
|
830
830
|
session.app.ttimeoutlen = 0.2
|
|
831
831
|
session.app.timeoutlen = 0.2
|
|
@@ -1036,8 +1036,10 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
1036
1036
|
except asyncio.CancelledError:
|
|
1037
1037
|
# Handle task cancellation from KeyboardInterrupt in asyncio.run()
|
|
1038
1038
|
print("Exiting cli")
|
|
1039
|
-
|
|
1040
|
-
interactive_loop.
|
|
1039
|
+
if platform.system() == "Darwin" or platform.system() == "Windows":
|
|
1040
|
+
interactive_loop.classic = True
|
|
1041
|
+
else:
|
|
1042
|
+
interactive_loop.classic = False
|
|
1041
1043
|
|
|
1042
1044
|
async def process_contact_chat_line(mc, contact, line):
|
|
1043
1045
|
if contact["type"] == 0:
|
|
@@ -1085,7 +1087,6 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1085
1087
|
line == "contact_info" or line == "ci" or\
|
|
1086
1088
|
line == "req_status" or line == "rs" or\
|
|
1087
1089
|
line == "req_neighbours" or line == "rn" or\
|
|
1088
|
-
line == "req_bstatus" or line == "rbs" or\
|
|
1089
1090
|
line == "req_telemetry" or line == "rt" or\
|
|
1090
1091
|
line == "req_acl" or\
|
|
1091
1092
|
line == "path" or\
|
|
@@ -1615,7 +1616,7 @@ async def print_disc_trace_to (mc, contact):
|
|
|
1615
1616
|
|
|
1616
1617
|
async def next_cmd(mc, cmds, json_output=False):
|
|
1617
1618
|
""" process next command """
|
|
1618
|
-
global
|
|
1619
|
+
global ARROW_HEAD, SLASH_START, SLASH_END, INVERT_SLASH
|
|
1619
1620
|
try :
|
|
1620
1621
|
argnum = 0
|
|
1621
1622
|
|
|
@@ -1741,18 +1742,18 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1741
1742
|
msg_ack.max_attempts=int(cmds[2])
|
|
1742
1743
|
case "flood_after":
|
|
1743
1744
|
msg_ack.flood_after=int(cmds[2])
|
|
1744
|
-
case "print_name":
|
|
1745
|
-
interactive_loop.print_name = (cmds[2] == "on")
|
|
1746
|
-
if json_output :
|
|
1747
|
-
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1748
1745
|
case "classic_prompt":
|
|
1749
1746
|
interactive_loop.classic = (cmds[2] == "on")
|
|
1750
1747
|
if json_output :
|
|
1751
1748
|
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1752
|
-
case "arrow_tail":
|
|
1753
|
-
ARROW_TAIL = cmds[2]
|
|
1754
1749
|
case "arrow_head":
|
|
1755
1750
|
ARROW_HEAD = cmds[2]
|
|
1751
|
+
case "slash_start":
|
|
1752
|
+
SLASH_START = cmds[2]
|
|
1753
|
+
case "slash_end":
|
|
1754
|
+
SLASH_END = cmds[2]
|
|
1755
|
+
case "invert_slash":
|
|
1756
|
+
INVERT_SLASH = cmds[2] == "on"
|
|
1756
1757
|
case "color" :
|
|
1757
1758
|
process_event_message.color = (cmds[2] == "on")
|
|
1758
1759
|
if json_output :
|
|
@@ -1983,11 +1984,6 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1983
1984
|
print(json.dumps({"flood_after" : msg_ack.flood_after}))
|
|
1984
1985
|
else:
|
|
1985
1986
|
print(f"flood_after: {msg_ack.flood_after}")
|
|
1986
|
-
case "print_name":
|
|
1987
|
-
if json_output :
|
|
1988
|
-
print(json.dumps({"print_name" : interactive_loop.print_name}))
|
|
1989
|
-
else:
|
|
1990
|
-
print(f"{'on' if interactive_loop.print_name else 'off'}")
|
|
1991
1987
|
case "classic_prompt":
|
|
1992
1988
|
if json_output :
|
|
1993
1989
|
print(json.dumps({"classic_prompt" : interactive_loop.classic}))
|
|
@@ -2344,12 +2340,11 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2344
2340
|
else :
|
|
2345
2341
|
color = process_event_message.color
|
|
2346
2342
|
classic = interactive_loop.classic or not color
|
|
2347
|
-
print("]",end="")
|
|
2348
2343
|
for t in ev.payload["path"]:
|
|
2349
2344
|
if classic :
|
|
2350
2345
|
print("→",end="")
|
|
2351
2346
|
else:
|
|
2352
|
-
print(f"
|
|
2347
|
+
print(f"{ANSI_INVERT}", end="")
|
|
2353
2348
|
snr = t['snr']
|
|
2354
2349
|
if color:
|
|
2355
2350
|
if snr >= 10 :
|
|
@@ -2368,7 +2363,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2368
2363
|
if "hash" in t:
|
|
2369
2364
|
print(f"[{t['hash']}]",end="")
|
|
2370
2365
|
else:
|
|
2371
|
-
print(
|
|
2366
|
+
print()
|
|
2372
2367
|
|
|
2373
2368
|
case "login" | "l" :
|
|
2374
2369
|
argnum = 2
|
|
@@ -2428,46 +2423,6 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2428
2423
|
contact = mc.get_contact_by_name(cmds[1])
|
|
2429
2424
|
contact["timeout"] = float(cmds[2])
|
|
2430
2425
|
|
|
2431
|
-
case "req_status" | "rs" :
|
|
2432
|
-
argnum = 1
|
|
2433
|
-
await mc.ensure_contacts()
|
|
2434
|
-
contact = mc.get_contact_by_name(cmds[1])
|
|
2435
|
-
res = await mc.commands.send_statusreq(contact)
|
|
2436
|
-
logger.debug(res)
|
|
2437
|
-
if res.type == EventType.ERROR:
|
|
2438
|
-
print(f"Error while requesting status: {res}")
|
|
2439
|
-
else :
|
|
2440
|
-
timeout = res.payload["suggested_timeout"]/800 if not "timeout" in contact or contact['timeout']==0 else contact["timeout"]
|
|
2441
|
-
res = await mc.wait_for_event(EventType.STATUS_RESPONSE, timeout=timeout)
|
|
2442
|
-
logger.debug(res)
|
|
2443
|
-
if res is None:
|
|
2444
|
-
if json_output :
|
|
2445
|
-
print(json.dumps({"error" : "Timeout waiting status"}))
|
|
2446
|
-
else:
|
|
2447
|
-
print("Timeout waiting status")
|
|
2448
|
-
else :
|
|
2449
|
-
print(json.dumps(res.payload, indent=4))
|
|
2450
|
-
|
|
2451
|
-
case "req_telemetry" | "rt" :
|
|
2452
|
-
argnum = 1
|
|
2453
|
-
await mc.ensure_contacts()
|
|
2454
|
-
contact = mc.get_contact_by_name(cmds[1])
|
|
2455
|
-
res = await mc.commands.send_telemetry_req(contact)
|
|
2456
|
-
logger.debug(res)
|
|
2457
|
-
if res.type == EventType.ERROR:
|
|
2458
|
-
print(f"Error while requesting telemetry")
|
|
2459
|
-
else:
|
|
2460
|
-
timeout = res.payload["suggested_timeout"]/800 if not "timeout" in contact or contact['timeout']==0 else contact["timeout"]
|
|
2461
|
-
res = await mc.wait_for_event(EventType.TELEMETRY_RESPONSE, timeout=timeout)
|
|
2462
|
-
logger.debug(res)
|
|
2463
|
-
if res is None:
|
|
2464
|
-
if json_output :
|
|
2465
|
-
print(json.dumps({"error" : "Timeout waiting telemetry"}))
|
|
2466
|
-
else:
|
|
2467
|
-
print("Timeout waiting telemetry")
|
|
2468
|
-
else :
|
|
2469
|
-
print(json.dumps(res.payload, indent=4))
|
|
2470
|
-
|
|
2471
2426
|
case "disc_path" | "dp" :
|
|
2472
2427
|
argnum = 1
|
|
2473
2428
|
await mc.ensure_contacts()
|
|
@@ -2553,7 +2508,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2553
2508
|
|
|
2554
2509
|
print(f" {name:16} {type:>4} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}")
|
|
2555
2510
|
|
|
2556
|
-
case "
|
|
2511
|
+
case "req_telemetry"|"rt" :
|
|
2557
2512
|
argnum = 1
|
|
2558
2513
|
await mc.ensure_contacts()
|
|
2559
2514
|
contact = mc.get_contact_by_name(cmds[1])
|
|
@@ -2565,9 +2520,13 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2565
2520
|
else:
|
|
2566
2521
|
print("Error getting data")
|
|
2567
2522
|
else :
|
|
2568
|
-
print(json.dumps(
|
|
2523
|
+
print(json.dumps({
|
|
2524
|
+
"name": contact["adv_name"],
|
|
2525
|
+
"pubkey_pre": contact["public_key"][0:12],
|
|
2526
|
+
"lpp": res,
|
|
2527
|
+
}, indent = 4))
|
|
2569
2528
|
|
|
2570
|
-
case "
|
|
2529
|
+
case "req_status"|"rs" :
|
|
2571
2530
|
argnum = 1
|
|
2572
2531
|
await mc.ensure_contacts()
|
|
2573
2532
|
contact = mc.get_contact_by_name(cmds[1])
|
|
@@ -3174,6 +3133,7 @@ def usage () :
|
|
|
3174
3133
|
-D : debug
|
|
3175
3134
|
-S : scan for devices and show a selector
|
|
3176
3135
|
-l : list available ble/serial devices and exit
|
|
3136
|
+
-C : toggles classic mode for prompt
|
|
3177
3137
|
-c <on/off> : disables most of color output if off
|
|
3178
3138
|
-T <timeout> : timeout for the ble scan (-S and -l) default 2s
|
|
3179
3139
|
-a <address> : specifies device address (can be a name)
|
|
@@ -3242,12 +3202,14 @@ async def main(argv):
|
|
|
3242
3202
|
with open(MCCLI_ADDRESS, encoding="utf-8") as f :
|
|
3243
3203
|
address = f.readline().strip()
|
|
3244
3204
|
|
|
3245
|
-
opts, args = getopt.getopt(argv, "a:d:s:ht:p:b:fjDhvSlT:Pc:")
|
|
3205
|
+
opts, args = getopt.getopt(argv, "a:d:s:ht:p:b:fjDhvSlT:Pc:C")
|
|
3246
3206
|
for opt, arg in opts :
|
|
3247
3207
|
match opt:
|
|
3248
3208
|
case "-c" :
|
|
3249
3209
|
if arg == "off":
|
|
3250
3210
|
process_event_message.color = False
|
|
3211
|
+
case "-C":
|
|
3212
|
+
interactive_loop.classic = not interactive_loop.classic
|
|
3251
3213
|
case "-d" : # name specified on cmdline
|
|
3252
3214
|
address = arg
|
|
3253
3215
|
case "-a" : # address specified on cmdline
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|