meshcore-cli 1.3.8__tar.gz → 1.3.12__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.3.8/README.md → meshcore_cli-1.3.12/PKG-INFO +31 -0
- meshcore_cli-1.3.8/PKG-INFO → meshcore_cli-1.3.12/README.md +12 -19
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/flake.nix +2 -2
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/pyproject.toml +2 -2
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/src/meshcore_cli/meshcore_cli.py +120 -73
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/.gitignore +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/LICENSE +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/flake.lock +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/src/meshcore_cli/__init__.py +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.12}/src/meshcore_cli/__main__.py +0 -0
|
@@ -1,7 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meshcore-cli
|
|
3
|
+
Version: 1.3.12
|
|
4
|
+
Summary: Command line interface to meshcore companion radios
|
|
5
|
+
Project-URL: Homepage, https://github.com/fdlamotte/meshcore-cli
|
|
6
|
+
Project-URL: Issues, https://github.com/fdlamotte/meshcore-cli/issues
|
|
7
|
+
Author-email: Florent de Lamotte <florent@frizoncorrea.fr>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: bleak<2.0,>=0.22
|
|
14
|
+
Requires-Dist: meshcore>=2.2.3
|
|
15
|
+
Requires-Dist: prompt-toolkit>=3.0.50
|
|
16
|
+
Requires-Dist: pycryptodome
|
|
17
|
+
Requires-Dist: requests>=2.28.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
1
20
|
# meshcore-cli
|
|
2
21
|
|
|
3
22
|
meshcore-cli : CLI interface to MeschCore companion app over BLE, TCP or Serial
|
|
4
23
|
|
|
24
|
+
## About
|
|
25
|
+
|
|
26
|
+
meshcore-cli is a tool that connects to your companion radio node (meshcore client) over BLE, TCP or Serial and lets you interact with it from a terminal using a command line interface.
|
|
27
|
+
|
|
28
|
+
You can send commands as parameters to the meshcore-cli command (from your shell) either interactively or through a script.
|
|
29
|
+
|
|
30
|
+
There is also an interactive mode (this is the default when no command is passed). In interactive mode you can enter a contact (another client a repeater, a sensor or a room) and interact with it. For clients, interaction consists in sending/receiving messages. For repeaters, rooms or sensors it will directly give you the remote cli (you can still send messages to rooms using double quote prefix or msg command).
|
|
31
|
+
|
|
32
|
+
Note that meshcore-cli only interacts with companion radios (through BLE, Serial or TCP), you can't connect to a repeater using its serial interface.
|
|
33
|
+
|
|
34
|
+
Also, most meshcore companions only have one interface compiled in at a time. So you can't connect via Serial to a node, which has been compiled as a BLE companion.
|
|
35
|
+
|
|
5
36
|
## Install
|
|
6
37
|
|
|
7
38
|
Meshcore-cli depends on the [python meshcore](https://github.com/fdlamotte/meshcore_py) package. You can install both via `pip` or `pipx` using the command:
|
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: meshcore-cli
|
|
3
|
-
Version: 1.3.8
|
|
4
|
-
Summary: Command line interface to meshcore companion radios
|
|
5
|
-
Project-URL: Homepage, https://github.com/fdlamotte/meshcore-cli
|
|
6
|
-
Project-URL: Issues, https://github.com/fdlamotte/meshcore-cli/issues
|
|
7
|
-
Author-email: Florent de Lamotte <florent@frizoncorrea.fr>
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Requires-Dist: bleak<2.0,>=0.22
|
|
14
|
-
Requires-Dist: meshcore>=2.2.2
|
|
15
|
-
Requires-Dist: prompt-toolkit>=3.0.50
|
|
16
|
-
Requires-Dist: pycryptodome
|
|
17
|
-
Requires-Dist: requests>=2.28.0
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
|
|
20
1
|
# meshcore-cli
|
|
21
2
|
|
|
22
3
|
meshcore-cli : CLI interface to MeschCore companion app over BLE, TCP or Serial
|
|
23
4
|
|
|
5
|
+
## About
|
|
6
|
+
|
|
7
|
+
meshcore-cli is a tool that connects to your companion radio node (meshcore client) over BLE, TCP or Serial and lets you interact with it from a terminal using a command line interface.
|
|
8
|
+
|
|
9
|
+
You can send commands as parameters to the meshcore-cli command (from your shell) either interactively or through a script.
|
|
10
|
+
|
|
11
|
+
There is also an interactive mode (this is the default when no command is passed). In interactive mode you can enter a contact (another client a repeater, a sensor or a room) and interact with it. For clients, interaction consists in sending/receiving messages. For repeaters, rooms or sensors it will directly give you the remote cli (you can still send messages to rooms using double quote prefix or msg command).
|
|
12
|
+
|
|
13
|
+
Note that meshcore-cli only interacts with companion radios (through BLE, Serial or TCP), you can't connect to a repeater using its serial interface.
|
|
14
|
+
|
|
15
|
+
Also, most meshcore companions only have one interface compiled in at a time. So you can't connect via Serial to a node, which has been compiled as a BLE companion.
|
|
16
|
+
|
|
24
17
|
## Install
|
|
25
18
|
|
|
26
19
|
Meshcore-cli depends on the [python meshcore](https://github.com/fdlamotte/meshcore_py) package. You can install both via `pip` or `pipx` using the command:
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
meshcore = python3Packages.buildPythonPackage rec {
|
|
19
19
|
pname = "meshcore";
|
|
20
|
-
version = "2.2.
|
|
20
|
+
version = "2.2.3";
|
|
21
21
|
pyproject = true;
|
|
22
22
|
|
|
23
23
|
src = python3Packages.fetchPypi {
|
|
24
24
|
inherit pname version;
|
|
25
|
-
sha256 = "sha256-
|
|
25
|
+
sha256 = "sha256-lmMflAlrNnfsc10J3CBxor9ftHK10bWyGTbjASJv82s=";
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
build-system = [ python3Packages.hatchling ];
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "meshcore-cli"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.12"
|
|
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.
|
|
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.
|
|
35
|
+
VERSION = "v1.3.12"
|
|
36
36
|
|
|
37
37
|
# default ble address is stored in a config file
|
|
38
38
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -72,6 +72,8 @@ ANSI_LIGHT_GREEN = "\033[0;92m"
|
|
|
72
72
|
ANSI_LIGHT_YELLOW = "\033[0;93m"
|
|
73
73
|
ANSI_LIGHT_GRAY="\033[0;38;5;247m"
|
|
74
74
|
ANSI_BGRAY="\033[1;38;5;247m"
|
|
75
|
+
ANSI_GRAY_BACK="\033[48;5;247m"
|
|
76
|
+
ANSI_RESET_BACK="\033[49m"
|
|
75
77
|
ANSI_ORANGE="\033[0;38;5;214m"
|
|
76
78
|
ANSI_BORANGE="\033[1;38;5;214m"
|
|
77
79
|
#ANSI_YELLOW="\033[0;38;5;226m"
|
|
@@ -82,8 +84,9 @@ ANSI_BYELLOW = "\033[1;33m"
|
|
|
82
84
|
#Unicode chars
|
|
83
85
|
# some possible symbols for prompts 🭬🬛🬗🭬🬛🬃🬗🭬🬛🬃🬗🬏🭀🭋🭨🮋
|
|
84
86
|
ARROW_HEAD = ""
|
|
85
|
-
SLASH_END = ""
|
|
86
|
-
SLASH_START = ""
|
|
87
|
+
SLASH_END = f"{ANSI_RESET_BACK}"
|
|
88
|
+
#SLASH_START = ""
|
|
89
|
+
SLASH_START = f"{ANSI_GRAY_BACK}"
|
|
87
90
|
INVERT_SLASH = False
|
|
88
91
|
|
|
89
92
|
def escape_ansi(line):
|
|
@@ -136,9 +139,9 @@ async def process_event_message(mc, ev, json_output, end="\n", above=False):
|
|
|
136
139
|
ts = data["sender_timestamp"]
|
|
137
140
|
if process_event_message.timestamp == "on":
|
|
138
141
|
if (abs(time.time()-ts) < 86400):
|
|
139
|
-
fmt = "%H:%
|
|
142
|
+
fmt = "%H:%M"
|
|
140
143
|
else:
|
|
141
|
-
fmt = "%y-%m-%d %H:%
|
|
144
|
+
fmt = "%y-%m-%d %H:%M"
|
|
142
145
|
else:
|
|
143
146
|
fmt = process_event_message.timestamp
|
|
144
147
|
path_str += f'{datetime.datetime.fromtimestamp(ts).strftime(fmt)},'
|
|
@@ -294,7 +297,7 @@ async def handle_log_rx(event):
|
|
|
294
297
|
chan_name = channel["channel_name"]
|
|
295
298
|
aes_key = bytes.fromhex(channel["channel_secret"])
|
|
296
299
|
cipher = AES.new(aes_key, AES.MODE_ECB)
|
|
297
|
-
message = cipher.decrypt(msg)[5:].decode("utf-8").strip("\x00")
|
|
300
|
+
message = cipher.decrypt(msg)[5:].decode("utf-8", "ignore").strip("\x00")
|
|
298
301
|
|
|
299
302
|
if chan_name != "" :
|
|
300
303
|
width = os.get_terminal_size().columns
|
|
@@ -324,7 +327,7 @@ async def handle_log_rx(event):
|
|
|
324
327
|
if flags & 0x40 > 0: #has feature2
|
|
325
328
|
adv_feat2 = pk_buf.read(2).hex()
|
|
326
329
|
if flags & 0x80 > 0: #has name
|
|
327
|
-
adv_name = pk_buf.read().decode("utf-8").strip("\x00")
|
|
330
|
+
adv_name = pk_buf.read().decode("utf-8", "ignore").strip("\x00")
|
|
328
331
|
|
|
329
332
|
if adv_name is None:
|
|
330
333
|
# try to get the name from the contact
|
|
@@ -334,14 +337,14 @@ async def handle_log_rx(event):
|
|
|
334
337
|
else:
|
|
335
338
|
adv_name = ct["adv_name"]
|
|
336
339
|
|
|
337
|
-
|
|
340
|
+
ts_str = ""
|
|
338
341
|
if process_event_message.timestamp != "" and process_event_message.timestamp != "off":
|
|
339
342
|
ts = adv_timestamp
|
|
340
343
|
if process_event_message.timestamp == "on":
|
|
341
344
|
if (abs(time.time()-ts) < 86400):
|
|
342
|
-
fmt = "%H:%
|
|
345
|
+
fmt = "%H:%M"
|
|
343
346
|
else:
|
|
344
|
-
fmt = "%y-%m-%d %H:%
|
|
347
|
+
fmt = "%y-%m-%d %H:%M"
|
|
345
348
|
else:
|
|
346
349
|
fmt = process_event_message.timestamp
|
|
347
350
|
ts_str = f' at {datetime.datetime.fromtimestamp(ts).strftime(fmt)}'
|
|
@@ -443,10 +446,16 @@ async def log_message(mc, msg):
|
|
|
443
446
|
ct = mc.get_contact_by_key_prefix(msg['pubkey_prefix'])
|
|
444
447
|
if ct is None:
|
|
445
448
|
msg["name"] = msg["pubkey_prefix"]
|
|
449
|
+
msg["sender"] = msg["pubkey_prefix"]
|
|
446
450
|
else:
|
|
447
451
|
msg["name"] = ct["adv_name"]
|
|
452
|
+
msg["sender"] = ct["adv_name"]
|
|
448
453
|
elif msg["type"] == "CHAN" :
|
|
449
|
-
|
|
454
|
+
if hasattr(mc, 'channels') :
|
|
455
|
+
msg["sender"] = mc.channels[msg['channel_idx']]["channel_name"]
|
|
456
|
+
else:
|
|
457
|
+
msg["sender"] = f"channel {msg['channel_idx']}"
|
|
458
|
+
msg["name"] = msg["sender"]
|
|
450
459
|
msg["timestamp"] = int(time.time())
|
|
451
460
|
|
|
452
461
|
with open(log_message.file, "a") as logfile:
|
|
@@ -539,6 +548,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
539
548
|
|
|
540
549
|
completion_list = {
|
|
541
550
|
"to" : to_list,
|
|
551
|
+
"/to" : to_list,
|
|
542
552
|
"public" : None,
|
|
543
553
|
"chan" : None,
|
|
544
554
|
}
|
|
@@ -599,6 +609,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
599
609
|
"lat" : None,
|
|
600
610
|
"lon" : None,
|
|
601
611
|
"coords" : None,
|
|
612
|
+
"private_key": None,
|
|
602
613
|
"print_snr" : {"on":None, "off": None},
|
|
603
614
|
"print_timestamp" : {"on":None, "off": None, "%Y:%M":None},
|
|
604
615
|
"json_msgs" : {"on":None, "off": None},
|
|
@@ -630,6 +641,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
630
641
|
"coords":None,
|
|
631
642
|
"lat":None,
|
|
632
643
|
"lon":None,
|
|
644
|
+
"private_key":None,
|
|
633
645
|
"print_snr":None,
|
|
634
646
|
"print_timestamp":None,
|
|
635
647
|
"json_msgs":None,
|
|
@@ -859,8 +871,6 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
|
|
|
859
871
|
|
|
860
872
|
await subscribe_to_msgs(mc, above=True)
|
|
861
873
|
|
|
862
|
-
handle_new_contact.print_new_contacts = True
|
|
863
|
-
|
|
864
874
|
try:
|
|
865
875
|
if os.path.isdir(MCCLI_CONFIG_DIR) :
|
|
866
876
|
our_history = FileHistory(MCCLI_HISTORY_FILE)
|
|
@@ -1000,6 +1010,9 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
|
|
|
1000
1010
|
except IndexError:
|
|
1001
1011
|
print(scope)
|
|
1002
1012
|
|
|
1013
|
+
elif line == "quit" or line == "q" or line == "/quit" or line == "/q" :
|
|
1014
|
+
break
|
|
1015
|
+
|
|
1003
1016
|
elif contact is None and (line.startswith("apply_to ") or line.startswith("at ")) or\
|
|
1004
1017
|
line.startswith("/apply_to ") or line.startswith("/at ") :
|
|
1005
1018
|
try:
|
|
@@ -1007,52 +1020,8 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
|
|
|
1007
1020
|
except IndexError:
|
|
1008
1021
|
logger.error(f"Error with apply_to command parameters")
|
|
1009
1022
|
|
|
1010
|
-
elif line.startswith("/")
|
|
1011
|
-
|
|
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:]
|
|
1023
|
+
elif line.startswith("to ") or line.startswith("/to "): # dest
|
|
1024
|
+
dest = line.split(" ", 1)[1]
|
|
1056
1025
|
if dest.startswith("\"") or dest.startswith("\'") : # if name starts with a quote
|
|
1057
1026
|
dest = shlex.split(dest)[0] # use shlex.split to get contact name between quotes
|
|
1058
1027
|
dest_scope = None
|
|
@@ -1098,14 +1067,55 @@ Some cmds have an help accessible with ?<cmd>. Do ?[Tab] to get a list.
|
|
|
1098
1067
|
if not dest_scope is None:
|
|
1099
1068
|
scope = await set_scope(mc, dest_scope)
|
|
1100
1069
|
|
|
1101
|
-
elif line == "to" :
|
|
1070
|
+
elif line == "to" or line == "/to" :
|
|
1102
1071
|
if contact is None :
|
|
1103
1072
|
print(mc.self_info['name'])
|
|
1104
1073
|
else:
|
|
1105
1074
|
print(contact["adv_name"])
|
|
1106
1075
|
|
|
1107
|
-
elif line
|
|
1108
|
-
|
|
1076
|
+
elif line.startswith("/") :
|
|
1077
|
+
path = line.split(" ", 1)[0]
|
|
1078
|
+
if path.count("/") == 1:
|
|
1079
|
+
args = line[1:].split(" ")
|
|
1080
|
+
dest = args[0]
|
|
1081
|
+
dest_scope = None
|
|
1082
|
+
if "%" in dest :
|
|
1083
|
+
dest_scope = dest.split("%")[-1]
|
|
1084
|
+
dest = dest[:-len(dest_scope)-1]
|
|
1085
|
+
await set_scope (mc, dest_scope)
|
|
1086
|
+
tct = mc.get_contact_by_name(dest)
|
|
1087
|
+
if len(args)>1 and not tct is None: # a contact, send a message
|
|
1088
|
+
if tct["type"] == 1 or tct["type"] == 3: # client or room
|
|
1089
|
+
last_ack = await msg_ack(mc, tct, line.split(" ", 1)[1])
|
|
1090
|
+
else:
|
|
1091
|
+
print("Can only send msg to chan, client or room")
|
|
1092
|
+
else :
|
|
1093
|
+
ch = await get_channel_by_name(mc, dest)
|
|
1094
|
+
if len(args)>1 and not ch is None: # a channel, send message
|
|
1095
|
+
await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
|
|
1096
|
+
else :
|
|
1097
|
+
try :
|
|
1098
|
+
await process_cmds(mc, shlex.split(line[1:]))
|
|
1099
|
+
except ValueError:
|
|
1100
|
+
logger.error(f"Error processing line{line[1:]}")
|
|
1101
|
+
else:
|
|
1102
|
+
cmdline = line[1:].split("/",1)[1]
|
|
1103
|
+
contact_name = path[1:].split("/",1)[0]
|
|
1104
|
+
dest_scope = None
|
|
1105
|
+
if "%" in contact_name:
|
|
1106
|
+
dest_scope = contact_name.split("%")[-1]
|
|
1107
|
+
contact_name = contact_name[:-len(dest_scope)-1]
|
|
1108
|
+
await set_scope (mc, dest_scope)
|
|
1109
|
+
tct = mc.get_contact_by_name(contact_name)
|
|
1110
|
+
if tct is None:
|
|
1111
|
+
print(f"{contact_name} is not a contact")
|
|
1112
|
+
else:
|
|
1113
|
+
if not await process_contact_chat_line(mc, tct, cmdline):
|
|
1114
|
+
if cmdline != "":
|
|
1115
|
+
if tct["type"] == 1:
|
|
1116
|
+
last_ack = await msg_ack(mc, tct, cmdline)
|
|
1117
|
+
else :
|
|
1118
|
+
await process_cmds(mc, ["cmd", tct["adv_name"], cmdline])
|
|
1109
1119
|
|
|
1110
1120
|
# commands that take one parameter (don't need quotes)
|
|
1111
1121
|
elif line.startswith("public ") :
|
|
@@ -1537,10 +1547,11 @@ async def send_cmd (mc, contact, cmd) :
|
|
|
1537
1547
|
if isinstance(contact, dict):
|
|
1538
1548
|
sent = res.payload.copy()
|
|
1539
1549
|
sent["type"] = "SENT_CMD"
|
|
1540
|
-
sent["
|
|
1550
|
+
sent["recipient"] = contact["adv_name"]
|
|
1541
1551
|
sent["text"] = cmd
|
|
1542
1552
|
sent["txt_type"] = 1
|
|
1543
|
-
sent["
|
|
1553
|
+
sent["sender"] = mc.self_info['name']
|
|
1554
|
+
sent["name"] = sent["recipient"]
|
|
1544
1555
|
await log_message(mc, sent)
|
|
1545
1556
|
return res
|
|
1546
1557
|
|
|
@@ -1550,9 +1561,16 @@ async def send_chan_msg(mc, nb, msg):
|
|
|
1550
1561
|
sent = res.payload.copy()
|
|
1551
1562
|
sent["type"] = "SENT_CHAN"
|
|
1552
1563
|
sent["channel_idx"] = nb
|
|
1564
|
+
if hasattr(mc, "channels"):
|
|
1565
|
+
chan_name = mc.channels[nb]["channel_name"]
|
|
1566
|
+
else:
|
|
1567
|
+
chan_name = f"channel {nb}"
|
|
1568
|
+
sent["chan_name"] = chan_name
|
|
1569
|
+
sent["recipient"] = chan_name
|
|
1553
1570
|
sent["text"] = msg
|
|
1554
1571
|
sent["txt_type"] = 0
|
|
1555
|
-
sent["
|
|
1572
|
+
sent["sender"] = mc.self_info['name']
|
|
1573
|
+
sent["name"] = chan_name
|
|
1556
1574
|
await log_message(mc, sent)
|
|
1557
1575
|
return res
|
|
1558
1576
|
|
|
@@ -1563,10 +1581,11 @@ async def send_msg (mc, contact, msg) :
|
|
|
1563
1581
|
if isinstance(contact, dict):
|
|
1564
1582
|
sent = res.payload.copy()
|
|
1565
1583
|
sent["type"] = "SENT_MSG"
|
|
1566
|
-
sent["
|
|
1584
|
+
sent["recipient"] = contact["adv_name"]
|
|
1567
1585
|
sent["text"] = msg
|
|
1568
1586
|
sent["txt_type"] = 0
|
|
1569
|
-
sent["
|
|
1587
|
+
sent["sender"] = mc.self_info['name']
|
|
1588
|
+
sent["name"] = sent["recipient"]
|
|
1570
1589
|
await log_message(mc, sent)
|
|
1571
1590
|
return res
|
|
1572
1591
|
|
|
@@ -1583,10 +1602,11 @@ async def msg_ack (mc, contact, msg) :
|
|
|
1583
1602
|
if isinstance(contact, dict):
|
|
1584
1603
|
sent = res.payload.copy()
|
|
1585
1604
|
sent["type"] = "SENT_MSG"
|
|
1586
|
-
sent["
|
|
1605
|
+
sent["recipient"] = contact["adv_name"]
|
|
1587
1606
|
sent["text"] = msg
|
|
1588
1607
|
sent["txt_type"] = 0
|
|
1589
|
-
sent["
|
|
1608
|
+
sent["sender"] = mc.self_info['name']
|
|
1609
|
+
sent["name"] = sent["recipient"]
|
|
1590
1610
|
await log_message(mc, sent)
|
|
1591
1611
|
return not res is None
|
|
1592
1612
|
msg_ack.max_attempts=3
|
|
@@ -1604,8 +1624,11 @@ async def set_scope (mc, scope) :
|
|
|
1604
1624
|
return scope
|
|
1605
1625
|
|
|
1606
1626
|
res = await mc.commands.set_flood_scope(scope)
|
|
1607
|
-
if res is None
|
|
1608
|
-
|
|
1627
|
+
if res is None :
|
|
1628
|
+
return None
|
|
1629
|
+
|
|
1630
|
+
if res.type == EventType.ERROR:
|
|
1631
|
+
if "error_code" in res.payload and res.payload["error_code"] == 1: #unsupported
|
|
1609
1632
|
set_scope.has_scope = False
|
|
1610
1633
|
return None
|
|
1611
1634
|
|
|
@@ -2073,6 +2096,16 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2073
2096
|
print(json.dumps(res.payload, indent=4))
|
|
2074
2097
|
else:
|
|
2075
2098
|
print("ok")
|
|
2099
|
+
case "private_key":
|
|
2100
|
+
params=bytes.fromhex(cmds[2])
|
|
2101
|
+
res = await mc.commands.import_private_key(params)
|
|
2102
|
+
logger.debug(res)
|
|
2103
|
+
if res.type == EventType.ERROR:
|
|
2104
|
+
print(f"Error: {res}")
|
|
2105
|
+
elif json_output :
|
|
2106
|
+
print(json.dumps(res.payload, indent=4))
|
|
2107
|
+
else:
|
|
2108
|
+
print("ok")
|
|
2076
2109
|
case "tuning":
|
|
2077
2110
|
params=cmds[2].commands.split(",")
|
|
2078
2111
|
res = await mc.commands.set_tuning(
|
|
@@ -2285,6 +2318,16 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2285
2318
|
print(json.dumps(res.payload, indent=4))
|
|
2286
2319
|
else:
|
|
2287
2320
|
print(f"Battery level : {res.payload['level']}")
|
|
2321
|
+
case "private_key":
|
|
2322
|
+
res = await mc.commands.export_private_key()
|
|
2323
|
+
logger.debug(res)
|
|
2324
|
+
if res.type == EventType.ERROR:
|
|
2325
|
+
print(f"Error exporting private key {res}")
|
|
2326
|
+
elif json_output :
|
|
2327
|
+
res.payload["private_key"] = res.payload["private_key"].hex()
|
|
2328
|
+
print(json.dumps(res.payload))
|
|
2329
|
+
else:
|
|
2330
|
+
print(f"Private key: {res.payload['private_key'].hex()}")
|
|
2288
2331
|
case "fstats" :
|
|
2289
2332
|
res = await mc.commands.get_bat()
|
|
2290
2333
|
logger.debug(res)
|
|
@@ -2476,7 +2519,9 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2476
2519
|
if cmds[1].isnumeric() :
|
|
2477
2520
|
nb = int(cmds[1])
|
|
2478
2521
|
else:
|
|
2479
|
-
|
|
2522
|
+
chan = await get_channel_by_name(mc, cmds[1])
|
|
2523
|
+
print (chan)
|
|
2524
|
+
nb = chan['channel_idx']
|
|
2480
2525
|
res = await send_chan_msg(mc, nb, cmds[2])
|
|
2481
2526
|
logger.debug(res)
|
|
2482
2527
|
if res.type == EventType.ERROR:
|
|
@@ -3431,6 +3476,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3431
3476
|
lon : longitude
|
|
3432
3477
|
radio : radio parameters
|
|
3433
3478
|
tx : tx power
|
|
3479
|
+
private_key : private key of the node
|
|
3434
3480
|
print_snr : snr display in messages
|
|
3435
3481
|
print_adverts : display adverts as they come
|
|
3436
3482
|
print_new_contacts : display new pending contacts when available
|
|
@@ -3449,6 +3495,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3449
3495
|
name <name> : node name
|
|
3450
3496
|
lat <lat> : latitude
|
|
3451
3497
|
lon <lon> : longitude
|
|
3498
|
+
private_key : private key
|
|
3452
3499
|
coords <lat,lon> : coordinates
|
|
3453
3500
|
multi_ack <on/off> : multi-acks feature
|
|
3454
3501
|
telemetry_mode_base <mode> : set basic telemetry mode all/selected/off
|
|
@@ -3468,7 +3515,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3468
3515
|
json_log_rx <on/off> : logs packets incoming to device as json
|
|
3469
3516
|
channel_echoes <on/off> : print repeats for channel data
|
|
3470
3517
|
advert_echoes <on/off> : print repeats for adverts
|
|
3471
|
-
|
|
3518
|
+
echo_unk_chans <on/off> : also dump unk channels (encrypted)
|
|
3472
3519
|
color <on/off> : color off should remove ANSI codes from output
|
|
3473
3520
|
meshcore-cli behaviour:
|
|
3474
3521
|
classic_prompt <on/off> : activates less fancier prompt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|