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.
- meshcore_cli/meshcore_cli.py +53 -32
- {meshcore_cli-1.2.2.dist-info → meshcore_cli-1.2.3.dist-info}/METADATA +1 -1
- meshcore_cli-1.2.3.dist-info/RECORD +8 -0
- meshcore_cli-1.2.2.dist-info/RECORD +0 -8
- {meshcore_cli-1.2.2.dist-info → meshcore_cli-1.2.3.dist-info}/WHEEL +0 -0
- {meshcore_cli-1.2.2.dist-info → meshcore_cli-1.2.3.dist-info}/entry_points.txt +0 -0
- {meshcore_cli-1.2.2.dist-info → meshcore_cli-1.2.3.dist-info}/licenses/LICENSE +0 -0
meshcore_cli/meshcore_cli.py
CHANGED
|
@@ -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.
|
|
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.
|
|
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 =
|
|
217
|
+
cipher_mac = pkt[path_len+3:path_len+5]
|
|
216
218
|
msg = pkt[path_len+5:]
|
|
217
|
-
channel =
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
781
|
-
|
|
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
|
-
|
|
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
|
-
|
|
878
|
-
|
|
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
|
-
|
|
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 "
|
|
1506
|
-
handle_log_rx.
|
|
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 "
|
|
1762
|
+
case "channel_echoes":
|
|
1745
1763
|
if json_output :
|
|
1746
|
-
print(json.dumps({"
|
|
1764
|
+
print(json.dumps({"channel_echoes" : handle_log_rx.channel_echoes}))
|
|
1747
1765
|
else:
|
|
1748
|
-
print(f"{'on' if handle_log_rx.
|
|
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
|
-
|
|
2713
|
-
|
|
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.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|