meshcore-cli 1.2.7__py3-none-any.whl → 1.2.8__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 +122 -49
- {meshcore_cli-1.2.7.dist-info → meshcore_cli-1.2.8.dist-info}/METADATA +2 -2
- meshcore_cli-1.2.8.dist-info/RECORD +8 -0
- meshcore_cli-1.2.7.dist-info/RECORD +0 -8
- {meshcore_cli-1.2.7.dist-info → meshcore_cli-1.2.8.dist-info}/WHEEL +0 -0
- {meshcore_cli-1.2.7.dist-info → meshcore_cli-1.2.8.dist-info}/entry_points.txt +0 -0
- {meshcore_cli-1.2.7.dist-info → meshcore_cli-1.2.8.dist-info}/licenses/LICENSE +0 -0
meshcore_cli/meshcore_cli.py
CHANGED
|
@@ -33,7 +33,7 @@ import re
|
|
|
33
33
|
from meshcore import MeshCore, EventType, logger
|
|
34
34
|
|
|
35
35
|
# Version
|
|
36
|
-
VERSION = "v1.2.
|
|
36
|
+
VERSION = "v1.2.8"
|
|
37
37
|
|
|
38
38
|
# default ble address is stored in a config file
|
|
39
39
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -710,13 +710,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
710
710
|
contact = to
|
|
711
711
|
prev_contact = None
|
|
712
712
|
|
|
713
|
-
|
|
714
|
-
if res is None or res.type == EventType.ERROR:
|
|
715
|
-
scope = None
|
|
716
|
-
prev_scope = None
|
|
717
|
-
else:
|
|
718
|
-
scope = "*"
|
|
719
|
-
prev_scope = "*"
|
|
713
|
+
scope = await set_scope(mc, "*")
|
|
720
714
|
|
|
721
715
|
await get_contacts(mc, anim=True)
|
|
722
716
|
await get_channels(mc, anim=True)
|
|
@@ -756,6 +750,9 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
756
750
|
|
|
757
751
|
last_ack = True
|
|
758
752
|
while True:
|
|
753
|
+
# reset scope (if changed)
|
|
754
|
+
scope = await set_scope(mc, scope)
|
|
755
|
+
|
|
759
756
|
color = process_event_message.color
|
|
760
757
|
classic = interactive_loop.classic or not color
|
|
761
758
|
print_name = interactive_loop.print_name
|
|
@@ -834,6 +831,8 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
834
831
|
completer=completer,
|
|
835
832
|
key_bindings=bindings)
|
|
836
833
|
|
|
834
|
+
line = line.strip()
|
|
835
|
+
|
|
837
836
|
if line == "" : # blank line
|
|
838
837
|
pass
|
|
839
838
|
|
|
@@ -848,24 +847,41 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
848
847
|
except ValueError:
|
|
849
848
|
logger.error("Error parsing line {line[1:]}")
|
|
850
849
|
|
|
851
|
-
elif line.startswith("/scope")
|
|
850
|
+
elif line.startswith("/scope") or\
|
|
851
|
+
line.startswith("scope") and contact is None:
|
|
852
852
|
if not scope is None:
|
|
853
853
|
prev_scope = scope
|
|
854
|
-
|
|
855
|
-
|
|
854
|
+
try:
|
|
855
|
+
newscope = line.split(" ", 1)[1]
|
|
856
|
+
scope = await set_scope(mc, newscope)
|
|
857
|
+
except IndexError:
|
|
858
|
+
print(scope)
|
|
859
|
+
|
|
860
|
+
elif contact is None and (line.startswith("apply_to ") or line.startswith("at ")) or\
|
|
861
|
+
line.startswith("/apply_to ") or line.startswith("/at ") :
|
|
862
|
+
try:
|
|
863
|
+
await apply_command_to_contacts(mc, line.split(" ",2)[1], line.split(" ",2)[2])
|
|
864
|
+
except IndexError:
|
|
865
|
+
logger.error(f"Error with apply_to command parameters")
|
|
856
866
|
|
|
857
867
|
elif line.startswith("/") :
|
|
858
868
|
path = line.split(" ", 1)[0]
|
|
859
869
|
if path.count("/") == 1:
|
|
860
870
|
args = line[1:].split(" ")
|
|
861
|
-
|
|
871
|
+
dest = args[0]
|
|
872
|
+
dest_scope = None
|
|
873
|
+
if "%" in dest :
|
|
874
|
+
dest_scope = dest.split("%")[-1]
|
|
875
|
+
dest = dest[:-len(dest_scope)-1]
|
|
876
|
+
await set_scope (mc, dest_scope)
|
|
877
|
+
tct = mc.get_contact_by_name(dest)
|
|
862
878
|
if len(args)>1 and not tct is None: # a contact, send a message
|
|
863
879
|
if tct["type"] == 1 or tct["type"] == 3: # client or room
|
|
864
880
|
last_ack = await msg_ack(mc, tct, line.split(" ", 1)[1])
|
|
865
881
|
else:
|
|
866
882
|
print("Can only send msg to chan, client or room")
|
|
867
883
|
else :
|
|
868
|
-
ch = await get_channel_by_name(mc,
|
|
884
|
+
ch = await get_channel_by_name(mc, dest)
|
|
869
885
|
if len(args)>1 and not ch is None: # a channel, send message
|
|
870
886
|
await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
|
|
871
887
|
else :
|
|
@@ -876,6 +892,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
876
892
|
else:
|
|
877
893
|
cmdline = line[1:].split("/",1)[1]
|
|
878
894
|
contact_name = path[1:].split("/",1)[0]
|
|
895
|
+
dest_scope = None
|
|
896
|
+
if "%" in contact_name:
|
|
897
|
+
dest_scope = contact_name.split("%")[-1]
|
|
898
|
+
contact_name = contact_name[:-len(dest_scope)-1]
|
|
899
|
+
await set_scope (mc, dest_scope)
|
|
879
900
|
tct = mc.get_contact_by_name(contact_name)
|
|
880
901
|
if tct is None:
|
|
881
902
|
print(f"{contact_name} is not a contact")
|
|
@@ -891,6 +912,10 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
891
912
|
dest = line[3:]
|
|
892
913
|
if dest.startswith("\"") or dest.startswith("\'") : # if name starts with a quote
|
|
893
914
|
dest = shlex.split(dest)[0] # use shlex.split to get contact name between quotes
|
|
915
|
+
dest_scope = None
|
|
916
|
+
if '%' in dest and scope!=None :
|
|
917
|
+
dest_scope = dest.split("%")[-1]
|
|
918
|
+
dest = dest[:-len(dest_scope)-1]
|
|
894
919
|
nc = mc.get_contact_by_name(dest)
|
|
895
920
|
if nc is None:
|
|
896
921
|
if dest == "public" :
|
|
@@ -904,6 +929,8 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
904
929
|
nc["adv_name"] = mc.channels[dest]["channel_name"]
|
|
905
930
|
elif dest == ".." : # previous recipient
|
|
906
931
|
nc = prev_contact
|
|
932
|
+
if dest_scope is None and not scope is None:
|
|
933
|
+
dest_scope = prev_scope
|
|
907
934
|
elif dest == "~" or dest == "/" or dest == mc.self_info['name']:
|
|
908
935
|
nc = None
|
|
909
936
|
elif dest == "!" :
|
|
@@ -921,6 +948,12 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
921
948
|
last_ack = True
|
|
922
949
|
prev_contact = contact
|
|
923
950
|
contact = nc
|
|
951
|
+
if dest_scope is None:
|
|
952
|
+
dest_scope = scope
|
|
953
|
+
if not scope is None and dest_scope != scope:
|
|
954
|
+
prev_scope = scope
|
|
955
|
+
if not dest_scope is None:
|
|
956
|
+
scope = await set_scope(mc, dest_scope)
|
|
924
957
|
|
|
925
958
|
elif line == "to" :
|
|
926
959
|
if contact is None :
|
|
@@ -949,13 +982,6 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
949
982
|
if last_ack == False :
|
|
950
983
|
contact = ln
|
|
951
984
|
|
|
952
|
-
elif contact is None and\
|
|
953
|
-
(line.startswith("apply_to ") or line.startswith("at ")):
|
|
954
|
-
try:
|
|
955
|
-
await apply_command_to_contacts(mc, line.split(" ",2)[1], line.split(" ",2)[2])
|
|
956
|
-
except IndexError:
|
|
957
|
-
logger.error(f"Error with apply_to command parameters")
|
|
958
|
-
|
|
959
985
|
# commands are passed through if at root
|
|
960
986
|
elif contact is None or line.startswith(".") :
|
|
961
987
|
try:
|
|
@@ -1007,6 +1033,12 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1007
1033
|
if contact["type"] == 0:
|
|
1008
1034
|
return False
|
|
1009
1035
|
|
|
1036
|
+
# if one element in line (most cases) strip the scope and apply it
|
|
1037
|
+
if not " " in line and "%" in line:
|
|
1038
|
+
dest_scope = line.split("%")[-1]
|
|
1039
|
+
line = line[:-len(dest_scope)-1]
|
|
1040
|
+
await set_scope (mc, dest_scope)
|
|
1041
|
+
|
|
1010
1042
|
if line.startswith(":") : # : will send a command to current recipient
|
|
1011
1043
|
args=["cmd", contact['adv_name'], line[1:]]
|
|
1012
1044
|
await process_cmds(mc, args)
|
|
@@ -1121,15 +1153,21 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1121
1153
|
return True
|
|
1122
1154
|
|
|
1123
1155
|
# same but for commands with a parameter
|
|
1124
|
-
if
|
|
1125
|
-
line.startswith("cp ") or line.startswith("change_path ") or\
|
|
1126
|
-
line.startswith("cf ") or line.startswith("change_flags ") or\
|
|
1127
|
-
line.startswith("req_binary ") or\
|
|
1128
|
-
line.startswith("login ") :
|
|
1156
|
+
if " " in line:
|
|
1129
1157
|
cmds = line.split(" ", 1)
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1158
|
+
if "%" in cmds[0]:
|
|
1159
|
+
dest_scope = cmds[0].split("%")[-1]
|
|
1160
|
+
cmds[0] = cmds[0][:-len(dest_scope)-1]
|
|
1161
|
+
await set_scope(mc, dest_scope)
|
|
1162
|
+
|
|
1163
|
+
if cmds[0] == "cmd" or cmds[0] == "msg" or\
|
|
1164
|
+
cmds[0] == "cp" or cmds[0] == "change_path" or\
|
|
1165
|
+
cmds[0] == "cf" or cmds[0] == "change_flags" or\
|
|
1166
|
+
cmds[0] == "req_binary" or\
|
|
1167
|
+
cmds[0] == "login" :
|
|
1168
|
+
args = [cmds[0], contact['adv_name'], cmds[1]]
|
|
1169
|
+
await process_cmds(mc, args)
|
|
1170
|
+
return True
|
|
1133
1171
|
|
|
1134
1172
|
if line == "login": # use stored password or prompt for it
|
|
1135
1173
|
password_file = ""
|
|
@@ -1318,7 +1356,7 @@ async def send_msg (mc, contact, msg) :
|
|
|
1318
1356
|
|
|
1319
1357
|
async def msg_ack (mc, contact, msg) :
|
|
1320
1358
|
timeout = 0 if not 'timeout' in contact else contact['timeout']
|
|
1321
|
-
res = await mc.commands.send_msg_with_retry(contact, msg,
|
|
1359
|
+
res = await mc.commands.send_msg_with_retry(contact, msg,
|
|
1322
1360
|
max_attempts=msg_ack.max_attempts,
|
|
1323
1361
|
flood_after=msg_ack.flood_after,
|
|
1324
1362
|
max_flood_attempts=msg_ack.max_flood_attempts,
|
|
@@ -1339,12 +1377,26 @@ msg_ack.flood_after=2
|
|
|
1339
1377
|
msg_ack.max_flood_attempts=1
|
|
1340
1378
|
|
|
1341
1379
|
async def set_scope (mc, scope) :
|
|
1380
|
+
if not set_scope.has_scope:
|
|
1381
|
+
return None
|
|
1382
|
+
|
|
1342
1383
|
if scope == "None" or scope == "0" or scope == "clear" or scope == "":
|
|
1343
1384
|
scope = "*"
|
|
1385
|
+
|
|
1386
|
+
if set_scope.current_scope == scope:
|
|
1387
|
+
return scope
|
|
1388
|
+
|
|
1344
1389
|
res = await mc.commands.set_flood_scope(scope)
|
|
1345
1390
|
if res is None or res.type == EventType.ERROR:
|
|
1391
|
+
if not res is None and res.payload["error_code"] == 1: #unsupported
|
|
1392
|
+
set_scope.has_scope = False
|
|
1346
1393
|
return None
|
|
1394
|
+
|
|
1395
|
+
set_scope.current_scope = scope
|
|
1396
|
+
|
|
1347
1397
|
return scope
|
|
1398
|
+
set_scope.has_scope = True
|
|
1399
|
+
set_scope.current_scope = None
|
|
1348
1400
|
|
|
1349
1401
|
async def get_channel (mc, chan) :
|
|
1350
1402
|
if not chan.isnumeric():
|
|
@@ -2419,23 +2471,32 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2419
2471
|
|
|
2420
2472
|
case "node_discover"|"nd" :
|
|
2421
2473
|
argnum = 1
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
if "
|
|
2432
|
-
types =
|
|
2433
|
-
|
|
2434
|
-
types =
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2474
|
+
prefix_only = True
|
|
2475
|
+
|
|
2476
|
+
if len(cmds) == 1:
|
|
2477
|
+
argnum = 0
|
|
2478
|
+
types = 0xFF
|
|
2479
|
+
else:
|
|
2480
|
+
try: # try to decode type as int
|
|
2481
|
+
types = int(cmds[1])
|
|
2482
|
+
except ValueError:
|
|
2483
|
+
if "all" in cmds[1]:
|
|
2484
|
+
types = 0xFF
|
|
2485
|
+
else :
|
|
2486
|
+
types = 0
|
|
2487
|
+
if "rep" in cmds[1] or "rpt" in cmds[1]:
|
|
2488
|
+
types = types | 4
|
|
2489
|
+
if "cli" in cmds[1] or "comp" in cmds[1]:
|
|
2490
|
+
types = types | 2
|
|
2491
|
+
if "room" in cmds[1]:
|
|
2492
|
+
types = types | 8
|
|
2493
|
+
if "sens" in cmds[1]:
|
|
2494
|
+
types = types | 16
|
|
2495
|
+
|
|
2496
|
+
if "full" in cmds[1]:
|
|
2497
|
+
prefix_only = False
|
|
2498
|
+
|
|
2499
|
+
res = await mc.commands.send_node_discover_req(types, prefix_only=prefix_only)
|
|
2439
2500
|
if res is None or res.type == EventType.ERROR:
|
|
2440
2501
|
print("Error sending discover request")
|
|
2441
2502
|
else:
|
|
@@ -2458,10 +2519,20 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2458
2519
|
await mc.ensure_contacts()
|
|
2459
2520
|
print(f"Discovered {len(dn)} nodes:")
|
|
2460
2521
|
for n in dn:
|
|
2461
|
-
name = mc.get_contact_by_key_prefix(n[
|
|
2522
|
+
name = f"{n['pubkey'][0:2]} {mc.get_contact_by_key_prefix(n['pubkey'])['adv_name']}"
|
|
2462
2523
|
if name is None:
|
|
2463
|
-
name = n["pubkey"][0:
|
|
2464
|
-
|
|
2524
|
+
name = n["pubkey"][0:16]
|
|
2525
|
+
type = f"t:{n['node_type']}"
|
|
2526
|
+
if n['node_type'] == 1:
|
|
2527
|
+
type = "CLI"
|
|
2528
|
+
elif n['node_type'] == 2:
|
|
2529
|
+
type = "REP"
|
|
2530
|
+
elif n['node_type'] == 3:
|
|
2531
|
+
type = "ROOM"
|
|
2532
|
+
elif n['node_type'] == 4:
|
|
2533
|
+
type = "SENS"
|
|
2534
|
+
|
|
2535
|
+
print(f" {name:16} {type:>4} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}")
|
|
2465
2536
|
|
|
2466
2537
|
case "req_btelemetry"|"rbt" :
|
|
2467
2538
|
argnum = 1
|
|
@@ -3100,6 +3171,8 @@ def get_help_for (cmdname, context="line") :
|
|
|
3100
3171
|
- rep for repeaters
|
|
3101
3172
|
- sens for sensors
|
|
3102
3173
|
- room for chat rooms
|
|
3174
|
+
|
|
3175
|
+
nd can be used with no filter parameter ... !!! BEWARE WITH CHAINING !!!
|
|
3103
3176
|
""")
|
|
3104
3177
|
|
|
3105
3178
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.8
|
|
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
|
|
@@ -10,7 +10,7 @@ License-File: LICENSE
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
|
-
Requires-Dist: meshcore>=2.1.
|
|
13
|
+
Requires-Dist: meshcore>=2.1.22
|
|
14
14
|
Requires-Dist: prompt-toolkit>=3.0.50
|
|
15
15
|
Requires-Dist: pycryptodome
|
|
16
16
|
Requires-Dist: requests>=2.28.0
|
|
@@ -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=dybZ5Xlk_eu2x4u60iFyHxm4rXqewZTo56-4m_20umU,141327
|
|
4
|
+
meshcore_cli-1.2.8.dist-info/METADATA,sha256=q3H8TQt9GfgChzTz_bMdeDxWcwpS4DAeUi6zfg51bs4,11657
|
|
5
|
+
meshcore_cli-1.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
meshcore_cli-1.2.8.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
+
meshcore_cli-1.2.8.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
+
meshcore_cli-1.2.8.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=E-zWpvbPyhstbU68BgINMxY6QAIM-tmU7zpPERZ_Xpk,138363
|
|
4
|
-
meshcore_cli-1.2.7.dist-info/METADATA,sha256=m8b_jUfFVeLgGlHOcJZTsSO91lTGdP5Jix-TF0v8W9w,11657
|
|
5
|
-
meshcore_cli-1.2.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
meshcore_cli-1.2.7.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
-
meshcore_cli-1.2.7.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
-
meshcore_cli-1.2.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|