meshcore-cli 1.3.8__tar.gz → 1.3.11__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcore-cli
3
- Version: 1.3.8
3
+ Version: 1.3.11
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
@@ -11,7 +11,7 @@ Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.10
13
13
  Requires-Dist: bleak<2.0,>=0.22
14
- Requires-Dist: meshcore>=2.2.2
14
+ Requires-Dist: meshcore>=2.2.3
15
15
  Requires-Dist: prompt-toolkit>=3.0.50
16
16
  Requires-Dist: pycryptodome
17
17
  Requires-Dist: requests>=2.28.0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "meshcore-cli"
7
- version = "1.3.8"
7
+ version = "1.3.11"
8
8
  authors = [
9
9
  { name="Florent de Lamotte", email="florent@frizoncorrea.fr" },
10
10
  ]
@@ -17,7 +17,7 @@ classifiers = [
17
17
  ]
18
18
  license = "MIT"
19
19
  license-files = ["LICEN[CS]E*"]
20
- dependencies = [ "meshcore >= 2.2.2",
20
+ dependencies = [ "meshcore >= 2.2.3",
21
21
  "bleak >= 0.22, <2.0",
22
22
  "prompt_toolkit >= 3.0.50",
23
23
  "requests >= 2.28.0",
@@ -32,7 +32,7 @@ import re
32
32
  from meshcore import MeshCore, EventType, logger
33
33
 
34
34
  # Version
35
- VERSION = "v1.3.8"
35
+ VERSION = "v1.3.11"
36
36
 
37
37
  # default ble address is stored in a config file
38
38
  MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
@@ -136,9 +136,9 @@ async def process_event_message(mc, ev, json_output, end="\n", above=False):
136
136
  ts = data["sender_timestamp"]
137
137
  if process_event_message.timestamp == "on":
138
138
  if (abs(time.time()-ts) < 86400):
139
- fmt = "%H:%S"
139
+ fmt = "%H:%M"
140
140
  else:
141
- fmt = "%y-%m-%d %H:%S"
141
+ fmt = "%y-%m-%d %H:%M"
142
142
  else:
143
143
  fmt = process_event_message.timestamp
144
144
  path_str += f'{datetime.datetime.fromtimestamp(ts).strftime(fmt)},'
@@ -334,14 +334,14 @@ async def handle_log_rx(event):
334
334
  else:
335
335
  adv_name = ct["adv_name"]
336
336
 
337
- ts_string = ""
337
+ ts_str = ""
338
338
  if process_event_message.timestamp != "" and process_event_message.timestamp != "off":
339
339
  ts = adv_timestamp
340
340
  if process_event_message.timestamp == "on":
341
341
  if (abs(time.time()-ts) < 86400):
342
- fmt = "%H:%S"
342
+ fmt = "%H:%M"
343
343
  else:
344
- fmt = "%y-%m-%d %H:%S"
344
+ fmt = "%y-%m-%d %H:%M"
345
345
  else:
346
346
  fmt = process_event_message.timestamp
347
347
  ts_str = f' at {datetime.datetime.fromtimestamp(ts).strftime(fmt)}'
@@ -539,6 +539,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
539
539
 
540
540
  completion_list = {
541
541
  "to" : to_list,
542
+ "/to" : to_list,
542
543
  "public" : None,
543
544
  "chan" : None,
544
545
  }
@@ -599,6 +600,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
599
600
  "lat" : None,
600
601
  "lon" : None,
601
602
  "coords" : None,
603
+ "private_key": None,
602
604
  "print_snr" : {"on":None, "off": None},
603
605
  "print_timestamp" : {"on":None, "off": None, "%Y:%M":None},
604
606
  "json_msgs" : {"on":None, "off": None},
@@ -630,6 +632,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
630
632
  "coords":None,
631
633
  "lat":None,
632
634
  "lon":None,
635
+ "private_key":None,
633
636
  "print_snr":None,
634
637
  "print_timestamp":None,
635
638
  "json_msgs":None,
@@ -859,8 +862,6 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
859
862
 
860
863
  await subscribe_to_msgs(mc, above=True)
861
864
 
862
- handle_new_contact.print_new_contacts = True
863
-
864
865
  try:
865
866
  if os.path.isdir(MCCLI_CONFIG_DIR) :
866
867
  our_history = FileHistory(MCCLI_HISTORY_FILE)
@@ -1000,6 +1001,9 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
1000
1001
  except IndexError:
1001
1002
  print(scope)
1002
1003
 
1004
+ elif line == "quit" or line == "q" or line == "/quit" or line == "/q" :
1005
+ break
1006
+
1003
1007
  elif contact is None and (line.startswith("apply_to ") or line.startswith("at ")) or\
1004
1008
  line.startswith("/apply_to ") or line.startswith("/at ") :
1005
1009
  try:
@@ -1007,52 +1011,8 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
1007
1011
  except IndexError:
1008
1012
  logger.error(f"Error with apply_to command parameters")
1009
1013
 
1010
- elif line.startswith("/") :
1011
- path = line.split(" ", 1)[0]
1012
- if path.count("/") == 1:
1013
- args = line[1:].split(" ")
1014
- dest = args[0]
1015
- dest_scope = None
1016
- if "%" in dest :
1017
- dest_scope = dest.split("%")[-1]
1018
- dest = dest[:-len(dest_scope)-1]
1019
- await set_scope (mc, dest_scope)
1020
- tct = mc.get_contact_by_name(dest)
1021
- if len(args)>1 and not tct is None: # a contact, send a message
1022
- if tct["type"] == 1 or tct["type"] == 3: # client or room
1023
- last_ack = await msg_ack(mc, tct, line.split(" ", 1)[1])
1024
- else:
1025
- print("Can only send msg to chan, client or room")
1026
- else :
1027
- ch = await get_channel_by_name(mc, dest)
1028
- if len(args)>1 and not ch is None: # a channel, send message
1029
- await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
1030
- else :
1031
- try :
1032
- await process_cmds(mc, shlex.split(line[1:]))
1033
- except ValueError:
1034
- logger.error(f"Error processing line{line[1:]}")
1035
- else:
1036
- cmdline = line[1:].split("/",1)[1]
1037
- contact_name = path[1:].split("/",1)[0]
1038
- dest_scope = None
1039
- if "%" in contact_name:
1040
- dest_scope = contact_name.split("%")[-1]
1041
- contact_name = contact_name[:-len(dest_scope)-1]
1042
- await set_scope (mc, dest_scope)
1043
- tct = mc.get_contact_by_name(contact_name)
1044
- if tct is None:
1045
- print(f"{contact_name} is not a contact")
1046
- else:
1047
- if not await process_contact_chat_line(mc, tct, cmdline):
1048
- if cmdline != "":
1049
- if tct["type"] == 1:
1050
- last_ack = await msg_ack(mc, tct, cmdline)
1051
- else :
1052
- await process_cmds(mc, ["cmd", tct["adv_name"], cmdline])
1053
-
1054
- elif line.startswith("to ") : # dest
1055
- dest = line[3:]
1014
+ elif line.startswith("to ") or line.startswith("/to "): # dest
1015
+ dest = line.split(" ", 1)[1]
1056
1016
  if dest.startswith("\"") or dest.startswith("\'") : # if name starts with a quote
1057
1017
  dest = shlex.split(dest)[0] # use shlex.split to get contact name between quotes
1058
1018
  dest_scope = None
@@ -1098,14 +1058,55 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
1098
1058
  if not dest_scope is None:
1099
1059
  scope = await set_scope(mc, dest_scope)
1100
1060
 
1101
- elif line == "to" :
1061
+ elif line == "to" or line == "/to" :
1102
1062
  if contact is None :
1103
1063
  print(mc.self_info['name'])
1104
1064
  else:
1105
1065
  print(contact["adv_name"])
1106
1066
 
1107
- elif line == "quit" or line == "q" :
1108
- break
1067
+ elif line.startswith("/") :
1068
+ path = line.split(" ", 1)[0]
1069
+ if path.count("/") == 1:
1070
+ args = line[1:].split(" ")
1071
+ dest = args[0]
1072
+ dest_scope = None
1073
+ if "%" in dest :
1074
+ dest_scope = dest.split("%")[-1]
1075
+ dest = dest[:-len(dest_scope)-1]
1076
+ await set_scope (mc, dest_scope)
1077
+ tct = mc.get_contact_by_name(dest)
1078
+ if len(args)>1 and not tct is None: # a contact, send a message
1079
+ if tct["type"] == 1 or tct["type"] == 3: # client or room
1080
+ last_ack = await msg_ack(mc, tct, line.split(" ", 1)[1])
1081
+ else:
1082
+ print("Can only send msg to chan, client or room")
1083
+ else :
1084
+ ch = await get_channel_by_name(mc, dest)
1085
+ if len(args)>1 and not ch is None: # a channel, send message
1086
+ await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
1087
+ else :
1088
+ try :
1089
+ await process_cmds(mc, shlex.split(line[1:]))
1090
+ except ValueError:
1091
+ logger.error(f"Error processing line{line[1:]}")
1092
+ else:
1093
+ cmdline = line[1:].split("/",1)[1]
1094
+ contact_name = path[1:].split("/",1)[0]
1095
+ dest_scope = None
1096
+ if "%" in contact_name:
1097
+ dest_scope = contact_name.split("%")[-1]
1098
+ contact_name = contact_name[:-len(dest_scope)-1]
1099
+ await set_scope (mc, dest_scope)
1100
+ tct = mc.get_contact_by_name(contact_name)
1101
+ if tct is None:
1102
+ print(f"{contact_name} is not a contact")
1103
+ else:
1104
+ if not await process_contact_chat_line(mc, tct, cmdline):
1105
+ if cmdline != "":
1106
+ if tct["type"] == 1:
1107
+ last_ack = await msg_ack(mc, tct, cmdline)
1108
+ else :
1109
+ await process_cmds(mc, ["cmd", tct["adv_name"], cmdline])
1109
1110
 
1110
1111
  # commands that take one parameter (don't need quotes)
1111
1112
  elif line.startswith("public ") :
@@ -1604,8 +1605,11 @@ async def set_scope (mc, scope) :
1604
1605
  return scope
1605
1606
 
1606
1607
  res = await mc.commands.set_flood_scope(scope)
1607
- if res is None or res.type == EventType.ERROR:
1608
- if not res is None and res.payload["error_code"] == 1: #unsupported
1608
+ if res is None :
1609
+ return None
1610
+
1611
+ if res.type == EventType.ERROR:
1612
+ if "error_code" in res.payload and res.payload["error_code"] == 1: #unsupported
1609
1613
  set_scope.has_scope = False
1610
1614
  return None
1611
1615
 
@@ -2073,6 +2077,16 @@ async def next_cmd(mc, cmds, json_output=False):
2073
2077
  print(json.dumps(res.payload, indent=4))
2074
2078
  else:
2075
2079
  print("ok")
2080
+ case "private_key":
2081
+ params=bytes.fromhex(cmds[2])
2082
+ res = await mc.commands.import_private_key(params)
2083
+ logger.debug(res)
2084
+ if res.type == EventType.ERROR:
2085
+ print(f"Error: {res}")
2086
+ elif json_output :
2087
+ print(json.dumps(res.payload, indent=4))
2088
+ else:
2089
+ print("ok")
2076
2090
  case "tuning":
2077
2091
  params=cmds[2].commands.split(",")
2078
2092
  res = await mc.commands.set_tuning(
@@ -2285,6 +2299,16 @@ async def next_cmd(mc, cmds, json_output=False):
2285
2299
  print(json.dumps(res.payload, indent=4))
2286
2300
  else:
2287
2301
  print(f"Battery level : {res.payload['level']}")
2302
+ case "private_key":
2303
+ res = await mc.commands.export_private_key()
2304
+ logger.debug(res)
2305
+ if res.type == EventType.ERROR:
2306
+ print(f"Error exporting private key {res}")
2307
+ elif json_output :
2308
+ res.payload["private_key"] = res.payload["private_key"].hex()
2309
+ print(json.dumps(res.payload))
2310
+ else:
2311
+ print(f"Private key: {res.payload['private_key'].hex()}")
2288
2312
  case "fstats" :
2289
2313
  res = await mc.commands.get_bat()
2290
2314
  logger.debug(res)
@@ -3431,6 +3455,7 @@ def get_help_for (cmdname, context="line") :
3431
3455
  lon : longitude
3432
3456
  radio : radio parameters
3433
3457
  tx : tx power
3458
+ private_key : private key of the node
3434
3459
  print_snr : snr display in messages
3435
3460
  print_adverts : display adverts as they come
3436
3461
  print_new_contacts : display new pending contacts when available
@@ -3449,6 +3474,7 @@ def get_help_for (cmdname, context="line") :
3449
3474
  name <name> : node name
3450
3475
  lat <lat> : latitude
3451
3476
  lon <lon> : longitude
3477
+ private_key : private key
3452
3478
  coords <lat,lon> : coordinates
3453
3479
  multi_ack <on/off> : multi-acks feature
3454
3480
  telemetry_mode_base <mode> : set basic telemetry mode all/selected/off
File without changes
File without changes
File without changes
File without changes
File without changes