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.
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/PKG-INFO +2 -2
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/pyproject.toml +2 -2
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/src/meshcore_cli/meshcore_cli.py +85 -59
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/.gitignore +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/LICENSE +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/README.md +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/flake.lock +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/flake.nix +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/src/meshcore_cli/__init__.py +0 -0
- {meshcore_cli-1.3.8 → meshcore_cli-1.3.11}/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.3.
|
|
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.
|
|
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.
|
|
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.
|
|
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.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:%
|
|
139
|
+
fmt = "%H:%M"
|
|
140
140
|
else:
|
|
141
|
-
fmt = "%y-%m-%d %H:%
|
|
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
|
-
|
|
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:%
|
|
342
|
+
fmt = "%H:%M"
|
|
343
343
|
else:
|
|
344
|
-
fmt = "%y-%m-%d %H:%
|
|
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
|
-
|
|
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
|
|
1108
|
-
|
|
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
|
|
1608
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|