meshcore-cli 1.2.2__py3-none-any.whl → 1.2.3__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.
@@ -26,13 +26,14 @@ from prompt_toolkit.completion.word_completer import WordCompleter
26
26
  from prompt_toolkit.document import Document
27
27
  from hashlib import sha256
28
28
  from Crypto.Cipher import AES
29
+ from Crypto.Hash import HMAC, SHA256
29
30
 
30
31
  import re
31
32
 
32
33
  from meshcore import MeshCore, EventType, logger
33
34
 
34
35
  # Version
35
- VERSION = "v1.2.2"
36
+ VERSION = "v1.2.3"
36
37
 
37
38
  # default ble address is stored in a config file
38
39
  MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
@@ -54,6 +55,7 @@ ANSI_INVERT = "\033[7m"
54
55
  ANSI_NORMAL = "\033[27m"
55
56
  ANSI_GREEN = "\033[0;32m"
56
57
  ANSI_BGREEN = "\033[1;32m"
58
+ ANSI_DGREEN="\033[0;38;5;22m"
57
59
  ANSI_BLUE = "\033[0;34m"
58
60
  ANSI_BBLUE = "\033[1;34m"
59
61
  ANSI_RED = "\033[0;31m"
@@ -207,14 +209,22 @@ async def handle_log_rx(event):
207
209
 
208
210
  pkt = bytes().fromhex(event.payload["payload"])
209
211
 
210
- if handle_log_rx.log_channels:
212
+ if handle_log_rx.channel_echoes:
211
213
  if pkt[0] == 0x15:
212
214
  path_len = pkt[1]
213
215
  path = pkt[2:path_len+2].hex()
214
216
  chan_hash = pkt[path_len+2:path_len+3].hex()
215
- cipher_mac = int.from_bytes(pkt[path_len+3:path_len+5], byteorder="little")
217
+ cipher_mac = pkt[path_len+3:path_len+5]
216
218
  msg = pkt[path_len+5:]
217
- channel = await get_channel_by_hash(mc, chan_hash)
219
+ channel = None
220
+ for c in await get_channels(mc):
221
+ if c["channel_hash"] == chan_hash : # validate against MAC
222
+ h = HMAC.new(bytes.fromhex(c["channel_secret"]), digestmod=SHA256)
223
+ h.update(msg)
224
+ if h.digest()[0:2] == cipher_mac:
225
+ channel = c
226
+ break
227
+
218
228
  if channel is None :
219
229
  chan_name = chan_hash
220
230
  message = msg.hex()
@@ -224,10 +234,18 @@ async def handle_log_rx(event):
224
234
  cipher = AES.new(aes_key, AES.MODE_ECB)
225
235
  message = cipher.decrypt(msg)[5:].decode("utf-8").strip("\x00")
226
236
 
227
- print_above(f"{ANSI_LIGHT_GRAY}{chan_name:>10} {ANSI_GREEN}{message[0:25]:25} {ANSI_LIGHT_GRAY}({event.payload['snr']:6,.2f},{event.payload['rssi']:4}){ANSI_YELLOW} [{path}]{ANSI_END}")
237
+ width = os.get_terminal_size().columns
238
+ cars = width - 13 - 2 * path_len - len(chan_name) - 1
239
+ dispmsg = message[0:cars]
240
+ txt = f"{ANSI_LIGHT_GRAY}{chan_name} {ANSI_DGREEN}{dispmsg+(cars-len(dispmsg))*" "} {ANSI_YELLOW}[{path}]{ANSI_LIGHT_GRAY}{event.payload['snr']:6,.2f}{event.payload['rssi']:4}{ANSI_END}"
241
+ if handle_message.above:
242
+ print_above(txt)
243
+ else:
244
+ print(txt)
245
+ return
228
246
 
229
247
  handle_log_rx.json_log_rx = False
230
- handle_log_rx.log_channels = False
248
+ handle_log_rx.channel_echoes = False
231
249
  handle_log_rx.mc = None
232
250
 
233
251
  async def handle_advert(event):
@@ -455,7 +473,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
455
473
  "print_name" : {"on":None, "off":None},
456
474
  "print_adverts" : {"on":None, "off":None},
457
475
  "json_log_rx" : {"on":None, "off":None},
458
- "log_channels" : {"on":None, "off":None},
476
+ "channel_echoes" : {"on":None, "off":None},
459
477
  "print_new_contacts" : {"on": None, "off":None},
460
478
  "print_path_updates" : {"on":None,"off":None},
461
479
  "classic_prompt" : {"on" : None, "off":None},
@@ -484,7 +502,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
484
502
  "print_name":None,
485
503
  "print_adverts":None,
486
504
  "json_log_rx":None,
487
- "log_channels":None,
505
+ "channel_echoes":None,
488
506
  "print_path_updates":None,
489
507
  "print_new_contacts":None,
490
508
  "classic_prompt":None,
@@ -777,8 +795,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
777
795
 
778
796
  # raw meshcli command as on command line
779
797
  elif line.startswith("$") :
780
- args = shlex.split(line[1:])
781
- await process_cmds(mc, args)
798
+ try :
799
+ args = shlex.split(line[1:])
800
+ await process_cmds(mc, args)
801
+ except ValueError:
802
+ logger.error("Error parsing line {line[1:]}")
782
803
 
783
804
  elif line.startswith("/") :
784
805
  path = line.split(" ", 1)[0]
@@ -795,7 +816,10 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
795
816
  if len(args)>1 and not ch is None: # a channel, send message
796
817
  await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
797
818
  else :
798
- await process_cmds(mc, shlex.split(line[1:]))
819
+ try :
820
+ await process_cmds(mc, shlex.split(line[1:]))
821
+ except ValueError:
822
+ logger.error(f"Error processing line{line[1:]}")
799
823
  else:
800
824
  cmdline = line[1:].split("/",1)[1]
801
825
  contact_name = path[1:].split("/",1)[0]
@@ -874,8 +898,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
874
898
 
875
899
  # commands are passed through if at root
876
900
  elif contact is None or line.startswith(".") :
877
- args = shlex.split(line)
878
- await process_cmds(mc, args)
901
+ try:
902
+ args = shlex.split(line)
903
+ await process_cmds(mc, args)
904
+ except ValueError:
905
+ logger.error(f"Error processing {line}")
879
906
 
880
907
  elif await process_contact_chat_line(mc, contact, line):
881
908
  pass
@@ -1205,16 +1232,6 @@ async def get_channel_by_name (mc, name):
1205
1232
 
1206
1233
  return None
1207
1234
 
1208
- async def get_channel_by_hash (mc, hash):
1209
- if not hasattr(mc, 'channels') :
1210
- await_get_channels(mc)
1211
-
1212
- for c in mc.channels:
1213
- if c['channel_hash'] == hash:
1214
- return c
1215
-
1216
- return None
1217
-
1218
1235
  async def get_contacts (mc, anim=False, lastomod=0, timeout=5) :
1219
1236
  if mc._contacts:
1220
1237
  return
@@ -1292,7 +1309,8 @@ async def get_channels (mc, anim=False) :
1292
1309
  ch = ch + 1
1293
1310
  if anim:
1294
1311
  print(".", end="", flush=True)
1295
- print (" Done")
1312
+ if anim:
1313
+ print (" Done")
1296
1314
  return mc.channels
1297
1315
 
1298
1316
  async def print_trace_to (mc, contact):
@@ -1502,8 +1520,8 @@ async def next_cmd(mc, cmds, json_output=False):
1502
1520
  handle_log_rx.json_log_rx = (cmds[2] == "on")
1503
1521
  if json_output :
1504
1522
  print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
1505
- case "log_channels" :
1506
- handle_log_rx.log_channels = (cmds[2] == "on")
1523
+ case "channel_echoes" :
1524
+ handle_log_rx.channel_echoes = (cmds[2] == "on")
1507
1525
  if json_output :
1508
1526
  print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
1509
1527
  case "print_adverts" :
@@ -1741,11 +1759,11 @@ async def next_cmd(mc, cmds, json_output=False):
1741
1759
  print(json.dumps({"json_log_rx" : handle_log_rx.json_log_rx}))
1742
1760
  else:
1743
1761
  print(f"{'on' if handle_log_rx.json_log_rx else 'off'}")
1744
- case "log_channels":
1762
+ case "channel_echoes":
1745
1763
  if json_output :
1746
- print(json.dumps({"log_channels" : handle_log_rx.log_channels}))
1764
+ print(json.dumps({"channel_echoes" : handle_log_rx.channel_echoes}))
1747
1765
  else:
1748
- print(f"{'on' if handle_log_rx.log_channels else 'off'}")
1766
+ print(f"{'on' if handle_log_rx.channel_echoes else 'off'}")
1749
1767
  case "print_adverts":
1750
1768
  if json_output :
1751
1769
  print(json.dumps({"print_adverts" : handle_advert.print_adverts}))
@@ -2616,7 +2634,7 @@ async def next_cmd(mc, cmds, json_output=False):
2616
2634
  if json_output:
2617
2635
  await ps.prompt_async()
2618
2636
  else:
2619
- await ps.prompt_async("Press Enter to continue ...")
2637
+ await ps.prompt_async("Press Enter to continue ...\n")
2620
2638
  except (EOFError, KeyboardInterrupt, asyncio.CancelledError):
2621
2639
  pass
2622
2640
 
@@ -2709,8 +2727,11 @@ async def process_script(mc, file, json_output=False):
2709
2727
  line = line.strip()
2710
2728
  if not (line == "" or line[0] == "#"):
2711
2729
  logger.debug(f"processing {line}")
2712
- cmds = shlex.split(line)
2713
- await process_cmds(mc, cmds, json_output)
2730
+ try :
2731
+ cmds = shlex.split(line)
2732
+ await process_cmds(mc, cmds, json_output)
2733
+ except ValueError:
2734
+ logger.error(f"Error processing {line}")
2714
2735
 
2715
2736
  def version():
2716
2737
  print (f"meshcore-cli: command line interface to MeshCore companion radios {VERSION}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcore-cli
3
- Version: 1.2.2
3
+ Version: 1.2.3
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
@@ -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=4vJusi30D_UBxw9lq4mTO5NqLp8y3zpTb09rCcF7064,126909
4
+ meshcore_cli-1.2.3.dist-info/METADATA,sha256=Z727GJ6CD7OzRfEUGose_dAfACX6Oo2IbM3kzBWihtc,11657
5
+ meshcore_cli-1.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
+ meshcore_cli-1.2.3.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
7
+ meshcore_cli-1.2.3.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
8
+ meshcore_cli-1.2.3.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=1HmybWFe78tqsbD4zjxHq4aCzTrxd_s7MwtrlKTiQ6c,125901
4
- meshcore_cli-1.2.2.dist-info/METADATA,sha256=VZygAExPzIUa2rPpHksZQHGxkj1awTiyHuSjpnKOOXk,11657
5
- meshcore_cli-1.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
- meshcore_cli-1.2.2.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
7
- meshcore_cli-1.2.2.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
8
- meshcore_cli-1.2.2.dist-info/RECORD,,