meshcore-cli 1.2.12__py3-none-any.whl → 1.3.0__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 +223 -144
- {meshcore_cli-1.2.12.dist-info → meshcore_cli-1.3.0.dist-info}/METADATA +114 -45
- meshcore_cli-1.3.0.dist-info/RECORD +8 -0
- meshcore_cli-1.2.12.dist-info/RECORD +0 -8
- {meshcore_cli-1.2.12.dist-info → meshcore_cli-1.3.0.dist-info}/WHEEL +0 -0
- {meshcore_cli-1.2.12.dist-info → meshcore_cli-1.3.0.dist-info}/entry_points.txt +0 -0
- {meshcore_cli-1.2.12.dist-info → meshcore_cli-1.3.0.dist-info}/licenses/LICENSE +0 -0
meshcore_cli/meshcore_cli.py
CHANGED
|
@@ -32,7 +32,7 @@ import re
|
|
|
32
32
|
from meshcore import MeshCore, EventType, logger
|
|
33
33
|
|
|
34
34
|
# Version
|
|
35
|
-
VERSION = "v1.
|
|
35
|
+
VERSION = "v1.3.0"
|
|
36
36
|
|
|
37
37
|
# default ble address is stored in a config file
|
|
38
38
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -76,13 +76,11 @@ ANSI_YELLOW = "\033[0;33m"
|
|
|
76
76
|
ANSI_BYELLOW = "\033[1;33m"
|
|
77
77
|
|
|
78
78
|
#Unicode chars
|
|
79
|
-
# some possible symbols for prompts
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
ARROW_TAIL = ""
|
|
85
|
-
ARROW_HEAD = " "
|
|
79
|
+
# some possible symbols for prompts 🭬🬛🬗🭬🬛🬃🬗🭬🬛🬃🬗🬏🭀🭋🭨🮋
|
|
80
|
+
ARROW_HEAD = ""
|
|
81
|
+
SLASH_END = ""
|
|
82
|
+
SLASH_START = ""
|
|
83
|
+
INVERT_SLASH = False
|
|
86
84
|
|
|
87
85
|
def escape_ansi(line):
|
|
88
86
|
ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
|
|
@@ -205,29 +203,62 @@ process_event_message.print_snr=False
|
|
|
205
203
|
process_event_message.color=True
|
|
206
204
|
process_event_message.last_node=None
|
|
207
205
|
|
|
206
|
+
PAYLOAD_TYPENAMES = ["REQ", "RESPONSE", "TEXT_MSG", "ACK", "ADVERT", "GRP_TXT", "GRP_DATA", "ANON_REQ", "PATH", "TRACE", "MULTIPART", "CONTROL"]
|
|
207
|
+
ROUTE_TYPENAMES = ["TC_FLOOD", "FLOOD", "DIRECT", "TC_DIRECT"]
|
|
208
|
+
|
|
208
209
|
async def handle_log_rx(event):
|
|
209
210
|
mc = handle_log_rx.mc
|
|
210
|
-
if handle_log_rx.json_log_rx: # json mode ... raw dump
|
|
211
|
-
msg = json.dumps(event.payload)
|
|
212
|
-
if handle_message.above:
|
|
213
|
-
print_above(msg)
|
|
214
|
-
else :
|
|
215
|
-
print(msg)
|
|
216
|
-
return
|
|
217
211
|
|
|
218
212
|
pkt = bytes().fromhex(event.payload["payload"])
|
|
219
213
|
pbuf = io.BytesIO(pkt)
|
|
220
214
|
header = pbuf.read(1)[0]
|
|
215
|
+
route_type = header & 0x03
|
|
216
|
+
payload_type = (header & 0x3c) >> 2
|
|
217
|
+
payload_ver = (header & 0xc0) >> 6
|
|
218
|
+
|
|
219
|
+
transport_code = None
|
|
220
|
+
if route_type == 0x00 or route_type == 0x03: # has transport code
|
|
221
|
+
transport_code = pbuf.read(4) # discard transport code
|
|
222
|
+
|
|
223
|
+
path_len = pbuf.read(1)[0]
|
|
224
|
+
path = pbuf.read(path_len).hex() # Beware of traces where pathes are mixed
|
|
225
|
+
|
|
226
|
+
try :
|
|
227
|
+
route_typename = ROUTE_TYPENAMES[route_type]
|
|
228
|
+
except IndexError:
|
|
229
|
+
logger.debug(f"Unknown route type {route_type}")
|
|
230
|
+
route_typename = "UNK"
|
|
231
|
+
|
|
232
|
+
try :
|
|
233
|
+
payload_typename = PAYLOAD_TYPENAMES[payload_type]
|
|
234
|
+
except IndexError:
|
|
235
|
+
logger.debug(f"Unknown payload type {payload_type}")
|
|
236
|
+
payload_typename = "UNK"
|
|
221
237
|
|
|
222
|
-
|
|
238
|
+
pkt_payload = pbuf.read()
|
|
239
|
+
|
|
240
|
+
event.payload["header"] = header
|
|
241
|
+
event.payload["route_type"] = route_type
|
|
242
|
+
event.payload["route_typename"] = route_typename
|
|
243
|
+
event.payload["payload_type"] = payload_type
|
|
244
|
+
event.payload["payload_typename"]= payload_typename
|
|
245
|
+
|
|
246
|
+
event.payload["payload_ver"] = payload_ver
|
|
247
|
+
|
|
248
|
+
if not transport_code is None:
|
|
249
|
+
event.payload["transport_code"] = transport_code.hex()
|
|
250
|
+
|
|
251
|
+
event.payload["path_len"] = path_len
|
|
252
|
+
event.payload["path"] = path
|
|
253
|
+
|
|
254
|
+
event.payload["pkt_payload"] = pkt_payload.hex()
|
|
255
|
+
|
|
256
|
+
if payload_type == 0x05: # flood msg / channel
|
|
223
257
|
if handle_log_rx.channel_echoes:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
chan_hash = pbuf.read(1).hex()
|
|
229
|
-
cipher_mac = pbuf.read(2)
|
|
230
|
-
msg = pbuf.read() # until the end of buffer
|
|
258
|
+
pk_buf = io.BytesIO(pkt_payload)
|
|
259
|
+
chan_hash = pk_buf.read(1).hex()
|
|
260
|
+
cipher_mac = pk_buf.read(2)
|
|
261
|
+
msg = pk_buf.read() # until the end of buffer
|
|
231
262
|
|
|
232
263
|
channel = None
|
|
233
264
|
for c in await get_channels(mc):
|
|
@@ -260,6 +291,14 @@ async def handle_log_rx(event):
|
|
|
260
291
|
else:
|
|
261
292
|
print(txt)
|
|
262
293
|
|
|
294
|
+
if handle_log_rx.json_log_rx: # json mode ... raw dump
|
|
295
|
+
msg = json.dumps(event.payload)
|
|
296
|
+
if handle_message.above:
|
|
297
|
+
print_above(msg)
|
|
298
|
+
else :
|
|
299
|
+
print(msg)
|
|
300
|
+
|
|
301
|
+
|
|
263
302
|
handle_log_rx.json_log_rx = False
|
|
264
303
|
handle_log_rx.channel_echoes = False
|
|
265
304
|
handle_log_rx.mc = None
|
|
@@ -470,7 +509,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
470
509
|
"login" : contact_list,
|
|
471
510
|
"cmd" : contact_list,
|
|
472
511
|
"req_status" : contact_list,
|
|
473
|
-
"
|
|
512
|
+
"req_neighbours": contact_list,
|
|
474
513
|
"logout" : contact_list,
|
|
475
514
|
"req_telemetry" : contact_list,
|
|
476
515
|
"req_binary" : contact_list,
|
|
@@ -495,7 +534,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
495
534
|
"print_snr" : {"on":None, "off": None},
|
|
496
535
|
"json_msgs" : {"on":None, "off": None},
|
|
497
536
|
"color" : {"on":None, "off":None},
|
|
498
|
-
"print_name" : {"on":None, "off":None},
|
|
499
537
|
"print_adverts" : {"on":None, "off":None},
|
|
500
538
|
"json_log_rx" : {"on":None, "off":None},
|
|
501
539
|
"channel_echoes" : {"on":None, "off":None},
|
|
@@ -525,7 +563,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
525
563
|
"print_snr":None,
|
|
526
564
|
"json_msgs":None,
|
|
527
565
|
"color":None,
|
|
528
|
-
"print_name":None,
|
|
529
566
|
"print_adverts":None,
|
|
530
567
|
"json_log_rx":None,
|
|
531
568
|
"channel_echoes":None,
|
|
@@ -577,7 +614,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
577
614
|
"login" : None,
|
|
578
615
|
"logout" : None,
|
|
579
616
|
"req_status" : None,
|
|
580
|
-
"req_bstatus" : None,
|
|
581
617
|
"req_neighbours": None,
|
|
582
618
|
"cmd" : None,
|
|
583
619
|
"ver" : None,
|
|
@@ -765,26 +801,32 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
765
801
|
|
|
766
802
|
color = process_event_message.color
|
|
767
803
|
classic = interactive_loop.classic or not color
|
|
768
|
-
print_name = interactive_loop.print_name
|
|
769
804
|
|
|
770
805
|
if classic:
|
|
771
806
|
prompt = ""
|
|
772
807
|
else:
|
|
773
808
|
prompt = f"{ANSI_INVERT}"
|
|
774
809
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
810
|
+
prompt = prompt + f"{ANSI_BGRAY}"
|
|
811
|
+
prompt = prompt + f"{mc.self_info['name']}"
|
|
812
|
+
if contact is None: # display scope
|
|
813
|
+
if not scope is None:
|
|
814
|
+
prompt = prompt + f"|{scope}"
|
|
815
|
+
|
|
816
|
+
if contact is None :
|
|
782
817
|
if classic :
|
|
783
|
-
prompt = prompt + ">
|
|
818
|
+
prompt = prompt + ">"
|
|
784
819
|
else :
|
|
785
|
-
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}
|
|
786
|
-
|
|
787
|
-
|
|
820
|
+
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}"
|
|
821
|
+
else:
|
|
822
|
+
if classic :
|
|
823
|
+
prompt = prompt + "/"
|
|
824
|
+
else :
|
|
825
|
+
if INVERT_SLASH:
|
|
826
|
+
prompt = prompt + f"{ANSI_INVERT}"
|
|
827
|
+
else:
|
|
828
|
+
prompt = prompt + f"{ANSI_NORMAL}"
|
|
829
|
+
prompt = prompt + f"{SLASH_START}"
|
|
788
830
|
if not last_ack:
|
|
789
831
|
prompt = prompt + f"{ANSI_BRED}"
|
|
790
832
|
if classic :
|
|
@@ -800,11 +842,9 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
800
842
|
else :
|
|
801
843
|
prompt = prompt + f"{ANSI_BBLUE}"
|
|
802
844
|
if not classic:
|
|
845
|
+
prompt = prompt + f"{SLASH_END}"
|
|
803
846
|
prompt = prompt + f"{ANSI_INVERT}"
|
|
804
847
|
|
|
805
|
-
if print_name and not classic :
|
|
806
|
-
prompt = prompt + f"{ANSI_NORMAL}{ARROW_TAIL}{ANSI_INVERT}"
|
|
807
|
-
|
|
808
848
|
prompt = prompt + f"{contact['adv_name']}"
|
|
809
849
|
if contact["type"] == 0 or contact["out_path_len"]==-1:
|
|
810
850
|
if scope is None:
|
|
@@ -818,14 +858,15 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
818
858
|
prompt = prompt + "|" + contact["out_path"]
|
|
819
859
|
|
|
820
860
|
if classic :
|
|
821
|
-
prompt = prompt + f"{ANSI_NORMAL}>
|
|
861
|
+
prompt = prompt + f"{ANSI_NORMAL}>"
|
|
822
862
|
else:
|
|
823
863
|
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}"
|
|
824
864
|
|
|
825
865
|
prompt = prompt + f"{ANSI_END}"
|
|
826
866
|
|
|
827
|
-
|
|
828
|
-
|
|
867
|
+
prompt = prompt + " "
|
|
868
|
+
if not color :
|
|
869
|
+
prompt=escape_ansi(prompt)
|
|
829
870
|
|
|
830
871
|
session.app.ttimeoutlen = 0.2
|
|
831
872
|
session.app.timeoutlen = 0.2
|
|
@@ -1036,8 +1077,10 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
1036
1077
|
except asyncio.CancelledError:
|
|
1037
1078
|
# Handle task cancellation from KeyboardInterrupt in asyncio.run()
|
|
1038
1079
|
print("Exiting cli")
|
|
1039
|
-
|
|
1040
|
-
interactive_loop.
|
|
1080
|
+
if platform.system() == "Darwin" or platform.system() == "Windows":
|
|
1081
|
+
interactive_loop.classic = True
|
|
1082
|
+
else:
|
|
1083
|
+
interactive_loop.classic = False
|
|
1041
1084
|
|
|
1042
1085
|
async def process_contact_chat_line(mc, contact, line):
|
|
1043
1086
|
if contact["type"] == 0:
|
|
@@ -1069,6 +1112,29 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1069
1112
|
print("")
|
|
1070
1113
|
return True
|
|
1071
1114
|
|
|
1115
|
+
if line.startswith("sleep") or line.startswith("s"):
|
|
1116
|
+
try:
|
|
1117
|
+
sleeptime = int(line.split(" ",2)[1])
|
|
1118
|
+
cmd_pos = 2
|
|
1119
|
+
except IndexError: # nothing arg after sleep
|
|
1120
|
+
sleeptime = 1
|
|
1121
|
+
cmd_pos = 0
|
|
1122
|
+
except ValueError:
|
|
1123
|
+
sleeptime = 1
|
|
1124
|
+
cmd_pos = 1
|
|
1125
|
+
|
|
1126
|
+
try:
|
|
1127
|
+
if cmd_pos > 0:
|
|
1128
|
+
secline = line.split(" ",cmd_pos)[cmd_pos]
|
|
1129
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1130
|
+
except IndexError:
|
|
1131
|
+
pass
|
|
1132
|
+
|
|
1133
|
+
# will sleep after executed command if there is a command
|
|
1134
|
+
await asyncio.sleep(sleeptime)
|
|
1135
|
+
|
|
1136
|
+
return True
|
|
1137
|
+
|
|
1072
1138
|
if line == "contact_lastmod":
|
|
1073
1139
|
timestamp = contact["lastmod"]
|
|
1074
1140
|
print(f"{contact['adv_name']} updated"
|
|
@@ -1085,7 +1151,6 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
1085
1151
|
line == "contact_info" or line == "ci" or\
|
|
1086
1152
|
line == "req_status" or line == "rs" or\
|
|
1087
1153
|
line == "req_neighbours" or line == "rn" or\
|
|
1088
|
-
line == "req_bstatus" or line == "rbs" or\
|
|
1089
1154
|
line == "req_telemetry" or line == "rt" or\
|
|
1090
1155
|
line == "req_acl" or\
|
|
1091
1156
|
line == "path" or\
|
|
@@ -1615,7 +1680,7 @@ async def print_disc_trace_to (mc, contact):
|
|
|
1615
1680
|
|
|
1616
1681
|
async def next_cmd(mc, cmds, json_output=False):
|
|
1617
1682
|
""" process next command """
|
|
1618
|
-
global
|
|
1683
|
+
global ARROW_HEAD, SLASH_START, SLASH_END, INVERT_SLASH
|
|
1619
1684
|
try :
|
|
1620
1685
|
argnum = 0
|
|
1621
1686
|
|
|
@@ -1722,37 +1787,25 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1722
1787
|
match cmds[1]:
|
|
1723
1788
|
case "help" :
|
|
1724
1789
|
argnum = 1
|
|
1725
|
-
|
|
1726
|
-
pin <pin> : ble pin
|
|
1727
|
-
radio <freq,bw,sf,cr> : radio params
|
|
1728
|
-
tuning <rx_dly,af> : tuning params
|
|
1729
|
-
tx <dbm> : tx power
|
|
1730
|
-
name <name> : node name
|
|
1731
|
-
lat <lat> : latitude
|
|
1732
|
-
lon <lon> : longitude
|
|
1733
|
-
coords <lat,lon> : coordinates
|
|
1734
|
-
print_snr <on/off> : toggle snr display in messages
|
|
1735
|
-
print_adverts <on/off> : display adverts as they come
|
|
1736
|
-
print_new_contacts <on/off> : display new pending contacts when available
|
|
1737
|
-
print_path_updates <on/off> : display path updates as they come""")
|
|
1790
|
+
get_help_for("set")
|
|
1738
1791
|
case "max_flood_attempts":
|
|
1739
1792
|
msg_ack.max_flood_attempts=int(cmds[2])
|
|
1740
1793
|
case "max_attempts":
|
|
1741
1794
|
msg_ack.max_attempts=int(cmds[2])
|
|
1742
1795
|
case "flood_after":
|
|
1743
1796
|
msg_ack.flood_after=int(cmds[2])
|
|
1744
|
-
case "print_name":
|
|
1745
|
-
interactive_loop.print_name = (cmds[2] == "on")
|
|
1746
|
-
if json_output :
|
|
1747
|
-
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1748
1797
|
case "classic_prompt":
|
|
1749
1798
|
interactive_loop.classic = (cmds[2] == "on")
|
|
1750
1799
|
if json_output :
|
|
1751
1800
|
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1752
|
-
case "arrow_tail":
|
|
1753
|
-
ARROW_TAIL = cmds[2]
|
|
1754
1801
|
case "arrow_head":
|
|
1755
1802
|
ARROW_HEAD = cmds[2]
|
|
1803
|
+
case "slash_start":
|
|
1804
|
+
SLASH_START = cmds[2]
|
|
1805
|
+
case "slash_end":
|
|
1806
|
+
SLASH_END = cmds[2]
|
|
1807
|
+
case "invert_slash":
|
|
1808
|
+
INVERT_SLASH = cmds[2] == "on"
|
|
1756
1809
|
case "color" :
|
|
1757
1810
|
process_event_message.color = (cmds[2] == "on")
|
|
1758
1811
|
if json_output :
|
|
@@ -1958,21 +2011,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1958
2011
|
argnum = 1
|
|
1959
2012
|
match cmds[1]:
|
|
1960
2013
|
case "help":
|
|
1961
|
-
|
|
1962
|
-
name : node name
|
|
1963
|
-
bat : battery level in mV
|
|
1964
|
-
fstats : fs statistics
|
|
1965
|
-
coords : adv coordinates
|
|
1966
|
-
lat : latitude
|
|
1967
|
-
lon : longitude
|
|
1968
|
-
radio : radio parameters
|
|
1969
|
-
tx : tx power
|
|
1970
|
-
print_snr : snr display in messages
|
|
1971
|
-
print_adverts : display adverts as they come
|
|
1972
|
-
print_new_contacts : display new pending contacts when available
|
|
1973
|
-
print_path_updates : display path updates as they come
|
|
1974
|
-
custom : all custom variables in json format
|
|
1975
|
-
each custom var can also be get/set directly""")
|
|
2014
|
+
get_help_for("get")
|
|
1976
2015
|
case "max_flood_attempts":
|
|
1977
2016
|
if json_output :
|
|
1978
2017
|
print(json.dumps({"max_flood_attempts" : msg_ack.max_flood_attempts}))
|
|
@@ -1983,11 +2022,6 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1983
2022
|
print(json.dumps({"flood_after" : msg_ack.flood_after}))
|
|
1984
2023
|
else:
|
|
1985
2024
|
print(f"flood_after: {msg_ack.flood_after}")
|
|
1986
|
-
case "print_name":
|
|
1987
|
-
if json_output :
|
|
1988
|
-
print(json.dumps({"print_name" : interactive_loop.print_name}))
|
|
1989
|
-
else:
|
|
1990
|
-
print(f"{'on' if interactive_loop.print_name else 'off'}")
|
|
1991
2025
|
case "classic_prompt":
|
|
1992
2026
|
if json_output :
|
|
1993
2027
|
print(json.dumps({"classic_prompt" : interactive_loop.classic}))
|
|
@@ -2344,12 +2378,12 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2344
2378
|
else :
|
|
2345
2379
|
color = process_event_message.color
|
|
2346
2380
|
classic = interactive_loop.classic or not color
|
|
2347
|
-
print("
|
|
2381
|
+
print(" ", end="")
|
|
2348
2382
|
for t in ev.payload["path"]:
|
|
2349
2383
|
if classic :
|
|
2350
2384
|
print("→",end="")
|
|
2351
2385
|
else:
|
|
2352
|
-
print(f"
|
|
2386
|
+
print(f"{ANSI_INVERT}", end="")
|
|
2353
2387
|
snr = t['snr']
|
|
2354
2388
|
if color:
|
|
2355
2389
|
if snr >= 10 :
|
|
@@ -2368,7 +2402,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2368
2402
|
if "hash" in t:
|
|
2369
2403
|
print(f"[{t['hash']}]",end="")
|
|
2370
2404
|
else:
|
|
2371
|
-
print(
|
|
2405
|
+
print()
|
|
2372
2406
|
|
|
2373
2407
|
case "login" | "l" :
|
|
2374
2408
|
argnum = 2
|
|
@@ -2428,46 +2462,6 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2428
2462
|
contact = mc.get_contact_by_name(cmds[1])
|
|
2429
2463
|
contact["timeout"] = float(cmds[2])
|
|
2430
2464
|
|
|
2431
|
-
case "req_status" | "rs" :
|
|
2432
|
-
argnum = 1
|
|
2433
|
-
await mc.ensure_contacts()
|
|
2434
|
-
contact = mc.get_contact_by_name(cmds[1])
|
|
2435
|
-
res = await mc.commands.send_statusreq(contact)
|
|
2436
|
-
logger.debug(res)
|
|
2437
|
-
if res.type == EventType.ERROR:
|
|
2438
|
-
print(f"Error while requesting status: {res}")
|
|
2439
|
-
else :
|
|
2440
|
-
timeout = res.payload["suggested_timeout"]/800 if not "timeout" in contact or contact['timeout']==0 else contact["timeout"]
|
|
2441
|
-
res = await mc.wait_for_event(EventType.STATUS_RESPONSE, timeout=timeout)
|
|
2442
|
-
logger.debug(res)
|
|
2443
|
-
if res is None:
|
|
2444
|
-
if json_output :
|
|
2445
|
-
print(json.dumps({"error" : "Timeout waiting status"}))
|
|
2446
|
-
else:
|
|
2447
|
-
print("Timeout waiting status")
|
|
2448
|
-
else :
|
|
2449
|
-
print(json.dumps(res.payload, indent=4))
|
|
2450
|
-
|
|
2451
|
-
case "req_telemetry" | "rt" :
|
|
2452
|
-
argnum = 1
|
|
2453
|
-
await mc.ensure_contacts()
|
|
2454
|
-
contact = mc.get_contact_by_name(cmds[1])
|
|
2455
|
-
res = await mc.commands.send_telemetry_req(contact)
|
|
2456
|
-
logger.debug(res)
|
|
2457
|
-
if res.type == EventType.ERROR:
|
|
2458
|
-
print(f"Error while requesting telemetry")
|
|
2459
|
-
else:
|
|
2460
|
-
timeout = res.payload["suggested_timeout"]/800 if not "timeout" in contact or contact['timeout']==0 else contact["timeout"]
|
|
2461
|
-
res = await mc.wait_for_event(EventType.TELEMETRY_RESPONSE, timeout=timeout)
|
|
2462
|
-
logger.debug(res)
|
|
2463
|
-
if res is None:
|
|
2464
|
-
if json_output :
|
|
2465
|
-
print(json.dumps({"error" : "Timeout waiting telemetry"}))
|
|
2466
|
-
else:
|
|
2467
|
-
print("Timeout waiting telemetry")
|
|
2468
|
-
else :
|
|
2469
|
-
print(json.dumps(res.payload, indent=4))
|
|
2470
|
-
|
|
2471
2465
|
case "disc_path" | "dp" :
|
|
2472
2466
|
argnum = 1
|
|
2473
2467
|
await mc.ensure_contacts()
|
|
@@ -2553,7 +2547,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2553
2547
|
|
|
2554
2548
|
print(f" {name:16} {type:>4} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}")
|
|
2555
2549
|
|
|
2556
|
-
case "
|
|
2550
|
+
case "req_telemetry"|"rt" :
|
|
2557
2551
|
argnum = 1
|
|
2558
2552
|
await mc.ensure_contacts()
|
|
2559
2553
|
contact = mc.get_contact_by_name(cmds[1])
|
|
@@ -2565,9 +2559,13 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2565
2559
|
else:
|
|
2566
2560
|
print("Error getting data")
|
|
2567
2561
|
else :
|
|
2568
|
-
print(json.dumps(
|
|
2562
|
+
print(json.dumps({
|
|
2563
|
+
"name": contact["adv_name"],
|
|
2564
|
+
"pubkey_pre": contact["public_key"][0:12],
|
|
2565
|
+
"lpp": res,
|
|
2566
|
+
}, indent = 4))
|
|
2569
2567
|
|
|
2570
|
-
case "
|
|
2568
|
+
case "req_status"|"rs" :
|
|
2571
2569
|
argnum = 1
|
|
2572
2570
|
await mc.ensure_contacts()
|
|
2573
2571
|
contact = mc.get_contact_by_name(cmds[1])
|
|
@@ -2651,15 +2649,30 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2651
2649
|
if json_output:
|
|
2652
2650
|
print(json.dumps(res, indent=4))
|
|
2653
2651
|
else:
|
|
2652
|
+
width = os.get_terminal_size().columns
|
|
2654
2653
|
print(f"Got {res['results_count']} neighbours out of {res['neighbours_count']} from {contact['adv_name']}:")
|
|
2655
2654
|
for n in res['neighbours']:
|
|
2656
2655
|
ct = mc.get_contact_by_key_prefix(n["pubkey"])
|
|
2657
|
-
if ct :
|
|
2656
|
+
if ct and width > 60 :
|
|
2658
2657
|
name = f"[{n['pubkey'][0:8]}] {ct['adv_name']}"
|
|
2658
|
+
name = f"{name:30}"
|
|
2659
|
+
elif ct :
|
|
2660
|
+
name = f"{ct['adv_name']}"
|
|
2661
|
+
name = f"{name:20}"
|
|
2659
2662
|
else:
|
|
2660
2663
|
name = f"[{n['pubkey']}]"
|
|
2661
2664
|
|
|
2662
|
-
|
|
2665
|
+
t_s = n['secs_ago']
|
|
2666
|
+
time_ago = f"{t_s}s"
|
|
2667
|
+
if t_s / 86400 >= 1 : # result in days
|
|
2668
|
+
time_ago = f"{int(t_s/86400)}d ago{f' ({time_ago})' if width > 62 else ''}"
|
|
2669
|
+
elif t_s / 3600 >= 1 : # result in days
|
|
2670
|
+
time_ago = f"{int(t_s/3600)}h ago{f' ({time_ago})' if width > 62 else ''}"
|
|
2671
|
+
elif t_s / 60 >= 1 : # result in min
|
|
2672
|
+
time_ago = f"{int(t_s/60)}m ago{f' ({time_ago})' if width > 62 else ''}"
|
|
2673
|
+
|
|
2674
|
+
|
|
2675
|
+
print(f" {name} {time_ago}, {n['snr']}dB{' SNR' if width > 66 else ''}")
|
|
2663
2676
|
|
|
2664
2677
|
case "req_binary" :
|
|
2665
2678
|
argnum = 2
|
|
@@ -3109,8 +3122,8 @@ def command_help():
|
|
|
3109
3122
|
reboot : reboots node
|
|
3110
3123
|
sleep <secs> : sleeps for a given amount of secs s
|
|
3111
3124
|
wait_key : wait until user presses <Enter> wk
|
|
3112
|
-
apply_to <
|
|
3113
|
-
|
|
3125
|
+
apply_to <f> <cmds> : sends cmds to contacts matching f at
|
|
3126
|
+
Messaging
|
|
3114
3127
|
msg <name> <msg> : send message to node by name m {
|
|
3115
3128
|
wait_ack : wait an ack wa }
|
|
3116
3129
|
chan <nb> <msg> : send message to channel number <nb> ch
|
|
@@ -3123,6 +3136,7 @@ def command_help():
|
|
|
3123
3136
|
get_channel <n> : get info for channel (by number or name)
|
|
3124
3137
|
set_channel n nm k : set channel info (nb, name, key)
|
|
3125
3138
|
remove_channel <n> : remove channel (by number or name)
|
|
3139
|
+
scope <s> : sets scope for flood messages
|
|
3126
3140
|
Management
|
|
3127
3141
|
advert : sends advert a
|
|
3128
3142
|
floodadv : flood advert
|
|
@@ -3174,7 +3188,6 @@ def usage () :
|
|
|
3174
3188
|
-D : debug
|
|
3175
3189
|
-S : scan for devices and show a selector
|
|
3176
3190
|
-l : list available ble/serial devices and exit
|
|
3177
|
-
-c <on/off> : disables most of color output if off
|
|
3178
3191
|
-T <timeout> : timeout for the ble scan (-S and -l) default 2s
|
|
3179
3192
|
-a <address> : specifies device address (can be a name)
|
|
3180
3193
|
-d <name> : filter meshcore devices with name or address
|
|
@@ -3183,14 +3196,16 @@ def usage () :
|
|
|
3183
3196
|
-p <port> : specifies tcp port (default 5000)
|
|
3184
3197
|
-s <port> : use serial port <port>
|
|
3185
3198
|
-b <baudrate> : specify baudrate
|
|
3199
|
+
-C : toggles classic mode for prompt
|
|
3200
|
+
-c <on/off> : disables most of color output if off
|
|
3186
3201
|
|
|
3187
3202
|
Available Commands and shorcuts (can be chained) :""")
|
|
3188
3203
|
command_help()
|
|
3189
3204
|
|
|
3190
3205
|
def get_help_for (cmdname, context="line") :
|
|
3191
3206
|
if cmdname == "apply_to" or cmdname == "at" :
|
|
3192
|
-
print("""apply_to <
|
|
3193
|
-
|
|
3207
|
+
print("""apply_to <f> <cmd> : applies cmd to contacts matching filter <f>
|
|
3208
|
+
Filter is constructed with comma separated fields :
|
|
3194
3209
|
- u, matches modification time < or > than a timestamp
|
|
3195
3210
|
(can also be days hours or minutes ago if followed by d,h or m)
|
|
3196
3211
|
- t, matches the type (1: client, 2: repeater, 3: room, 4: sensor)
|
|
@@ -3198,7 +3213,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3198
3213
|
- d, direct, similar to h>-1
|
|
3199
3214
|
- f, flood, similar to h<0 or h=-1
|
|
3200
3215
|
|
|
3201
|
-
Note: Some commands like contact_name (aka cn), reset_path (aka rp), forget_password (aka fp) can be chained.
|
|
3216
|
+
Note: Some commands like contact_name (aka cn), reset_path (aka rp), forget_password (aka fp) can be chained. There is also a sleep command taking an optional event. The sleep will be issued after the command, it helps limiting rate through repeaters ...
|
|
3202
3217
|
|
|
3203
3218
|
Examples:
|
|
3204
3219
|
# removes all clients that have not been updated in last 2 days
|
|
@@ -3209,7 +3224,7 @@ def get_help_for (cmdname, context="line") :
|
|
|
3209
3224
|
at t=2 rp login
|
|
3210
3225
|
""")
|
|
3211
3226
|
|
|
3212
|
-
|
|
3227
|
+
elif cmdname == "node_discover" or cmdname == "nd" :
|
|
3213
3228
|
print("""node_discover <filter> : discovers 0-hop nodes and displays signal info
|
|
3214
3229
|
|
|
3215
3230
|
filter can be "all" for all types or nodes or a comma separated list consisting of :
|
|
@@ -3221,6 +3236,68 @@ def get_help_for (cmdname, context="line") :
|
|
|
3221
3236
|
nd can be used with no filter parameter ... !!! BEWARE WITH CHAINING !!!
|
|
3222
3237
|
""")
|
|
3223
3238
|
|
|
3239
|
+
elif cmdname == "get" :
|
|
3240
|
+
print("""Gets parameters from node
|
|
3241
|
+
Please see also help for set command, which is more up to date ...
|
|
3242
|
+
name : node name
|
|
3243
|
+
bat : battery level in mV
|
|
3244
|
+
fstats : fs statistics
|
|
3245
|
+
coords : adv coordinates
|
|
3246
|
+
lat : latitude
|
|
3247
|
+
lon : longitude
|
|
3248
|
+
radio : radio parameters
|
|
3249
|
+
tx : tx power
|
|
3250
|
+
print_snr : snr display in messages
|
|
3251
|
+
print_adverts : display adverts as they come
|
|
3252
|
+
print_new_contacts : display new pending contacts when available
|
|
3253
|
+
print_path_updates : display path updates as they come
|
|
3254
|
+
custom : all custom variables in json format
|
|
3255
|
+
each custom var can also be get/set directly""")
|
|
3256
|
+
|
|
3257
|
+
elif cmdname == "set" :
|
|
3258
|
+
print("""Available parameters :
|
|
3259
|
+
device:
|
|
3260
|
+
pin <pin> : ble pin
|
|
3261
|
+
radio <freq,bw,sf,cr> : radio params
|
|
3262
|
+
tuning <rx_dly,af> : tuning params
|
|
3263
|
+
tx <dbm> : tx power
|
|
3264
|
+
name <name> : node name
|
|
3265
|
+
lat <lat> : latitude
|
|
3266
|
+
lon <lon> : longitude
|
|
3267
|
+
coords <lat,lon> : coordinates
|
|
3268
|
+
auto_update_contacts <> : automatically updates contact list
|
|
3269
|
+
multi_ack <on/off> : multi-acks feature
|
|
3270
|
+
telemetry_mode_base <mode> : set basic telemetry mode all/selected/off
|
|
3271
|
+
telemetry_mode_loc <mode> : set location telemetry mode all/selected/off
|
|
3272
|
+
telemetry_mode_env <mode> : set env telemetry mode all/selected/off
|
|
3273
|
+
advert_loc_policy <policy> : "share" means loc will be shared in adv
|
|
3274
|
+
display:
|
|
3275
|
+
print_snr <on/off> : toggle snr display in messages
|
|
3276
|
+
print_adverts <on/off> : display adverts as they come
|
|
3277
|
+
print_new_contacts <on/off> : display new pending contacts when available
|
|
3278
|
+
print_path_updates <on/off> : display path updates as they come
|
|
3279
|
+
json_log_rx <on/off> : logs packets incoming to device as json
|
|
3280
|
+
channel_echoes <on/off> : print repeats for channel data
|
|
3281
|
+
echo_unk_channels <on/off> : also dump unk channels (encrypted)
|
|
3282
|
+
color <on/off> : color off should remove ANSI codes from output
|
|
3283
|
+
prompt:
|
|
3284
|
+
classic_prompt <on/off> : activates less fancier prompt
|
|
3285
|
+
arrow_head <string> : change arrow head in prompt
|
|
3286
|
+
slash_start <string> : idem for slash start
|
|
3287
|
+
slash_end <string> : slash end
|
|
3288
|
+
invert_slash <on/off> : apply color inversion to slash """)
|
|
3289
|
+
|
|
3290
|
+
elif cmdname == "scope":
|
|
3291
|
+
print("""scope <scope> : changes flood scope of the node
|
|
3292
|
+
|
|
3293
|
+
The scope command can be used from command line or interactive mode to set the region in which flood packets will be transmitted.
|
|
3294
|
+
|
|
3295
|
+
Managing Flood Scope in interactive mode
|
|
3296
|
+
Flood scope has recently been introduced in meshcore (from v1.10.0). It limits the scope of packets to regions, using transport codes in the frame.
|
|
3297
|
+
When entering chat mode, scope will be reset to *, meaning classic flood.
|
|
3298
|
+
You can switch scope using the scope command, or postfixing the to command with %<scope>.
|
|
3299
|
+
Scope can also be applied to a command using % before the scope name. For instance login%#Morbihan will limit diffusion of the login command (which is usually sent flood to get the path to a repeater) to the #Morbihan region.""")
|
|
3300
|
+
|
|
3224
3301
|
else:
|
|
3225
3302
|
print(f"Sorry, no help yet for {cmdname}")
|
|
3226
3303
|
|
|
@@ -3242,12 +3319,14 @@ async def main(argv):
|
|
|
3242
3319
|
with open(MCCLI_ADDRESS, encoding="utf-8") as f :
|
|
3243
3320
|
address = f.readline().strip()
|
|
3244
3321
|
|
|
3245
|
-
opts, args = getopt.getopt(argv, "a:d:s:ht:p:b:fjDhvSlT:Pc:")
|
|
3322
|
+
opts, args = getopt.getopt(argv, "a:d:s:ht:p:b:fjDhvSlT:Pc:C")
|
|
3246
3323
|
for opt, arg in opts :
|
|
3247
3324
|
match opt:
|
|
3248
3325
|
case "-c" :
|
|
3249
3326
|
if arg == "off":
|
|
3250
3327
|
process_event_message.color = False
|
|
3328
|
+
case "-C":
|
|
3329
|
+
interactive_loop.classic = not interactive_loop.classic
|
|
3251
3330
|
case "-d" : # name specified on cmdline
|
|
3252
3331
|
address = arg
|
|
3253
3332
|
case "-a" : # address specified on cmdline
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
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
|
|
@@ -58,22 +58,25 @@ Init files can also be defined for a given device, meshcore-cli will look for `&
|
|
|
58
58
|
|
|
59
59
|
### Arguments
|
|
60
60
|
|
|
61
|
-
Arguments mostly deals with
|
|
61
|
+
Arguments mostly deals with connection to the node
|
|
62
62
|
|
|
63
63
|
<pre>
|
|
64
64
|
-h : prints this help
|
|
65
65
|
-v : prints version
|
|
66
66
|
-j : json output (disables init file)
|
|
67
67
|
-D : debug
|
|
68
|
-
-S :
|
|
69
|
-
-l : list available ble devices and exit
|
|
70
|
-
-T <timeout
|
|
71
|
-
-a <address
|
|
72
|
-
-d <name
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
68
|
+
-S : scan for devices and show a selector
|
|
69
|
+
-l : list available ble/serial devices and exit
|
|
70
|
+
-T <timeout> : timeout for the ble scan (-S and -l) default 2s
|
|
71
|
+
-a <address> : specifies device address (can be a name)
|
|
72
|
+
-d <name> : filter meshcore devices with name or address
|
|
73
|
+
-P : forces pairing via the OS
|
|
74
|
+
-t <hostname> : connects via tcp/ip
|
|
75
|
+
-p <port> : specifies tcp port (default 5000)
|
|
76
|
+
-s <port> : use serial port <port>
|
|
77
|
+
-b <baudrate> : specify baudrate
|
|
78
|
+
-C : toggles classic mode for prompt
|
|
79
|
+
-c <on/off> : disables most of color output if off
|
|
77
80
|
</pre>
|
|
78
81
|
|
|
79
82
|
### Available Commands
|
|
@@ -81,60 +84,70 @@ Arguments mostly deals with ble connection
|
|
|
81
84
|
Commands are given after arguments, they can be chained and some have shortcuts. Also prefixing a command with a dot `.` will force it to output json instead of synthetic result.
|
|
82
85
|
|
|
83
86
|
<pre>
|
|
87
|
+
?<cmd> may give you some more help about cmd
|
|
84
88
|
General commands
|
|
85
89
|
chat : enter the chat (interactive) mode
|
|
86
|
-
chat_to <ct
|
|
87
|
-
script <filename
|
|
90
|
+
chat_to <ct> : enter chat with contact to
|
|
91
|
+
script <filename> : execute commands in filename
|
|
88
92
|
infos : print informations about the node i
|
|
89
93
|
self_telemetry : print own telemtry t
|
|
90
94
|
card : export this node URI e
|
|
91
95
|
ver : firmware version v
|
|
92
96
|
reboot : reboots node
|
|
93
|
-
sleep <secs
|
|
94
|
-
wait_key : wait until user presses <Enter
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
sleep <secs> : sleeps for a given amount of secs s
|
|
98
|
+
wait_key : wait until user presses <Enter> wk
|
|
99
|
+
apply_to <f> <cmds> : sends cmds to contacts matching f at
|
|
100
|
+
Messaging
|
|
101
|
+
msg <name> <msg> : send message to node by name m {
|
|
97
102
|
wait_ack : wait an ack wa }
|
|
98
|
-
chan <nb
|
|
99
|
-
public <msg
|
|
103
|
+
chan <nb> <msg> : send message to channel number <nb> ch
|
|
104
|
+
public <msg> : send message to public channel (0) dch
|
|
100
105
|
recv : reads next msg r
|
|
101
106
|
wait_msg : wait for a message and read it wm
|
|
102
107
|
sync_msgs : gets all unread msgs from the node sm
|
|
103
108
|
msgs_subscribe : display msgs as they arrive ms
|
|
104
|
-
|
|
109
|
+
get_channels : prints all channel info
|
|
110
|
+
get_channel <n> : get info for channel (by number or name)
|
|
105
111
|
set_channel n nm k : set channel info (nb, name, key)
|
|
112
|
+
remove_channel <n> : remove channel (by number or name)
|
|
113
|
+
scope <s> : sets node's flood scope
|
|
106
114
|
Management
|
|
107
115
|
advert : sends advert a
|
|
108
116
|
floodadv : flood advert
|
|
109
|
-
get <param
|
|
110
|
-
set <param
|
|
111
|
-
time <epoch
|
|
117
|
+
get <param> : gets a param, \"get help\" for more
|
|
118
|
+
set <param> <value> : sets a param, \"set help\" for more
|
|
119
|
+
time <epoch> : sets time to given epoch
|
|
112
120
|
clock : get current time
|
|
113
121
|
clock sync : sync device clock st
|
|
122
|
+
node_discover <filter> : discovers nodes based on their type nd
|
|
114
123
|
Contacts
|
|
115
124
|
contacts / list : gets contact list lc
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
reload_contacts : force reloading all contacts rc
|
|
126
|
+
contact_info <ct> : prints information for contact ct ci
|
|
127
|
+
contact_timeout <ct> v : sets temp default timeout for contact
|
|
128
|
+
share_contact <ct> : share a contact with others sc
|
|
129
|
+
export_contact <ct> : get a contact's URI ec
|
|
130
|
+
import_contact <URI> : import a contact from its URI ic
|
|
131
|
+
remove_contact <ct> : removes a contact from this node
|
|
132
|
+
path <ct> : diplays path for a contact
|
|
133
|
+
disc_path <ct> : discover new path and display dp
|
|
134
|
+
reset_path <ct> : resets path to a contact to flood rp
|
|
135
|
+
change_path <ct> <pth> : change the path to a contact cp
|
|
136
|
+
change_flags <ct> <f> : change contact flags (tel_l|tel_a|star)cf
|
|
137
|
+
req_telemetry <ct> : prints telemetry data as json rt
|
|
138
|
+
req_mma <ct> : requests min/max/avg for a sensor rm
|
|
139
|
+
req_acl <ct> : requests access control list for sensor
|
|
129
140
|
pending_contacts : show pending contacts
|
|
130
|
-
add_pending <
|
|
131
|
-
flush_pending : flush pending contact
|
|
141
|
+
add_pending <pending> : manually add pending contact
|
|
142
|
+
flush_pending : flush pending contact list
|
|
132
143
|
Repeaters
|
|
133
|
-
login <name
|
|
134
|
-
logout <name
|
|
135
|
-
cmd <name
|
|
144
|
+
login <name> <pwd> : log into a node (rep) with given pwd l
|
|
145
|
+
logout <name> : log out of a repeater
|
|
146
|
+
cmd <name> <cmd> : sends a command to a repeater (no ack) c [
|
|
136
147
|
wmt8 : wait for a msg (reply) with a timeout ]
|
|
137
|
-
req_status <name
|
|
148
|
+
req_status <name> : requests status from a node rs
|
|
149
|
+
req_neighbours <name> : requests for neighbours in binary form rn
|
|
150
|
+
trace <path> : run a trace, path is comma separated
|
|
138
151
|
</pre>
|
|
139
152
|
|
|
140
153
|
### Interactive Mode
|
|
@@ -147,14 +160,70 @@ You'll get a prompt with the name of your node. From here you can type meshcore-
|
|
|
147
160
|
|
|
148
161
|
The `to` command is specific to chat mode, it lets you enter the recipient for next command. By default you're on your node but you can enter other nodes or public rooms. Here are some examples :
|
|
149
162
|
|
|
150
|
-
- `to <
|
|
163
|
+
- `to <dest>` : will enter dest (node or channel)
|
|
151
164
|
- `to /`, `to ~` : will go to the root (your node)
|
|
152
165
|
- `to ..` : will go to the last node (it will switch between the two last nodes, this is just a 1-depth history)
|
|
153
166
|
- `to !` : will switch to the node you received last message from
|
|
154
167
|
|
|
155
|
-
When you are
|
|
168
|
+
When you are in a node, the behaviour will depend on the node type, if you're on a chat node, it will send messages by default and you can chat. On a repeater or a room server, it will send commands (autocompletion has been set to comply with the CommonCli class of meshcore). To send a message through a room you'll have to prefix the message with a quote or use the send command.
|
|
156
169
|
|
|
157
|
-
|
|
170
|
+
The `/` character is used to bypass the node you have currently selected using `to`:
|
|
171
|
+
- `/<cmd>` issues cmd command on the root
|
|
172
|
+
- `/<node>/<cmd>` will send cmd to selected node
|
|
173
|
+
- `/<dest> <msg>` will send msg to dest (channel or node)
|
|
174
|
+
|
|
175
|
+
#### Flood Scope in interactive mode
|
|
176
|
+
|
|
177
|
+
Flood scope has recently been introduced in meshcore (from `v1.10.0`). It limits the scope of packets to regions, using transport codes in the frame.
|
|
178
|
+
|
|
179
|
+
When entering chat mode, scope will be reset to `*`, meaning classic flood.
|
|
180
|
+
|
|
181
|
+
You can switch scope using the `scope` command, or postfixing the `to` command with `%<scope>`.
|
|
182
|
+
|
|
183
|
+
Scope can also be applied to a command using `%` before the scope name. For instance `login%#Morbihan` will limit diffusion of the login command (which is usually sent flood to get the path to a repeater) to the `#Morbihan` region.
|
|
184
|
+
|
|
185
|
+
#### Channel echoes
|
|
186
|
+
|
|
187
|
+
It's sometimes interesting to know the path taken by a message received from a channel or which repeaters have repeated a sent message.
|
|
188
|
+
|
|
189
|
+
The app give you the information by listening `rx_log` from the device, when obtained the information is attached to the message and can be read.
|
|
190
|
+
|
|
191
|
+
In meshcore-cli I went lower-level by implementing channel echoes. When activated (with `/set channel_echoes on`), all the channel messages will be printed on the terminal along with the SNR and path taken. When sending a message, you'll have all the repeats from 0-hop repeaters as echoes, and when a message is received, you should see information about the received message, but also all the instances of the same message that might have reached you from another path.
|
|
192
|
+
|
|
193
|
+
In the example below, a msg has been sent between two repeaters, 21 and 25. 25 repeated the message and 21 the repeat and both echoes came back to the node with different SNRs.
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
f1down/#fdl|*> 8
|
|
197
|
+
#fdl f1down: 8 [25] -4.75-112
|
|
198
|
+
#fdl f1down: 8 [2521] 1.00-109
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Issuing batch commands to contacts with apply to
|
|
202
|
+
|
|
203
|
+
`apply_to <f> <cmd>` : applies cmd to contacts matching filter `<f>` it can be used to apply the same command to a pool of repeaters, or remove some contacts matching a condition.
|
|
204
|
+
|
|
205
|
+
Filter is constructed with comma separated fields :
|
|
206
|
+
|
|
207
|
+
- `u`, matches modification time `<` or `>` than a timestamp (can also be days hours or minutes ago if followed by `d`,`h` or `m`)
|
|
208
|
+
- `t`, matches the type (1: client, 2: repeater, 3: room, 4: sensor)
|
|
209
|
+
- `h`, matches number of hops
|
|
210
|
+
- `d`, direct, similar to `h>-1`
|
|
211
|
+
- `f`, flood, similar to `h<0` or `h=-1`
|
|
212
|
+
|
|
213
|
+
Commands should be written as if in interactive mode, if writing from the commandline don't forget to use commas to clearly delimit fields.
|
|
214
|
+
|
|
215
|
+
Note: Some commands like `contact_name` (aka `cn`), `reset_path` (aka `rp`), `forget_password` (aka `fp`) can be chained. There is also a `sleep` command taking an optional time parameter. The sleep will be issued after the command, it helps limiting rate through repeaters ...
|
|
216
|
+
|
|
217
|
+
#### Examples
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
# removes all clients that have not been updated in last 2 days
|
|
221
|
+
at u<2d,t=1 remove_contact
|
|
222
|
+
# gives traces to repeaters that have been updated in the last 24h and are direct
|
|
223
|
+
at t=2,u>1d,d cn trace
|
|
224
|
+
# tries to do flood login to all repeaters
|
|
225
|
+
at t=2 rp login
|
|
226
|
+
```
|
|
158
227
|
|
|
159
228
|
## Examples
|
|
160
229
|
|
|
@@ -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=C6dZGtfqIEIsEaoVBNOmr4xmoy0sMr-gZOTqZ0prxd0,146609
|
|
4
|
+
meshcore_cli-1.3.0.dist-info/METADATA,sha256=Fgv1cb5iij3ExDitwB8vRvP7GuxSOPqkv5nxnyATMRA,15873
|
|
5
|
+
meshcore_cli-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
meshcore_cli-1.3.0.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
+
meshcore_cli-1.3.0.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
+
meshcore_cli-1.3.0.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=cbZPbXrhDENJvnGlR63lrupJQ22Nzk4bxGzudBKQMxQ,143616
|
|
4
|
-
meshcore_cli-1.2.12.dist-info/METADATA,sha256=XU2LqLOUEHonAUDaa2VaiLsp_wvVa5XIwobda-mP_NE,11658
|
|
5
|
-
meshcore_cli-1.2.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
meshcore_cli-1.2.12.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
-
meshcore_cli-1.2.12.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
-
meshcore_cli-1.2.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|