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.
@@ -33,7 +33,7 @@ import re
33
33
  from meshcore import MeshCore, EventType, logger
34
34
 
35
35
  # Version
36
- VERSION = "v1.2.7"
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
- res = await mc.commands.set_flood_scope("0")
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
- newscope = line.split(" ", 1)[1]
855
- scope = await set_scope(mc, newscope)
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
- tct = mc.get_contact_by_name(args[0])
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, args[0])
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 line.startswith("cmd ") or line.startswith("msg ") or\
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
- args = [cmds[0], contact['adv_name'], cmds[1]]
1131
- await process_cmds(mc, args)
1132
- return True
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
- try: # try to decode type as int
2423
- types = int(cmds[1])
2424
- except ValueError:
2425
- if "all" in cmds[1]:
2426
- types = 0xFF
2427
- else :
2428
- types = 0
2429
- if "rep" in cmds[1]:
2430
- types = types | 4
2431
- if "cli" in cmds[1] or "comp" in cmds[1]:
2432
- types = types | 2
2433
- if "room" in cmds[1]:
2434
- types = types | 8
2435
- if "sens" in cmds[1]:
2436
- types = types | 16
2437
-
2438
- res = await mc.commands.send_node_discover_req(types)
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["pubkey"])['adv_name']
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:12]
2464
- print(f" {name:12} type {n['node_type']} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}")
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.7
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.21
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,,