meshcore-cli 1.2.1__tar.gz → 1.2.5__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.
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/PKG-INFO +2 -1
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/pyproject.toml +2 -2
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/src/meshcore_cli/meshcore_cli.py +176 -23
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/.gitignore +0 -0
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/LICENSE +0 -0
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/README.md +0 -0
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/flake.lock +0 -0
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/flake.nix +0 -0
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/src/meshcore_cli/__init__.py +0 -0
- {meshcore_cli-1.2.1 → meshcore_cli-1.2.5}/src/meshcore_cli/__main__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
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
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Requires-Dist: meshcore>=2.1.19
|
|
14
14
|
Requires-Dist: prompt-toolkit>=3.0.50
|
|
15
|
+
Requires-Dist: pycryptodome
|
|
15
16
|
Requires-Dist: requests>=2.28.0
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
17
18
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "meshcore-cli"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.5"
|
|
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.1.19", "prompt_toolkit >= 3.0.50", "requests >= 2.28.0" ]
|
|
20
|
+
dependencies = [ "meshcore >= 2.1.19", "prompt_toolkit >= 3.0.50", "requests >= 2.28.0", "pycryptodome" ]
|
|
21
21
|
|
|
22
22
|
[project.urls]
|
|
23
23
|
Homepage = "https://github.com/fdlamotte/meshcore-cli"
|
|
@@ -24,13 +24,16 @@ from prompt_toolkit.key_binding import KeyBindings
|
|
|
24
24
|
from prompt_toolkit.shortcuts import radiolist_dialog
|
|
25
25
|
from prompt_toolkit.completion.word_completer import WordCompleter
|
|
26
26
|
from prompt_toolkit.document import Document
|
|
27
|
+
from hashlib import sha256
|
|
28
|
+
from Crypto.Cipher import AES
|
|
29
|
+
from Crypto.Hash import HMAC, SHA256
|
|
27
30
|
|
|
28
31
|
import re
|
|
29
32
|
|
|
30
33
|
from meshcore import MeshCore, EventType, logger
|
|
31
34
|
|
|
32
35
|
# Version
|
|
33
|
-
VERSION = "v1.2.
|
|
36
|
+
VERSION = "v1.2.5"
|
|
34
37
|
|
|
35
38
|
# default ble address is stored in a config file
|
|
36
39
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -52,6 +55,7 @@ ANSI_INVERT = "\033[7m"
|
|
|
52
55
|
ANSI_NORMAL = "\033[27m"
|
|
53
56
|
ANSI_GREEN = "\033[0;32m"
|
|
54
57
|
ANSI_BGREEN = "\033[1;32m"
|
|
58
|
+
ANSI_DGREEN="\033[0;38;5;22m"
|
|
55
59
|
ANSI_BLUE = "\033[0;34m"
|
|
56
60
|
ANSI_BBLUE = "\033[1;34m"
|
|
57
61
|
ANSI_RED = "\033[0;31m"
|
|
@@ -193,6 +197,60 @@ process_event_message.print_snr=False
|
|
|
193
197
|
process_event_message.color=True
|
|
194
198
|
process_event_message.last_node=None
|
|
195
199
|
|
|
200
|
+
async def handle_log_rx(event):
|
|
201
|
+
mc = handle_log_rx.mc
|
|
202
|
+
if handle_log_rx.json_log_rx: # json mode ... raw dump
|
|
203
|
+
msg = json.dumps(event.payload)
|
|
204
|
+
if handle_message.above:
|
|
205
|
+
print_above(msg)
|
|
206
|
+
else :
|
|
207
|
+
print(msg)
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
pkt = bytes().fromhex(event.payload["payload"])
|
|
211
|
+
|
|
212
|
+
if handle_log_rx.channel_echoes:
|
|
213
|
+
if pkt[0] == 0x15:
|
|
214
|
+
chan_name = ""
|
|
215
|
+
path_len = pkt[1]
|
|
216
|
+
path = pkt[2:path_len+2].hex()
|
|
217
|
+
chan_hash = pkt[path_len+2:path_len+3].hex()
|
|
218
|
+
cipher_mac = pkt[path_len+3:path_len+5]
|
|
219
|
+
msg = pkt[path_len+5:]
|
|
220
|
+
channel = None
|
|
221
|
+
for c in await get_channels(mc):
|
|
222
|
+
if c["channel_hash"] == chan_hash : # validate against MAC
|
|
223
|
+
h = HMAC.new(bytes.fromhex(c["channel_secret"]), digestmod=SHA256)
|
|
224
|
+
h.update(msg)
|
|
225
|
+
if h.digest()[0:2] == cipher_mac:
|
|
226
|
+
channel = c
|
|
227
|
+
break
|
|
228
|
+
|
|
229
|
+
if channel is None :
|
|
230
|
+
if handle_log_rx.echo_unk_chans:
|
|
231
|
+
chan_name = chan_hash
|
|
232
|
+
message = msg.hex()
|
|
233
|
+
else:
|
|
234
|
+
chan_name = channel["channel_name"]
|
|
235
|
+
aes_key = bytes.fromhex(channel["channel_secret"])
|
|
236
|
+
cipher = AES.new(aes_key, AES.MODE_ECB)
|
|
237
|
+
message = cipher.decrypt(msg)[5:].decode("utf-8").strip("\x00")
|
|
238
|
+
|
|
239
|
+
if chan_name != "" :
|
|
240
|
+
width = os.get_terminal_size().columns
|
|
241
|
+
cars = width - 13 - 2 * path_len - len(chan_name) - 1
|
|
242
|
+
dispmsg = message[0:cars]
|
|
243
|
+
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}"
|
|
244
|
+
if handle_message.above:
|
|
245
|
+
print_above(txt)
|
|
246
|
+
else:
|
|
247
|
+
print(txt)
|
|
248
|
+
|
|
249
|
+
handle_log_rx.json_log_rx = False
|
|
250
|
+
handle_log_rx.channel_echoes = False
|
|
251
|
+
handle_log_rx.mc = None
|
|
252
|
+
handle_log_rx.echo_unk_chans=False
|
|
253
|
+
|
|
196
254
|
async def handle_advert(event):
|
|
197
255
|
if not handle_advert.print_adverts:
|
|
198
256
|
return
|
|
@@ -320,7 +378,7 @@ class MyNestedCompleter(NestedCompleter):
|
|
|
320
378
|
opts = self.options.keys()
|
|
321
379
|
completer = WordCompleter(
|
|
322
380
|
opts, ignore_case=self.ignore_case,
|
|
323
|
-
pattern=re.compile(r"([a-zA-Z0-9_
|
|
381
|
+
pattern=re.compile(r"([a-zA-Z0-9_\\/\#]+|[^a-zA-Z0-9_\s\#]+)"))
|
|
324
382
|
yield from completer.get_completions(document, complete_event)
|
|
325
383
|
else: # normal behavior for remainder
|
|
326
384
|
yield from super().get_completions(document, complete_event)
|
|
@@ -341,6 +399,10 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
341
399
|
for c in it :
|
|
342
400
|
contact_list[c[1]['adv_name']] = None
|
|
343
401
|
|
|
402
|
+
pit = iter(pending.items())
|
|
403
|
+
for c in pit :
|
|
404
|
+
pending_list[c[1]['adv_name']] = None
|
|
405
|
+
|
|
344
406
|
pit = iter(pending.items())
|
|
345
407
|
for c in pit :
|
|
346
408
|
pending_list[c[1]['public_key']] = None
|
|
@@ -417,6 +479,9 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
417
479
|
"color" : {"on":None, "off":None},
|
|
418
480
|
"print_name" : {"on":None, "off":None},
|
|
419
481
|
"print_adverts" : {"on":None, "off":None},
|
|
482
|
+
"json_log_rx" : {"on":None, "off":None},
|
|
483
|
+
"channel_echoes" : {"on":None, "off":None},
|
|
484
|
+
"echo_unk_chans" : {"on":None, "off":None},
|
|
420
485
|
"print_new_contacts" : {"on": None, "off":None},
|
|
421
486
|
"print_path_updates" : {"on":None,"off":None},
|
|
422
487
|
"classic_prompt" : {"on" : None, "off":None},
|
|
@@ -444,6 +509,9 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
444
509
|
"color":None,
|
|
445
510
|
"print_name":None,
|
|
446
511
|
"print_adverts":None,
|
|
512
|
+
"json_log_rx":None,
|
|
513
|
+
"channel_echoes":None,
|
|
514
|
+
"echo_unk_chans":None,
|
|
447
515
|
"print_path_updates":None,
|
|
448
516
|
"print_new_contacts":None,
|
|
449
517
|
"classic_prompt":None,
|
|
@@ -604,6 +672,14 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
604
672
|
|
|
605
673
|
completion_list.update(slash_contacts_completion_list)
|
|
606
674
|
|
|
675
|
+
slash_chan_completion_list = {}
|
|
676
|
+
if not channels is None:
|
|
677
|
+
for c in channels :
|
|
678
|
+
if c["channel_name"] != "":
|
|
679
|
+
slash_chan_completion_list["/" + c["channel_name"]] = None
|
|
680
|
+
|
|
681
|
+
completion_list.update(slash_chan_completion_list)
|
|
682
|
+
|
|
607
683
|
completion_list.update({
|
|
608
684
|
"script" : None,
|
|
609
685
|
"quit" : None
|
|
@@ -675,7 +751,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
675
751
|
if classic :
|
|
676
752
|
prompt = prompt + " > "
|
|
677
753
|
else :
|
|
678
|
-
prompt = prompt + "
|
|
754
|
+
prompt = prompt + f"{ANSI_NORMAL}🭬{ANSI_INVERT}"
|
|
679
755
|
|
|
680
756
|
if not contact is None :
|
|
681
757
|
if not last_ack:
|
|
@@ -696,7 +772,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
696
772
|
prompt = prompt + f"{ANSI_INVERT}"
|
|
697
773
|
|
|
698
774
|
if print_name and not classic :
|
|
699
|
-
prompt = prompt + "
|
|
775
|
+
prompt = prompt + f"{ANSI_NORMAL}🭨{ANSI_INVERT}"
|
|
700
776
|
|
|
701
777
|
prompt = prompt + f"{contact['adv_name']}"
|
|
702
778
|
if classic :
|
|
@@ -728,14 +804,31 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
728
804
|
|
|
729
805
|
# raw meshcli command as on command line
|
|
730
806
|
elif line.startswith("$") :
|
|
731
|
-
|
|
732
|
-
|
|
807
|
+
try :
|
|
808
|
+
args = shlex.split(line[1:])
|
|
809
|
+
await process_cmds(mc, args)
|
|
810
|
+
except ValueError:
|
|
811
|
+
logger.error("Error parsing line {line[1:]}")
|
|
733
812
|
|
|
734
813
|
elif line.startswith("/") :
|
|
735
814
|
path = line.split(" ", 1)[0]
|
|
736
815
|
if path.count("/") == 1:
|
|
737
|
-
args =
|
|
738
|
-
|
|
816
|
+
args = line[1:].split(" ")
|
|
817
|
+
tct = mc.get_contact_by_name(args[0])
|
|
818
|
+
if len(args)>1 and not tct is None: # a contact, send a message
|
|
819
|
+
if tct["type"] == 1 or tct["type"] == 3: # client or room
|
|
820
|
+
last_ack = await msg_ack(mc, tct, line.split(" ", 1)[1])
|
|
821
|
+
else:
|
|
822
|
+
print("Can only send msg to chan, client or room")
|
|
823
|
+
else :
|
|
824
|
+
ch = await get_channel_by_name(mc, args[0])
|
|
825
|
+
if len(args)>1 and not ch is None: # a channel, send message
|
|
826
|
+
await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
|
|
827
|
+
else :
|
|
828
|
+
try :
|
|
829
|
+
await process_cmds(mc, shlex.split(line[1:]))
|
|
830
|
+
except ValueError:
|
|
831
|
+
logger.error(f"Error processing line{line[1:]}")
|
|
739
832
|
else:
|
|
740
833
|
cmdline = line[1:].split("/",1)[1]
|
|
741
834
|
contact_name = path[1:].split("/",1)[0]
|
|
@@ -744,10 +837,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
744
837
|
print(f"{contact_name} is not a contact")
|
|
745
838
|
else:
|
|
746
839
|
if not await process_contact_chat_line(mc, tct, cmdline):
|
|
747
|
-
if
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
840
|
+
if cmdline != "":
|
|
841
|
+
if tct["type"] == 1:
|
|
842
|
+
last_ack = await msg_ack(mc, tct, cmdline)
|
|
843
|
+
else :
|
|
844
|
+
await process_cmds(mc, ["cmd", tct["adv_name"], cmdline])
|
|
751
845
|
|
|
752
846
|
elif line.startswith("to ") : # dest
|
|
753
847
|
dest = line[3:]
|
|
@@ -805,7 +899,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
805
899
|
if ln is None :
|
|
806
900
|
print("No received msg yet !")
|
|
807
901
|
elif ln["type"] == 0 :
|
|
808
|
-
await
|
|
902
|
+
await send_chan_msg(mc, ln["chan_nb"], line[1:])
|
|
809
903
|
else :
|
|
810
904
|
last_ack = await msg_ack(mc, ln, line[1:])
|
|
811
905
|
if last_ack == False :
|
|
@@ -813,8 +907,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
813
907
|
|
|
814
908
|
# commands are passed through if at root
|
|
815
909
|
elif contact is None or line.startswith(".") :
|
|
816
|
-
|
|
817
|
-
|
|
910
|
+
try:
|
|
911
|
+
args = shlex.split(line)
|
|
912
|
+
await process_cmds(mc, args)
|
|
913
|
+
except ValueError:
|
|
914
|
+
logger.error(f"Error processing {line}")
|
|
818
915
|
|
|
819
916
|
elif await process_contact_chat_line(mc, contact, line):
|
|
820
917
|
pass
|
|
@@ -837,7 +934,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
837
934
|
last_ack = await msg_ack(mc, contact, line)
|
|
838
935
|
|
|
839
936
|
elif contact["type"] == 0 : # channel, send msg to channel
|
|
840
|
-
await
|
|
937
|
+
await send_chan_msg(mc, contact["chan_nb"], line)
|
|
841
938
|
|
|
842
939
|
elif contact["type"] == 1 : # chat, send to recipient and wait ack
|
|
843
940
|
last_ack = await msg_ack(mc, contact, line)
|
|
@@ -970,7 +1067,18 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
970
1067
|
password_file = ""
|
|
971
1068
|
password = ""
|
|
972
1069
|
if os.path.isdir(MCCLI_CONFIG_DIR) :
|
|
1070
|
+
# if a password file exists with node name open it and destroy it
|
|
973
1071
|
password_file = MCCLI_CONFIG_DIR + contact['adv_name'] + ".pass"
|
|
1072
|
+
if os.path.exists(password_file) :
|
|
1073
|
+
with open(password_file, "r", encoding="utf-8") as f :
|
|
1074
|
+
password=f.readline().strip()
|
|
1075
|
+
os.remove(password_file)
|
|
1076
|
+
password_file = MCCLI_CONFIG_DIR + contact["public_key"] + ".pass"
|
|
1077
|
+
with open(password_file, "w", encoding="utf-8") as f :
|
|
1078
|
+
f.write(password)
|
|
1079
|
+
|
|
1080
|
+
# this is the new correct password file, using pubkey
|
|
1081
|
+
password_file = MCCLI_CONFIG_DIR + contact["public_key"] + ".pass"
|
|
974
1082
|
if os.path.exists(password_file) :
|
|
975
1083
|
with open(password_file, "r", encoding="utf-8") as f :
|
|
976
1084
|
password=f.readline().strip()
|
|
@@ -993,6 +1101,9 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
993
1101
|
|
|
994
1102
|
if line.startswith("forget_password") or line.startswith("fp"):
|
|
995
1103
|
password_file = MCCLI_CONFIG_DIR + contact['adv_name'] + ".pass"
|
|
1104
|
+
if os.path.exists(password_file):
|
|
1105
|
+
os.remove(password_file)
|
|
1106
|
+
password_file = MCCLI_CONFIG_DIR + contact['public_key'] + ".pass"
|
|
996
1107
|
if os.path.exists(password_file):
|
|
997
1108
|
os.remove(password_file)
|
|
998
1109
|
try:
|
|
@@ -1112,6 +1223,7 @@ async def set_channel (mc, chan, name, key=None):
|
|
|
1112
1223
|
return None
|
|
1113
1224
|
|
|
1114
1225
|
info = res.payload
|
|
1226
|
+
info["channel_hash"] = sha256(info["channel_secret"]).digest()[0:1].hex()
|
|
1115
1227
|
info["channel_secret"] = info["channel_secret"].hex()
|
|
1116
1228
|
|
|
1117
1229
|
if hasattr(mc,'channels') :
|
|
@@ -1200,12 +1312,14 @@ async def get_channels (mc, anim=False) :
|
|
|
1200
1312
|
if res.type == EventType.ERROR:
|
|
1201
1313
|
break
|
|
1202
1314
|
info = res.payload
|
|
1315
|
+
info["channel_hash"] = sha256(info["channel_secret"]).digest()[0:1].hex()
|
|
1203
1316
|
info["channel_secret"] = info["channel_secret"].hex()
|
|
1204
1317
|
mc.channels.append(info)
|
|
1205
1318
|
ch = ch + 1
|
|
1206
1319
|
if anim:
|
|
1207
1320
|
print(".", end="", flush=True)
|
|
1208
|
-
|
|
1321
|
+
if anim:
|
|
1322
|
+
print (" Done")
|
|
1209
1323
|
return mc.channels
|
|
1210
1324
|
|
|
1211
1325
|
async def print_trace_to (mc, contact):
|
|
@@ -1411,6 +1525,18 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1411
1525
|
process_event_message.print_snr = (cmds[2] == "on")
|
|
1412
1526
|
if json_output :
|
|
1413
1527
|
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1528
|
+
case "json_log_rx" :
|
|
1529
|
+
handle_log_rx.json_log_rx = (cmds[2] == "on")
|
|
1530
|
+
if json_output :
|
|
1531
|
+
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1532
|
+
case "channel_echoes" :
|
|
1533
|
+
handle_log_rx.channel_echoes = (cmds[2] == "on")
|
|
1534
|
+
if json_output :
|
|
1535
|
+
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1536
|
+
case "echo_unk_chans" :
|
|
1537
|
+
handle_log_rx.echo_unk_chans = (cmds[2] == "on")
|
|
1538
|
+
if json_output :
|
|
1539
|
+
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1414
1540
|
case "print_adverts" :
|
|
1415
1541
|
handle_advert.print_adverts = (cmds[2] == "on")
|
|
1416
1542
|
if json_output :
|
|
@@ -1641,6 +1767,21 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1641
1767
|
print(json.dumps({"color" : process_event_message.color}))
|
|
1642
1768
|
else:
|
|
1643
1769
|
print(f"{'on' if process_event_message.color else 'off'}")
|
|
1770
|
+
case "json_log_rx":
|
|
1771
|
+
if json_output :
|
|
1772
|
+
print(json.dumps({"json_log_rx" : handle_log_rx.json_log_rx}))
|
|
1773
|
+
else:
|
|
1774
|
+
print(f"{'on' if handle_log_rx.json_log_rx else 'off'}")
|
|
1775
|
+
case "channel_echoes":
|
|
1776
|
+
if json_output :
|
|
1777
|
+
print(json.dumps({"channel_echoes" : handle_log_rx.channel_echoes}))
|
|
1778
|
+
else:
|
|
1779
|
+
print(f"{'on' if handle_log_rx.channel_echoes else 'off'}")
|
|
1780
|
+
case "echo_unk_chans":
|
|
1781
|
+
if json_output :
|
|
1782
|
+
print(json.dumps({"echo_unk_chans" : handle_log_rx.echo_unk_chans}))
|
|
1783
|
+
else:
|
|
1784
|
+
print(f"{'on' if handle_log_rx.echo_unk_chans else 'off'}")
|
|
1644
1785
|
case "print_adverts":
|
|
1645
1786
|
if json_output :
|
|
1646
1787
|
print(json.dumps({"print_adverts" : handle_advert.print_adverts}))
|
|
@@ -2232,6 +2373,13 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2232
2373
|
case "add_pending":
|
|
2233
2374
|
argnum = 1
|
|
2234
2375
|
contact = mc.pop_pending_contact(cmds[1])
|
|
2376
|
+
if contact is None: # try to find by name
|
|
2377
|
+
key = None
|
|
2378
|
+
for c in mc.pending_contacts.items():
|
|
2379
|
+
if c[1]['adv_name'] == cmds[1]:
|
|
2380
|
+
key = c[1]['public_key']
|
|
2381
|
+
contact = mc.pop_pending_contact(key)
|
|
2382
|
+
break
|
|
2235
2383
|
if contact is None:
|
|
2236
2384
|
if json_output:
|
|
2237
2385
|
print(json.dumps({"error":"Contact does not exist"}))
|
|
@@ -2511,7 +2659,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2511
2659
|
if json_output:
|
|
2512
2660
|
await ps.prompt_async()
|
|
2513
2661
|
else:
|
|
2514
|
-
await ps.prompt_async("Press Enter to continue
|
|
2662
|
+
await ps.prompt_async("Press Enter to continue ...\n")
|
|
2515
2663
|
except (EOFError, KeyboardInterrupt, asyncio.CancelledError):
|
|
2516
2664
|
pass
|
|
2517
2665
|
|
|
@@ -2570,7 +2718,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2570
2718
|
await mc.ensure_contacts()
|
|
2571
2719
|
contact = mc.get_contact_by_name(cmds[0])
|
|
2572
2720
|
if contact is None:
|
|
2573
|
-
logger.error(f"Unknown command : {cmd}
|
|
2721
|
+
logger.error(f"Unknown command : {cmd}. {cmds} not executed ...")
|
|
2574
2722
|
return None
|
|
2575
2723
|
|
|
2576
2724
|
await interactive_loop(mc, to=contact)
|
|
@@ -2579,7 +2727,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2579
2727
|
return cmds[argnum+1:]
|
|
2580
2728
|
|
|
2581
2729
|
except IndexError:
|
|
2582
|
-
logger.error("Error in parameters
|
|
2730
|
+
logger.error("Error in parameters")
|
|
2583
2731
|
return None
|
|
2584
2732
|
except EOFError:
|
|
2585
2733
|
logger.error("Cancelled")
|
|
@@ -2604,8 +2752,11 @@ async def process_script(mc, file, json_output=False):
|
|
|
2604
2752
|
line = line.strip()
|
|
2605
2753
|
if not (line == "" or line[0] == "#"):
|
|
2606
2754
|
logger.debug(f"processing {line}")
|
|
2607
|
-
|
|
2608
|
-
|
|
2755
|
+
try :
|
|
2756
|
+
cmds = shlex.split(line)
|
|
2757
|
+
await process_cmds(mc, cmds, json_output)
|
|
2758
|
+
except ValueError:
|
|
2759
|
+
logger.error(f"Error processing {line}")
|
|
2609
2760
|
|
|
2610
2761
|
def version():
|
|
2611
2762
|
print (f"meshcore-cli: command line interface to MeshCore companion radios {VERSION}")
|
|
@@ -2661,7 +2812,7 @@ def command_help():
|
|
|
2661
2812
|
req_mma <ct> : requests min/max/avg for a sensor rm
|
|
2662
2813
|
req_acl <ct> : requests access control list for sensor
|
|
2663
2814
|
pending_contacts : show pending contacts
|
|
2664
|
-
add_pending <
|
|
2815
|
+
add_pending <pending> : manually add pending contact
|
|
2665
2816
|
flush_pending : flush pending contact list
|
|
2666
2817
|
Repeaters
|
|
2667
2818
|
login <name> <pwd> : log into a node (rep) with given pwd l
|
|
@@ -2881,10 +3032,12 @@ async def main(argv):
|
|
|
2881
3032
|
handle_message.mc = mc # connect meshcore to handle_message
|
|
2882
3033
|
handle_advert.mc = mc
|
|
2883
3034
|
handle_path_update.mc = mc
|
|
3035
|
+
handle_log_rx.mc = mc
|
|
2884
3036
|
|
|
2885
3037
|
mc.subscribe(EventType.ADVERTISEMENT, handle_advert)
|
|
2886
3038
|
mc.subscribe(EventType.PATH_UPDATE, handle_path_update)
|
|
2887
3039
|
mc.subscribe(EventType.NEW_CONTACT, handle_new_contact)
|
|
3040
|
+
mc.subscribe(EventType.RX_LOG_DATA, handle_log_rx)
|
|
2888
3041
|
|
|
2889
3042
|
mc.auto_update_contacts = True
|
|
2890
3043
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|