meshcore-cli 1.1.40__py3-none-any.whl → 1.2.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 +516 -338
- {meshcore_cli-1.1.40.dist-info → meshcore_cli-1.2.0.dist-info}/METADATA +1 -1
- meshcore_cli-1.2.0.dist-info/RECORD +8 -0
- meshcore_cli-1.1.40.dist-info/RECORD +0 -8
- {meshcore_cli-1.1.40.dist-info → meshcore_cli-1.2.0.dist-info}/WHEEL +0 -0
- {meshcore_cli-1.1.40.dist-info → meshcore_cli-1.2.0.dist-info}/entry_points.txt +0 -0
- {meshcore_cli-1.1.40.dist-info → meshcore_cli-1.2.0.dist-info}/licenses/LICENSE +0 -0
meshcore_cli/meshcore_cli.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/python
|
|
2
|
-
"""
|
|
2
|
+
"""
|
|
3
3
|
mccli.py : CLI interface to MeschCore BLE companion app
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
import asyncio
|
|
6
7
|
import os, sys
|
|
7
8
|
import time, datetime
|
|
@@ -9,21 +10,27 @@ import getopt, json, shlex, re
|
|
|
9
10
|
import logging
|
|
10
11
|
import requests
|
|
11
12
|
from bleak import BleakScanner, BleakClient
|
|
13
|
+
from bleak.exc import BleakError
|
|
12
14
|
import serial.tools.list_ports
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
import traceback
|
|
15
17
|
from prompt_toolkit.shortcuts import PromptSession
|
|
16
18
|
from prompt_toolkit.shortcuts import CompleteStyle
|
|
17
19
|
from prompt_toolkit.completion import NestedCompleter
|
|
20
|
+
from prompt_toolkit.completion import CompleteEvent, Completer, Completion
|
|
18
21
|
from prompt_toolkit.history import FileHistory
|
|
19
22
|
from prompt_toolkit.formatted_text import ANSI
|
|
20
23
|
from prompt_toolkit.key_binding import KeyBindings
|
|
21
24
|
from prompt_toolkit.shortcuts import radiolist_dialog
|
|
25
|
+
from prompt_toolkit.completion.word_completer import WordCompleter
|
|
26
|
+
from prompt_toolkit.document import Document
|
|
27
|
+
|
|
28
|
+
import re
|
|
22
29
|
|
|
23
30
|
from meshcore import MeshCore, EventType, logger
|
|
24
31
|
|
|
25
32
|
# Version
|
|
26
|
-
VERSION = "v1.
|
|
33
|
+
VERSION = "v1.2.0"
|
|
27
34
|
|
|
28
35
|
# default ble address is stored in a config file
|
|
29
36
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -296,6 +303,29 @@ async def subscribe_to_msgs(mc, json_output=False, above=False):
|
|
|
296
303
|
CS = mc.subscribe(EventType.CHANNEL_MSG_RECV, handle_message)
|
|
297
304
|
await mc.start_auto_message_fetching()
|
|
298
305
|
|
|
306
|
+
# redefine get_completion to let user put symbols in first item
|
|
307
|
+
# and handle navigating in path ...
|
|
308
|
+
class MyNestedCompleter(NestedCompleter):
|
|
309
|
+
def get_completions( self, document, complete_event):
|
|
310
|
+
txt = document.text_before_cursor.lstrip()
|
|
311
|
+
if not " " in txt:
|
|
312
|
+
if txt != "" and txt[0] == "/" and txt.count("/") == 1:
|
|
313
|
+
opts = []
|
|
314
|
+
for k in self.options.keys():
|
|
315
|
+
if k[0] == "/" :
|
|
316
|
+
v = "/" + k.split("/")[1] #+ ("/" if k.count("/") == 2 else "")
|
|
317
|
+
if v not in opts:
|
|
318
|
+
opts.append(v)
|
|
319
|
+
else:
|
|
320
|
+
opts = self.options.keys()
|
|
321
|
+
completer = WordCompleter(
|
|
322
|
+
opts, ignore_case=self.ignore_case,
|
|
323
|
+
pattern=re.compile(r"([a-zA-Z0-9_\\/]+|[^a-zA-Z0-9_\s]+)"))
|
|
324
|
+
yield from completer.get_completions(document, complete_event)
|
|
325
|
+
else: # normal behavior for remainder
|
|
326
|
+
yield from super().get_completions(document, complete_event)
|
|
327
|
+
|
|
328
|
+
|
|
299
329
|
def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
300
330
|
contact_list = {}
|
|
301
331
|
pending_list = {}
|
|
@@ -306,7 +336,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
306
336
|
if not process_event_message.last_node is None:
|
|
307
337
|
to_list["!"] = None
|
|
308
338
|
to_list[".."] = None
|
|
309
|
-
to_list["public"] = None
|
|
310
339
|
|
|
311
340
|
it = iter(contacts.items())
|
|
312
341
|
for c in it :
|
|
@@ -332,229 +361,248 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
332
361
|
"chan" : None,
|
|
333
362
|
}
|
|
334
363
|
|
|
364
|
+
root_completion_list = {
|
|
365
|
+
"ver" : None,
|
|
366
|
+
"infos" : None,
|
|
367
|
+
"advert" : None,
|
|
368
|
+
"floodadv" : None,
|
|
369
|
+
"msg" : contact_list,
|
|
370
|
+
"wait_ack" : None,
|
|
371
|
+
"time" : None,
|
|
372
|
+
"clock" : {"sync" : None},
|
|
373
|
+
"reboot" : None,
|
|
374
|
+
"card" : None,
|
|
375
|
+
"upload_card" : None,
|
|
376
|
+
"contacts": None,
|
|
377
|
+
"pending_contacts": None,
|
|
378
|
+
"add_pending": pending_list,
|
|
379
|
+
"flush_pending": None,
|
|
380
|
+
"contact_info": contact_list,
|
|
381
|
+
"export_contact" : contact_list,
|
|
382
|
+
"upload_contact" : contact_list,
|
|
383
|
+
"share_contact" : contact_list,
|
|
384
|
+
"path": contact_list,
|
|
385
|
+
"disc_path" : contact_list,
|
|
386
|
+
"trace" : None,
|
|
387
|
+
"reset_path" : contact_list,
|
|
388
|
+
"change_path" : contact_list,
|
|
389
|
+
"change_flags" : contact_list,
|
|
390
|
+
"remove_contact" : contact_list,
|
|
391
|
+
"import_contact" : {"meshcore://":None},
|
|
392
|
+
"reload_contacts" : None,
|
|
393
|
+
"login" : contact_list,
|
|
394
|
+
"cmd" : contact_list,
|
|
395
|
+
"req_status" : contact_list,
|
|
396
|
+
"req_bstatus" : contact_list,
|
|
397
|
+
"logout" : contact_list,
|
|
398
|
+
"req_telemetry" : contact_list,
|
|
399
|
+
"req_binary" : contact_list,
|
|
400
|
+
"req_mma" : contact_list,
|
|
401
|
+
"self_telemetry" : None,
|
|
402
|
+
"get_channel": None,
|
|
403
|
+
"set_channel": None,
|
|
404
|
+
"get_channels": None,
|
|
405
|
+
"remove_channel": None,
|
|
406
|
+
"set" : {
|
|
407
|
+
"name" : None,
|
|
408
|
+
"pin" : None,
|
|
409
|
+
"radio" : {",,,":None, "f,bw,sf,cr":None},
|
|
410
|
+
"tx" : None,
|
|
411
|
+
"tuning" : {",", "af,tx_d"},
|
|
412
|
+
"lat" : None,
|
|
413
|
+
"lon" : None,
|
|
414
|
+
"coords" : None,
|
|
415
|
+
"print_snr" : {"on":None, "off": None},
|
|
416
|
+
"json_msgs" : {"on":None, "off": None},
|
|
417
|
+
"color" : {"on":None, "off":None},
|
|
418
|
+
"print_name" : {"on":None, "off":None},
|
|
419
|
+
"print_adverts" : {"on":None, "off":None},
|
|
420
|
+
"print_new_contacts" : {"on": None, "off":None},
|
|
421
|
+
"print_path_updates" : {"on":None,"off":None},
|
|
422
|
+
"classic_prompt" : {"on" : None, "off":None},
|
|
423
|
+
"manual_add_contacts" : {"on" : None, "off":None},
|
|
424
|
+
"telemetry_mode_base" : {"always" : None, "device":None, "never":None},
|
|
425
|
+
"telemetry_mode_loc" : {"always" : None, "device":None, "never":None},
|
|
426
|
+
"telemetry_mode_env" : {"always" : None, "device":None, "never":None},
|
|
427
|
+
"advert_loc_policy" : {"none" : None, "share" : None},
|
|
428
|
+
"auto_update_contacts" : {"on":None, "off":None},
|
|
429
|
+
"multi_acks" : {"on": None, "off":None},
|
|
430
|
+
"max_attempts" : None,
|
|
431
|
+
"max_flood_attempts" : None,
|
|
432
|
+
"flood_after" : None,
|
|
433
|
+
},
|
|
434
|
+
"get" : {"name":None,
|
|
435
|
+
"bat":None,
|
|
436
|
+
"fstats": None,
|
|
437
|
+
"radio":None,
|
|
438
|
+
"tx":None,
|
|
439
|
+
"coords":None,
|
|
440
|
+
"lat":None,
|
|
441
|
+
"lon":None,
|
|
442
|
+
"print_snr":None,
|
|
443
|
+
"json_msgs":None,
|
|
444
|
+
"color":None,
|
|
445
|
+
"print_name":None,
|
|
446
|
+
"print_adverts":None,
|
|
447
|
+
"print_path_updates":None,
|
|
448
|
+
"print_new_contacts":None,
|
|
449
|
+
"classic_prompt":None,
|
|
450
|
+
"manual_add_contacts":None,
|
|
451
|
+
"telemetry_mode_base":None,
|
|
452
|
+
"telemetry_mode_loc":None,
|
|
453
|
+
"telemetry_mode_env":None,
|
|
454
|
+
"advert_loc_policy":None,
|
|
455
|
+
"auto_update_contacts":None,
|
|
456
|
+
"multi_acks":None,
|
|
457
|
+
"max_attempts":None,
|
|
458
|
+
"max_flood_attempts":None,
|
|
459
|
+
"flood_after":None,
|
|
460
|
+
"custom":None,
|
|
461
|
+
},
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
contact_completion_list = {
|
|
465
|
+
"contact_info": None,
|
|
466
|
+
"export_contact" : None,
|
|
467
|
+
"share_contact" : None,
|
|
468
|
+
"upload_contact" : None,
|
|
469
|
+
"path": None,
|
|
470
|
+
"disc_path": None,
|
|
471
|
+
"trace": None,
|
|
472
|
+
"dtrace": None,
|
|
473
|
+
"reset_path" : None,
|
|
474
|
+
"change_path" : None,
|
|
475
|
+
"change_flags" : None,
|
|
476
|
+
"req_telemetry" : None,
|
|
477
|
+
"req_binary" : None,
|
|
478
|
+
"forget_password" : None,
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
client_completion_list = dict(contact_completion_list)
|
|
482
|
+
client_completion_list.update({
|
|
483
|
+
"get" : { "timeout":None, },
|
|
484
|
+
"set" : { "timeout":None, },
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
repeater_completion_list = dict(contact_completion_list)
|
|
488
|
+
repeater_completion_list.update({
|
|
489
|
+
"login" : None,
|
|
490
|
+
"logout" : None,
|
|
491
|
+
"req_status" : None,
|
|
492
|
+
"req_bstatus" : None,
|
|
493
|
+
"cmd" : None,
|
|
494
|
+
"ver" : None,
|
|
495
|
+
"advert" : None,
|
|
496
|
+
"time" : None,
|
|
497
|
+
"clock" : {"sync" : None},
|
|
498
|
+
"reboot" : None,
|
|
499
|
+
"start ota" : None,
|
|
500
|
+
"password" : None,
|
|
501
|
+
"neighbors" : None,
|
|
502
|
+
"req_acl":None,
|
|
503
|
+
"setperm":contact_list,
|
|
504
|
+
"gps" : {"on":None,"off":None,"sync":None,"setloc":None,
|
|
505
|
+
"advert" : {"none": None, "share": None, "prefs": None},
|
|
506
|
+
},
|
|
507
|
+
"sensor": {"list": None, "set": {"gps": None}, "get": {"gps": None}},
|
|
508
|
+
"get" : {"name" : None,
|
|
509
|
+
"role":None,
|
|
510
|
+
"radio" : None,
|
|
511
|
+
"freq":None,
|
|
512
|
+
"tx":None,
|
|
513
|
+
"af" : None,
|
|
514
|
+
"repeat" : None,
|
|
515
|
+
"allow.read.only" : None,
|
|
516
|
+
"flood.advert.interval" : None,
|
|
517
|
+
"flood.max":None,
|
|
518
|
+
"advert.interval" : None,
|
|
519
|
+
"guest.password" : None,
|
|
520
|
+
"rxdelay": None,
|
|
521
|
+
"txdelay": None,
|
|
522
|
+
"direct.tx_delay":None,
|
|
523
|
+
"public.key":None,
|
|
524
|
+
"lat" : None,
|
|
525
|
+
"lon" : None,
|
|
526
|
+
"telemetry" : None,
|
|
527
|
+
"status" : None,
|
|
528
|
+
"timeout" : None,
|
|
529
|
+
"acl":None,
|
|
530
|
+
"bridge.enabled":None,
|
|
531
|
+
"bridge.delay":None,
|
|
532
|
+
"bridge.source":None,
|
|
533
|
+
"bridge.baud":None,
|
|
534
|
+
"bridge.secret":None,
|
|
535
|
+
"bridge.type":None,
|
|
536
|
+
},
|
|
537
|
+
"set" : {"name" : None,
|
|
538
|
+
"radio" : {",,,":None, "f,bw,sf,cr": None},
|
|
539
|
+
"freq" : None,
|
|
540
|
+
"tx" : None,
|
|
541
|
+
"af": None,
|
|
542
|
+
"repeat" : {"on": None, "off": None},
|
|
543
|
+
"flood.advert.interval" : None,
|
|
544
|
+
"flood.max" : None,
|
|
545
|
+
"advert.interval" : None,
|
|
546
|
+
"guest.password" : None,
|
|
547
|
+
"allow.read.only" : {"on": None, "off": None},
|
|
548
|
+
"rxdelay" : None,
|
|
549
|
+
"txdelay": None,
|
|
550
|
+
"direct.txdelay" : None,
|
|
551
|
+
"lat" : None,
|
|
552
|
+
"lon" : None,
|
|
553
|
+
"timeout" : None,
|
|
554
|
+
"perm":contact_list,
|
|
555
|
+
"bridge.enabled":{"on": None, "off": None},
|
|
556
|
+
"bridge.delay":None,
|
|
557
|
+
"bridge.source":None,
|
|
558
|
+
"bridge.baud":None,
|
|
559
|
+
"bridge.secret":None,
|
|
560
|
+
},
|
|
561
|
+
"erase": None,
|
|
562
|
+
"log" : {"start" : None, "stop" : None, "erase" : None}
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
sensor_completion_list = dict(repeater_completion_list)
|
|
566
|
+
sensor_completion_list.update({"req_mma":{"begin end":None}})
|
|
567
|
+
sensor_completion_list["get"].update({ "mma":None, })
|
|
568
|
+
|
|
335
569
|
if to is None :
|
|
336
|
-
completion_list.update(
|
|
337
|
-
"ver" : None,
|
|
338
|
-
"infos" : None,
|
|
339
|
-
"advert" : None,
|
|
340
|
-
"floodadv" : None,
|
|
341
|
-
"msg" : contact_list,
|
|
342
|
-
"wait_ack" : None,
|
|
343
|
-
"time" : None,
|
|
344
|
-
"clock" : {"sync" : None},
|
|
345
|
-
"reboot" : None,
|
|
346
|
-
"card" : None,
|
|
347
|
-
"upload_card" : None,
|
|
348
|
-
"contacts": None,
|
|
349
|
-
"pending_contacts": None,
|
|
350
|
-
"add_pending": pending_list,
|
|
351
|
-
"flush_pending": None,
|
|
352
|
-
"contact_info": contact_list,
|
|
353
|
-
"export_contact" : contact_list,
|
|
354
|
-
"upload_contact" : contact_list,
|
|
355
|
-
"share_contact" : contact_list,
|
|
356
|
-
"path": contact_list,
|
|
357
|
-
"disc_path" : contact_list,
|
|
358
|
-
"trace" : None,
|
|
359
|
-
"reset_path" : contact_list,
|
|
360
|
-
"change_path" : contact_list,
|
|
361
|
-
"change_flags" : contact_list,
|
|
362
|
-
"remove_contact" : contact_list,
|
|
363
|
-
"import_contact" : {"meshcore://":None},
|
|
364
|
-
"reload_contacts" : None,
|
|
365
|
-
"login" : contact_list,
|
|
366
|
-
"cmd" : contact_list,
|
|
367
|
-
"req_status" : contact_list,
|
|
368
|
-
"req_bstatus" : contact_list,
|
|
369
|
-
"logout" : contact_list,
|
|
370
|
-
"req_telemetry" : contact_list,
|
|
371
|
-
"req_binary" : contact_list,
|
|
372
|
-
"req_mma" : contact_list,
|
|
373
|
-
"self_telemetry" : None,
|
|
374
|
-
"get_channel": None,
|
|
375
|
-
"set_channel": None,
|
|
376
|
-
"get_channels": None,
|
|
377
|
-
"remove_channel": None,
|
|
378
|
-
"set" : {
|
|
379
|
-
"name" : None,
|
|
380
|
-
"pin" : None,
|
|
381
|
-
"radio" : {",,,":None, "f,bw,sf,cr":None},
|
|
382
|
-
"tx" : None,
|
|
383
|
-
"tuning" : {",", "af,tx_d"},
|
|
384
|
-
"lat" : None,
|
|
385
|
-
"lon" : None,
|
|
386
|
-
"coords" : None,
|
|
387
|
-
"print_snr" : {"on":None, "off": None},
|
|
388
|
-
"json_msgs" : {"on":None, "off": None},
|
|
389
|
-
"color" : {"on":None, "off":None},
|
|
390
|
-
"print_name" : {"on":None, "off":None},
|
|
391
|
-
"print_adverts" : {"on":None, "off":None},
|
|
392
|
-
"print_new_contacts" : {"on": None, "off":None},
|
|
393
|
-
"print_path_updates" : {"on":None,"off":None},
|
|
394
|
-
"classic_prompt" : {"on" : None, "off":None},
|
|
395
|
-
"manual_add_contacts" : {"on" : None, "off":None},
|
|
396
|
-
"telemetry_mode_base" : {"always" : None, "device":None, "never":None},
|
|
397
|
-
"telemetry_mode_loc" : {"always" : None, "device":None, "never":None},
|
|
398
|
-
"telemetry_mode_env" : {"always" : None, "device":None, "never":None},
|
|
399
|
-
"advert_loc_policy" : {"none" : None, "share" : None},
|
|
400
|
-
"auto_update_contacts" : {"on":None, "off":None},
|
|
401
|
-
"multi_acks" : {"on": None, "off":None},
|
|
402
|
-
"max_attempts" : None,
|
|
403
|
-
"max_flood_attempts" : None,
|
|
404
|
-
"flood_after" : None,
|
|
405
|
-
},
|
|
406
|
-
"get" : {"name":None,
|
|
407
|
-
"bat":None,
|
|
408
|
-
"fstats": None,
|
|
409
|
-
"radio":None,
|
|
410
|
-
"tx":None,
|
|
411
|
-
"coords":None,
|
|
412
|
-
"lat":None,
|
|
413
|
-
"lon":None,
|
|
414
|
-
"print_snr":None,
|
|
415
|
-
"json_msgs":None,
|
|
416
|
-
"color":None,
|
|
417
|
-
"print_name":None,
|
|
418
|
-
"print_adverts":None,
|
|
419
|
-
"print_path_updates":None,
|
|
420
|
-
"print_new_contacts":None,
|
|
421
|
-
"classic_prompt":None,
|
|
422
|
-
"manual_add_contacts":None,
|
|
423
|
-
"telemetry_mode_base":None,
|
|
424
|
-
"telemetry_mode_loc":None,
|
|
425
|
-
"telemetry_mode_env":None,
|
|
426
|
-
"advert_loc_policy":None,
|
|
427
|
-
"auto_update_contacts":None,
|
|
428
|
-
"multi_acks":None,
|
|
429
|
-
"max_attempts":None,
|
|
430
|
-
"max_flood_attempts":None,
|
|
431
|
-
"flood_after":None,
|
|
432
|
-
"custom":None,
|
|
433
|
-
},
|
|
434
|
-
})
|
|
570
|
+
completion_list.update(dict(root_completion_list))
|
|
435
571
|
completion_list["set"].update(make_completion_dict.custom_vars)
|
|
436
572
|
completion_list["get"].update(make_completion_dict.custom_vars)
|
|
437
573
|
else :
|
|
438
574
|
completion_list.update({
|
|
439
575
|
"send" : None,
|
|
440
576
|
})
|
|
577
|
+
if to['type'] == 1 :
|
|
578
|
+
completion_list.update(client_completion_list)
|
|
579
|
+
if to['type'] == 2 or to['type'] == 3 : # repeaters and room servers
|
|
580
|
+
completion_list.update(repeater_completion_list)
|
|
581
|
+
if (to['type'] == 4) : #specific to sensors
|
|
582
|
+
completion_list.update(sensor_completion_list)
|
|
441
583
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
"upload_contact" : None,
|
|
448
|
-
"path": None,
|
|
449
|
-
"disc_path": None,
|
|
450
|
-
"trace": None,
|
|
451
|
-
"dtrace": None,
|
|
452
|
-
"reset_path" : None,
|
|
453
|
-
"change_path" : None,
|
|
454
|
-
"change_flags" : None,
|
|
455
|
-
"req_telemetry" : None,
|
|
456
|
-
"req_binary" : None,
|
|
457
|
-
})
|
|
584
|
+
slash_root_completion_list = {}
|
|
585
|
+
for k,v in root_completion_list.items():
|
|
586
|
+
slash_root_completion_list["/"+k]=v
|
|
587
|
+
|
|
588
|
+
completion_list.update(slash_root_completion_list)
|
|
458
589
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
if to['type'] > 1 : # repeaters and room servers
|
|
470
|
-
completion_list.update({
|
|
471
|
-
"login" : None,
|
|
472
|
-
"logout" : None,
|
|
473
|
-
"req_status" : None,
|
|
474
|
-
"req_bstatus" : None,
|
|
475
|
-
"cmd" : None,
|
|
476
|
-
"ver" : None,
|
|
477
|
-
"advert" : None,
|
|
478
|
-
"time" : None,
|
|
479
|
-
"clock" : {"sync" : None},
|
|
480
|
-
"reboot" : None,
|
|
481
|
-
"start ota" : None,
|
|
482
|
-
"password" : None,
|
|
483
|
-
"neighbors" : None,
|
|
484
|
-
"req_acl":None,
|
|
485
|
-
"setperm":contact_list,
|
|
486
|
-
"gps" : {"on":None,"off":None,"sync":None,"setloc":None,
|
|
487
|
-
"advert" : {"none": None, "share": None, "prefs": None},
|
|
488
|
-
},
|
|
489
|
-
"sensor": {"list": None, "set": {"gps": None}, "get": {"gps": None}},
|
|
490
|
-
"get" : {"name" : None,
|
|
491
|
-
"role":None,
|
|
492
|
-
"radio" : None,
|
|
493
|
-
"freq":None,
|
|
494
|
-
"tx":None,
|
|
495
|
-
"af" : None,
|
|
496
|
-
"repeat" : None,
|
|
497
|
-
"allow.read.only" : None,
|
|
498
|
-
"flood.advert.interval" : None,
|
|
499
|
-
"flood.max":None,
|
|
500
|
-
"advert.interval" : None,
|
|
501
|
-
"guest.password" : None,
|
|
502
|
-
"rxdelay": None,
|
|
503
|
-
"txdelay": None,
|
|
504
|
-
"direct.tx_delay":None,
|
|
505
|
-
"public.key":None,
|
|
506
|
-
"lat" : None,
|
|
507
|
-
"lon" : None,
|
|
508
|
-
"telemetry" : None,
|
|
509
|
-
"status" : None,
|
|
510
|
-
"timeout" : None,
|
|
511
|
-
"acl":None,
|
|
512
|
-
"bridge.enabled":None,
|
|
513
|
-
"bridge.delay":None,
|
|
514
|
-
"bridge.source":None,
|
|
515
|
-
"bridge.baud":None,
|
|
516
|
-
"bridge.secret":None,
|
|
517
|
-
"bridge.type":None,
|
|
518
|
-
},
|
|
519
|
-
"set" : {"name" : None,
|
|
520
|
-
"radio" : {",,,":None, "f,bw,sf,cr": None},
|
|
521
|
-
"freq" : None,
|
|
522
|
-
"tx" : None,
|
|
523
|
-
"af": None,
|
|
524
|
-
"repeat" : {"on": None, "off": None},
|
|
525
|
-
"flood.advert.interval" : None,
|
|
526
|
-
"flood.max" : None,
|
|
527
|
-
"advert.interval" : None,
|
|
528
|
-
"guest.password" : None,
|
|
529
|
-
"allow.read.only" : {"on": None, "off": None},
|
|
530
|
-
"rxdelay" : None,
|
|
531
|
-
"txdelay": None,
|
|
532
|
-
"direct.txdelay" : None,
|
|
533
|
-
"lat" : None,
|
|
534
|
-
"lon" : None,
|
|
535
|
-
"timeout" : None,
|
|
536
|
-
"perm":contact_list,
|
|
537
|
-
"bridge.enabled":{"on": None, "off": None},
|
|
538
|
-
"bridge.delay":None,
|
|
539
|
-
"bridge.source":None,
|
|
540
|
-
"bridge.baud":None,
|
|
541
|
-
"bridge.secret":None,
|
|
542
|
-
},
|
|
543
|
-
"erase": None,
|
|
544
|
-
"log" : {"start" : None, "stop" : None, "erase" : None}
|
|
545
|
-
})
|
|
590
|
+
slash_contacts_completion_list = {}
|
|
591
|
+
for k,v in contacts.items():
|
|
592
|
+
d={}
|
|
593
|
+
if v["type"] == 1:
|
|
594
|
+
l = client_completion_list
|
|
595
|
+
elif v["type"] == 2 or v["type"] == 3:
|
|
596
|
+
l = repeater_completion_list
|
|
597
|
+
elif v["type"] == 4:
|
|
598
|
+
l = sensor_completion_list
|
|
546
599
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
"req_mma":{"begin end":None},
|
|
550
|
-
})
|
|
600
|
+
for kk, vv in l.items():
|
|
601
|
+
d["/" + v["adv_name"] + "/" + kk] = vv
|
|
551
602
|
|
|
552
|
-
|
|
553
|
-
"mma":None,
|
|
554
|
-
})
|
|
603
|
+
slash_contacts_completion_list.update(d)
|
|
555
604
|
|
|
556
|
-
|
|
557
|
-
})
|
|
605
|
+
completion_list.update(slash_contacts_completion_list)
|
|
558
606
|
|
|
559
607
|
completion_list.update({
|
|
560
608
|
"script" : None,
|
|
@@ -573,7 +621,6 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
573
621
|
contact = to
|
|
574
622
|
prev_contact = None
|
|
575
623
|
|
|
576
|
-
# await get_contacts(mc, anim=True)
|
|
577
624
|
await get_contacts(mc, anim=True)
|
|
578
625
|
await get_channels(mc, anim=True)
|
|
579
626
|
await subscribe_to_msgs(mc, above=True)
|
|
@@ -665,7 +712,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
665
712
|
session.app.ttimeoutlen = 0.2
|
|
666
713
|
session.app.timeoutlen = 0.2
|
|
667
714
|
|
|
668
|
-
completer =
|
|
715
|
+
completer = MyNestedCompleter.from_nested_dict(
|
|
669
716
|
make_completion_dict(mc.contacts,
|
|
670
717
|
mc.pending_contacts,
|
|
671
718
|
to=contact,
|
|
@@ -684,6 +731,24 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
684
731
|
args = shlex.split(line[1:])
|
|
685
732
|
await process_cmds(mc, args)
|
|
686
733
|
|
|
734
|
+
elif line.startswith("/") :
|
|
735
|
+
path = line.split(" ", 1)[0]
|
|
736
|
+
if path.count("/") == 1:
|
|
737
|
+
args = shlex.split(line[1:])
|
|
738
|
+
await process_cmds(mc, args)
|
|
739
|
+
else:
|
|
740
|
+
cmdline = line[1:].split("/",1)[1]
|
|
741
|
+
contact_name = path[1:].split("/",1)[0]
|
|
742
|
+
tct = mc.get_contact_by_name(contact_name)
|
|
743
|
+
if tct is None:
|
|
744
|
+
print(f"{contact_name} is not a contact")
|
|
745
|
+
else:
|
|
746
|
+
if not await process_contact_chat_line(mc, tct, cmdline):
|
|
747
|
+
if tct["type"] == 1:
|
|
748
|
+
last_ack = await msg_ack(mc, tct, cmdline)
|
|
749
|
+
else :
|
|
750
|
+
await process_cmds(mc, ["cmd", tct["adv_name"], cmdline])
|
|
751
|
+
|
|
687
752
|
elif line.startswith("to ") : # dest
|
|
688
753
|
dest = line[3:]
|
|
689
754
|
if dest.startswith("\"") or dest.startswith("\'") : # if name starts with a quote
|
|
@@ -706,7 +771,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
706
771
|
elif dest == "!" :
|
|
707
772
|
nc = process_event_message.last_node
|
|
708
773
|
else :
|
|
709
|
-
chan = await get_channel_by_name(mc, dest)
|
|
774
|
+
chan = await get_channel_by_name(mc, dest)
|
|
710
775
|
if chan is None :
|
|
711
776
|
print(f"Contact '{dest}' not found in contacts.")
|
|
712
777
|
nc = contact
|
|
@@ -751,104 +816,8 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
751
816
|
args = shlex.split(line)
|
|
752
817
|
await process_cmds(mc, args)
|
|
753
818
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
line == "ec" or line == "export_contact" or\
|
|
757
|
-
line == "uc" or line == "upload_contact" or\
|
|
758
|
-
line == "rp" or line == "reset_path" or\
|
|
759
|
-
line == "dp" or line == "disc_path" or\
|
|
760
|
-
line == "contact_info" or line == "ci" or\
|
|
761
|
-
line == "req_status" or line == "rs" or\
|
|
762
|
-
line == "req_bstatus" or line == "rbs" or\
|
|
763
|
-
line == "req_telemetry" or line == "rt" or\
|
|
764
|
-
line == "req_acl" or\
|
|
765
|
-
line == "path" or\
|
|
766
|
-
line == "logout" ) :
|
|
767
|
-
args = [line, contact['adv_name']]
|
|
768
|
-
await process_cmds(mc, args)
|
|
769
|
-
|
|
770
|
-
elif contact["type"] > 0 and line.startswith("set timeout "):
|
|
771
|
-
cmds=line.split(" ")
|
|
772
|
-
contact["timeout"] = float(cmds[2])
|
|
773
|
-
|
|
774
|
-
elif contact["type"] > 0 and line == "get timeout":
|
|
775
|
-
print(f"timeout: {0 if not 'timeout' in contact else contact['timeout']}")
|
|
776
|
-
|
|
777
|
-
elif contact["type"] == 4 and\
|
|
778
|
-
(line.startswith("get mma ")) or\
|
|
779
|
-
contact["type"] > 1 and\
|
|
780
|
-
(line.startswith("get telemetry") or line.startswith("get status") or line.startswith("get acl")):
|
|
781
|
-
cmds = line.split(" ")
|
|
782
|
-
args = [f"req_{cmds[1]}", contact['adv_name']]
|
|
783
|
-
if len(cmds) > 2 :
|
|
784
|
-
args = args + cmds[2:]
|
|
785
|
-
if line.startswith("get mma ") and len(args) < 4:
|
|
786
|
-
args.append("0")
|
|
787
|
-
await process_cmds(mc, args)
|
|
788
|
-
|
|
789
|
-
# special treatment for setperm to support contact name as param
|
|
790
|
-
elif contact["type"] > 1 and\
|
|
791
|
-
(line.startswith("setperm ") or line.startswith("set perm ")):
|
|
792
|
-
try:
|
|
793
|
-
cmds = shlex.split(line)
|
|
794
|
-
off = 1 if line.startswith("set perm") else 0
|
|
795
|
-
name = cmds[1 + off]
|
|
796
|
-
perm_string = cmds[2 + off]
|
|
797
|
-
if (perm_string.startswith("0x")):
|
|
798
|
-
perm = int(perm_string,0)
|
|
799
|
-
elif (perm_string.startswith("#")):
|
|
800
|
-
perm = int(perm_string[1:])
|
|
801
|
-
else:
|
|
802
|
-
perm = int(perm_string,16)
|
|
803
|
-
ct=mc.get_contact_by_name(name)
|
|
804
|
-
if ct is None:
|
|
805
|
-
ct=mc.get_contact_by_key_prefix(name)
|
|
806
|
-
if ct is None:
|
|
807
|
-
if name == "self" or mc.self_info["public_key"].startswith(name):
|
|
808
|
-
key = mc.self_info["public_key"]
|
|
809
|
-
else:
|
|
810
|
-
key = name
|
|
811
|
-
else:
|
|
812
|
-
key=ct["public_key"]
|
|
813
|
-
newline=f"setperm {key} {perm}"
|
|
814
|
-
await process_cmds(mc, ["cmd", contact["adv_name"], newline])
|
|
815
|
-
except IndexError:
|
|
816
|
-
print("Wrong number of parameters")
|
|
817
|
-
|
|
818
|
-
# trace called on a contact
|
|
819
|
-
elif contact["type"] > 0 and (
|
|
820
|
-
line == "trace" or line == "tr") :
|
|
821
|
-
await print_trace_to(mc, contact)
|
|
822
|
-
|
|
823
|
-
elif contact["type"] > 0 and (
|
|
824
|
-
line == "dtrace" or line == "dt") :
|
|
825
|
-
await print_disc_trace_to(mc, contact)
|
|
826
|
-
|
|
827
|
-
# same but for commands with a parameter
|
|
828
|
-
elif contact["type"] > 0 and (line.startswith("cmd ") or\
|
|
829
|
-
line.startswith("cp ") or line.startswith("change_path ") or\
|
|
830
|
-
line.startswith("cf ") or line.startswith("change_flags ") or\
|
|
831
|
-
line.startswith("req_binary ") or\
|
|
832
|
-
line.startswith("login ")) :
|
|
833
|
-
cmds = line.split(" ", 1)
|
|
834
|
-
args = [cmds[0], contact['adv_name'], cmds[1]]
|
|
835
|
-
await process_cmds(mc, args)
|
|
836
|
-
|
|
837
|
-
elif contact["type"] == 4 and \
|
|
838
|
-
(line.startswith("req_mma ") or line.startswith('rm ')) :
|
|
839
|
-
cmds = line.split(" ")
|
|
840
|
-
if len(cmds) < 3 :
|
|
841
|
-
cmds.append("0")
|
|
842
|
-
args = [cmds[0], contact['adv_name'], cmds[1], cmds[2]]
|
|
843
|
-
await process_cmds(mc, args)
|
|
844
|
-
|
|
845
|
-
elif line.startswith(":") : # : will send a command to current recipient
|
|
846
|
-
args=["cmd", contact['adv_name'], line[1:]]
|
|
847
|
-
await process_cmds(mc, args)
|
|
848
|
-
|
|
849
|
-
elif line == "reset path" : # reset path for compat with terminal chat
|
|
850
|
-
args = ["reset_path", contact['adv_name']]
|
|
851
|
-
await process_cmds(mc, args)
|
|
819
|
+
elif await process_contact_chat_line(mc, contact, line):
|
|
820
|
+
pass
|
|
852
821
|
|
|
853
822
|
elif line == "list" : # list command from chat displays contacts on a line
|
|
854
823
|
it = iter(mc.contacts.items())
|
|
@@ -886,6 +855,165 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
886
855
|
interactive_loop.classic = False
|
|
887
856
|
interactive_loop.print_name = True
|
|
888
857
|
|
|
858
|
+
async def process_contact_chat_line(mc, contact, line):
|
|
859
|
+
if contact["type"] == 0:
|
|
860
|
+
return False
|
|
861
|
+
|
|
862
|
+
if line.startswith(":") : # : will send a command to current recipient
|
|
863
|
+
args=["cmd", contact['adv_name'], line[1:]]
|
|
864
|
+
await process_cmds(mc, args)
|
|
865
|
+
return True
|
|
866
|
+
|
|
867
|
+
if line == "reset path" : # reset path for compat with terminal chat
|
|
868
|
+
args = ["reset_path", contact['adv_name']]
|
|
869
|
+
await process_cmds(mc, args)
|
|
870
|
+
return True
|
|
871
|
+
|
|
872
|
+
# commands that take contact as second arg will be sent to recipient
|
|
873
|
+
if line == "sc" or line == "share_contact" or\
|
|
874
|
+
line == "ec" or line == "export_contact" or\
|
|
875
|
+
line == "uc" or line == "upload_contact" or\
|
|
876
|
+
line == "rp" or line == "reset_path" or\
|
|
877
|
+
line == "dp" or line == "disc_path" or\
|
|
878
|
+
line == "contact_info" or line == "ci" or\
|
|
879
|
+
line == "req_status" or line == "rs" or\
|
|
880
|
+
line == "req_bstatus" or line == "rbs" or\
|
|
881
|
+
line == "req_telemetry" or line == "rt" or\
|
|
882
|
+
line == "req_acl" or\
|
|
883
|
+
line == "path" or\
|
|
884
|
+
line == "logout" :
|
|
885
|
+
args = [line, contact['adv_name']]
|
|
886
|
+
await process_cmds(mc, args)
|
|
887
|
+
return True
|
|
888
|
+
|
|
889
|
+
# special case for rp that can be chained from cmdline
|
|
890
|
+
if line.startswith("rp ") or line.startswith("reset_path ") :
|
|
891
|
+
args = ["rp", contact['adv_name']]
|
|
892
|
+
await process_cmds(mc, args)
|
|
893
|
+
secline = line.split(" ", 1)[1]
|
|
894
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
895
|
+
return True
|
|
896
|
+
|
|
897
|
+
if line.startswith("set timeout "):
|
|
898
|
+
cmds=line.split(" ")
|
|
899
|
+
contact["timeout"] = float(cmds[2])
|
|
900
|
+
return True
|
|
901
|
+
|
|
902
|
+
if line == "get timeout":
|
|
903
|
+
print(f"timeout: {0 if not 'timeout' in contact else contact['timeout']}")
|
|
904
|
+
return True
|
|
905
|
+
|
|
906
|
+
if contact["type"] == 4 and\
|
|
907
|
+
(line.startswith("get mma ")) or\
|
|
908
|
+
contact["type"] > 1 and\
|
|
909
|
+
(line.startswith("get telemetry") or line.startswith("get status") or line.startswith("get acl")):
|
|
910
|
+
cmds = line.split(" ")
|
|
911
|
+
args = [f"req_{cmds[1]}", contact['adv_name']]
|
|
912
|
+
if len(cmds) > 2 :
|
|
913
|
+
args = args + cmds[2:]
|
|
914
|
+
if line.startswith("get mma ") and len(args) < 4:
|
|
915
|
+
args.append("0")
|
|
916
|
+
await process_cmds(mc, args)
|
|
917
|
+
return True
|
|
918
|
+
|
|
919
|
+
# special treatment for setperm to support contact name as param
|
|
920
|
+
if contact["type"] > 1 and\
|
|
921
|
+
(line.startswith("setperm ") or line.startswith("set perm ")):
|
|
922
|
+
try:
|
|
923
|
+
cmds = shlex.split(line)
|
|
924
|
+
off = 1 if line.startswith("set perm") else 0
|
|
925
|
+
name = cmds[1 + off]
|
|
926
|
+
perm_string = cmds[2 + off]
|
|
927
|
+
if (perm_string.startswith("0x")):
|
|
928
|
+
perm = int(perm_string,0)
|
|
929
|
+
elif (perm_string.startswith("#")):
|
|
930
|
+
perm = int(perm_string[1:])
|
|
931
|
+
else:
|
|
932
|
+
perm = int(perm_string,16)
|
|
933
|
+
ct=mc.get_contact_by_name(name)
|
|
934
|
+
if ct is None:
|
|
935
|
+
ct=mc.get_contact_by_key_prefix(name)
|
|
936
|
+
if ct is None:
|
|
937
|
+
if name == "self" or mc.self_info["public_key"].startswith(name):
|
|
938
|
+
key = mc.self_info["public_key"]
|
|
939
|
+
else:
|
|
940
|
+
key = name
|
|
941
|
+
else:
|
|
942
|
+
key=ct["public_key"]
|
|
943
|
+
newline=f"setperm {key} {perm}"
|
|
944
|
+
await process_cmds(mc, ["cmd", contact["adv_name"], newline])
|
|
945
|
+
except IndexError:
|
|
946
|
+
print("Wrong number of parameters")
|
|
947
|
+
return True
|
|
948
|
+
|
|
949
|
+
# trace called on a contact
|
|
950
|
+
if line == "trace" or line == "tr" :
|
|
951
|
+
await print_trace_to(mc, contact)
|
|
952
|
+
return True
|
|
953
|
+
|
|
954
|
+
if line == "dtrace" or line == "dt" :
|
|
955
|
+
await print_disc_trace_to(mc, contact)
|
|
956
|
+
return True
|
|
957
|
+
|
|
958
|
+
# same but for commands with a parameter
|
|
959
|
+
if line.startswith("cmd ") or\
|
|
960
|
+
line.startswith("cp ") or line.startswith("change_path ") or\
|
|
961
|
+
line.startswith("cf ") or line.startswith("change_flags ") or\
|
|
962
|
+
line.startswith("req_binary ") or\
|
|
963
|
+
line.startswith("login ") :
|
|
964
|
+
cmds = line.split(" ", 1)
|
|
965
|
+
args = [cmds[0], contact['adv_name'], cmds[1]]
|
|
966
|
+
await process_cmds(mc, args)
|
|
967
|
+
return True
|
|
968
|
+
|
|
969
|
+
if line == "login": # use stored password or prompt for it
|
|
970
|
+
password_file = ""
|
|
971
|
+
password = ""
|
|
972
|
+
if os.path.isdir(MCCLI_CONFIG_DIR) :
|
|
973
|
+
password_file = MCCLI_CONFIG_DIR + contact['adv_name'] + ".pass"
|
|
974
|
+
if os.path.exists(password_file) :
|
|
975
|
+
with open(password_file, "r", encoding="utf-8") as f :
|
|
976
|
+
password=f.readline().strip()
|
|
977
|
+
|
|
978
|
+
if password == "":
|
|
979
|
+
try:
|
|
980
|
+
sess = PromptSession("Password: ", is_password=True)
|
|
981
|
+
password = await sess.prompt_async()
|
|
982
|
+
except EOFError:
|
|
983
|
+
logger.info("Canceled")
|
|
984
|
+
return True
|
|
985
|
+
|
|
986
|
+
if password_file != "":
|
|
987
|
+
with open(password_file, "w", encoding="utf-8") as f :
|
|
988
|
+
f.write(password)
|
|
989
|
+
|
|
990
|
+
args = ["login", contact['adv_name'], password]
|
|
991
|
+
await process_cmds(mc, args)
|
|
992
|
+
return True
|
|
993
|
+
|
|
994
|
+
if line.startswith("forget_password") or line.startswith("fp"):
|
|
995
|
+
password_file = MCCLI_CONFIG_DIR + contact['adv_name'] + ".pass"
|
|
996
|
+
if os.path.exists(password_file):
|
|
997
|
+
os.remove(password_file)
|
|
998
|
+
try:
|
|
999
|
+
secline = line.split(" ", 1)[1]
|
|
1000
|
+
await process_contact_chat_line(mc, contact, secline)
|
|
1001
|
+
except IndexError:
|
|
1002
|
+
pass
|
|
1003
|
+
return True
|
|
1004
|
+
|
|
1005
|
+
if contact["type"] == 4 and \
|
|
1006
|
+
(line.startswith("req_mma ") or line.startswith('rm ')) :
|
|
1007
|
+
cmds = line.split(" ")
|
|
1008
|
+
if len(cmds) < 3 :
|
|
1009
|
+
cmds.append("0")
|
|
1010
|
+
args = [cmds[0], contact['adv_name'], cmds[1], cmds[2]]
|
|
1011
|
+
await process_cmds(mc, args)
|
|
1012
|
+
return True
|
|
1013
|
+
|
|
1014
|
+
return False
|
|
1015
|
+
|
|
1016
|
+
|
|
889
1017
|
async def send_cmd (mc, contact, cmd) :
|
|
890
1018
|
res = await mc.commands.send_cmd(contact, cmd)
|
|
891
1019
|
if not res is None and not res.type == EventType.ERROR:
|
|
@@ -909,7 +1037,7 @@ async def send_chan_msg(mc, nb, msg):
|
|
|
909
1037
|
sent["text"] = msg
|
|
910
1038
|
sent["txt_type"] = 0
|
|
911
1039
|
sent["name"] = mc.self_info['name']
|
|
912
|
-
await log_message(mc, sent)
|
|
1040
|
+
await log_message(mc, sent)
|
|
913
1041
|
return res
|
|
914
1042
|
|
|
915
1043
|
async def send_msg (mc, contact, msg) :
|
|
@@ -1002,6 +1130,9 @@ async def get_channel_by_name (mc, name):
|
|
|
1002
1130
|
return None
|
|
1003
1131
|
|
|
1004
1132
|
async def get_contacts (mc, anim=False, lastomod=0, timeout=5) :
|
|
1133
|
+
if mc._contacts:
|
|
1134
|
+
return
|
|
1135
|
+
|
|
1005
1136
|
if anim:
|
|
1006
1137
|
print("Fetching contacts ", end="", flush=True)
|
|
1007
1138
|
|
|
@@ -1020,7 +1151,7 @@ async def get_contacts (mc, anim=False, lastomod=0, timeout=5) :
|
|
|
1020
1151
|
done, pending = await asyncio.wait(
|
|
1021
1152
|
futures, timeout=timeout, return_when=asyncio.FIRST_COMPLETED
|
|
1022
1153
|
)
|
|
1023
|
-
|
|
1154
|
+
|
|
1024
1155
|
# Check if any future completed successfully
|
|
1025
1156
|
if len(done) == 0:
|
|
1026
1157
|
logger.debug("Timeout while getting contacts")
|
|
@@ -1040,7 +1171,7 @@ async def get_contacts (mc, anim=False, lastomod=0, timeout=5) :
|
|
|
1040
1171
|
if anim:
|
|
1041
1172
|
if event.type == EventType.CONTACTS:
|
|
1042
1173
|
print ((len(event.payload)-contact_nb)*"." + " Done")
|
|
1043
|
-
else :
|
|
1174
|
+
else :
|
|
1044
1175
|
print(" Error")
|
|
1045
1176
|
for future in pending:
|
|
1046
1177
|
future.cancel()
|
|
@@ -1702,7 +1833,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1702
1833
|
res = await set_channel(mc, cmds[1], cmds[2])
|
|
1703
1834
|
elif len(cmds[3]) != 32:
|
|
1704
1835
|
res = None
|
|
1705
|
-
else:
|
|
1836
|
+
else:
|
|
1706
1837
|
res = await set_channel(mc, cmds[1], cmds[2], bytes.fromhex(cmds[3]))
|
|
1707
1838
|
if res is None:
|
|
1708
1839
|
print("Error setting channel")
|
|
@@ -1722,8 +1853,8 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1722
1853
|
case "msg" | "m" | "{" : # sends to a contact from name
|
|
1723
1854
|
argnum = 2
|
|
1724
1855
|
dest = None
|
|
1725
|
-
|
|
1726
|
-
if len(cmds[1]) == 12: # possibly an hex prefix
|
|
1856
|
+
|
|
1857
|
+
if len(cmds[1]) == 12: # possibly an hex prefix
|
|
1727
1858
|
try:
|
|
1728
1859
|
dest = bytes.fromhex(cmds[1])
|
|
1729
1860
|
except ValueError:
|
|
@@ -1800,11 +1931,18 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1800
1931
|
|
|
1801
1932
|
case "trace" | "tr":
|
|
1802
1933
|
argnum = 1
|
|
1803
|
-
|
|
1934
|
+
path = cmds[1]
|
|
1935
|
+
plen = int(len(path)/2)
|
|
1936
|
+
if plen > 1 and path.count(",") == 0:
|
|
1937
|
+
path = cmds[1][0:2]
|
|
1938
|
+
for i in range(1, plen):
|
|
1939
|
+
path = path + "," + cmds[1][2*i:2*i+2]
|
|
1940
|
+
|
|
1941
|
+
res = await mc.commands.send_trace(path=path)
|
|
1804
1942
|
if res and res.type != EventType.ERROR:
|
|
1805
1943
|
tag= int.from_bytes(res.payload['expected_ack'], byteorder="little")
|
|
1806
1944
|
timeout = res.payload["suggested_timeout"] / 1000 * 1.2
|
|
1807
|
-
ev = await mc.wait_for_event(EventType.TRACE_DATA,
|
|
1945
|
+
ev = await mc.wait_for_event(EventType.TRACE_DATA,
|
|
1808
1946
|
attribute_filters={"tag": tag},
|
|
1809
1947
|
timeout=timeout)
|
|
1810
1948
|
if ev is None:
|
|
@@ -1856,7 +1994,12 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1856
1994
|
else:
|
|
1857
1995
|
print(f"Unknown contact {cmds[1]}")
|
|
1858
1996
|
else:
|
|
1859
|
-
|
|
1997
|
+
password = cmds[2]
|
|
1998
|
+
if password == "$":
|
|
1999
|
+
sess = PromptSession("Password: ", is_password=True)
|
|
2000
|
+
password = await sess.prompt_async()
|
|
2001
|
+
|
|
2002
|
+
res = await mc.commands.send_login(contact, password)
|
|
1860
2003
|
logger.debug(res)
|
|
1861
2004
|
if res.type == EventType.ERROR:
|
|
1862
2005
|
if json_output :
|
|
@@ -1938,7 +2081,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1938
2081
|
print("Timeout waiting telemetry")
|
|
1939
2082
|
else :
|
|
1940
2083
|
print(json.dumps(res.payload, indent=4))
|
|
1941
|
-
|
|
2084
|
+
|
|
1942
2085
|
case "disc_path" | "dp" :
|
|
1943
2086
|
argnum = 1
|
|
1944
2087
|
await mc.ensure_contacts()
|
|
@@ -2438,6 +2581,9 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2438
2581
|
except IndexError:
|
|
2439
2582
|
logger.error("Error in parameters, returning")
|
|
2440
2583
|
return None
|
|
2584
|
+
except EOFError:
|
|
2585
|
+
logger.error("Cancelled")
|
|
2586
|
+
return None
|
|
2441
2587
|
|
|
2442
2588
|
async def process_cmds (mc, args, json_output=False) :
|
|
2443
2589
|
cmds = args
|
|
@@ -2568,7 +2714,7 @@ async def main(argv):
|
|
|
2568
2714
|
with open(MCCLI_ADDRESS, encoding="utf-8") as f :
|
|
2569
2715
|
address = f.readline().strip()
|
|
2570
2716
|
|
|
2571
|
-
opts, args = getopt.getopt(argv, "a:d:s:ht:p:b:
|
|
2717
|
+
opts, args = getopt.getopt(argv, "a:d:s:ht:p:b:fjDhvSlT:P")
|
|
2572
2718
|
for opt, arg in opts :
|
|
2573
2719
|
match opt:
|
|
2574
2720
|
case "-d" : # name specified on cmdline
|
|
@@ -2598,25 +2744,34 @@ async def main(argv):
|
|
|
2598
2744
|
case "-v":
|
|
2599
2745
|
version()
|
|
2600
2746
|
return
|
|
2747
|
+
case "-f": # connect to first encountered device
|
|
2748
|
+
address = ""
|
|
2601
2749
|
case "-l" :
|
|
2602
2750
|
print("BLE devices:")
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2751
|
+
try :
|
|
2752
|
+
devices = await BleakScanner.discover(timeout=timeout)
|
|
2753
|
+
if len(devices) == 0:
|
|
2754
|
+
print(" No ble device found")
|
|
2755
|
+
for d in devices :
|
|
2756
|
+
if not d.name is None and d.name.startswith("MeshCore-"):
|
|
2757
|
+
print(f" {d.address} {d.name}")
|
|
2758
|
+
except BleakError:
|
|
2759
|
+
print(" No BLE HW")
|
|
2609
2760
|
print("\nSerial ports:")
|
|
2610
2761
|
ports = serial.tools.list_ports.comports()
|
|
2611
2762
|
for port, desc, hwid in sorted(ports):
|
|
2612
2763
|
print(f" {port:<18} {desc} [{hwid}]")
|
|
2613
2764
|
return
|
|
2614
2765
|
case "-S" :
|
|
2615
|
-
devices = await BleakScanner.discover(timeout=timeout)
|
|
2616
2766
|
choices = []
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2767
|
+
|
|
2768
|
+
try :
|
|
2769
|
+
devices = await BleakScanner.discover(timeout=timeout)
|
|
2770
|
+
for d in devices:
|
|
2771
|
+
if not d.name is None and d.name.startswith("MeshCore-"):
|
|
2772
|
+
choices.append(({"type":"ble","device":d}, f"{d.address:<22} {d.name}"))
|
|
2773
|
+
except BleakError:
|
|
2774
|
+
logger.info("No BLE Device")
|
|
2620
2775
|
|
|
2621
2776
|
ports = serial.tools.list_ports.comports()
|
|
2622
2777
|
for port, desc, hwid in sorted(ports):
|
|
@@ -2642,7 +2797,7 @@ async def main(argv):
|
|
|
2642
2797
|
else:
|
|
2643
2798
|
logger.error("Invalid choice")
|
|
2644
2799
|
return
|
|
2645
|
-
|
|
2800
|
+
|
|
2646
2801
|
if (debug==True):
|
|
2647
2802
|
logger.setLevel(logging.DEBUG)
|
|
2648
2803
|
elif (json_output) :
|
|
@@ -2660,7 +2815,10 @@ async def main(argv):
|
|
|
2660
2815
|
elif address and len(address) == 36 and len(address.split("-")) == 5:
|
|
2661
2816
|
client = BleakClient(address) # mac uses uuid, we'll pass a client
|
|
2662
2817
|
else:
|
|
2663
|
-
|
|
2818
|
+
if address == "":
|
|
2819
|
+
logger.info(f"Searching first MC BLE device")
|
|
2820
|
+
else:
|
|
2821
|
+
logger.info(f"Scanning BLE for device matching {address}")
|
|
2664
2822
|
devices = await BleakScanner.discover(timeout=timeout)
|
|
2665
2823
|
found = False
|
|
2666
2824
|
for d in devices:
|
|
@@ -2685,6 +2843,26 @@ async def main(argv):
|
|
|
2685
2843
|
mc = await MeshCore.create_ble(address=address, device=device, client=client, debug=debug, only_error=json_output, pin=pin)
|
|
2686
2844
|
except ConnectionError :
|
|
2687
2845
|
logger.info("Error while connecting, retrying once ...")
|
|
2846
|
+
if device is None or client is None: # Search for device
|
|
2847
|
+
logger.info(f"Scanning BLE for device matching {address}")
|
|
2848
|
+
devices = await BleakScanner.discover(timeout=timeout)
|
|
2849
|
+
found = False
|
|
2850
|
+
for d in devices:
|
|
2851
|
+
if not d.name is None and d.name.startswith("MeshCore-") and\
|
|
2852
|
+
(address is None or address in d.name) :
|
|
2853
|
+
address=d.address
|
|
2854
|
+
device=d
|
|
2855
|
+
logger.info(f"Found device {d.name} {d.address}")
|
|
2856
|
+
found = True
|
|
2857
|
+
break
|
|
2858
|
+
elif d.address == address : # on a mac, address is an uuid
|
|
2859
|
+
device = d
|
|
2860
|
+
logger.info(f"Found device {d.name} {d.address}")
|
|
2861
|
+
found = True
|
|
2862
|
+
break
|
|
2863
|
+
if not found :
|
|
2864
|
+
logger.info(f"Couldn't find device {address}")
|
|
2865
|
+
return
|
|
2688
2866
|
try :
|
|
2689
2867
|
mc = await MeshCore.create_ble(address=address, device=device, client=client, debug=debug, only_error=json_output, pin=pin)
|
|
2690
2868
|
except ConnectionError :
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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
|
|
@@ -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=xvBECDCbnHtrK7VAEsQNHcYB1T9k3dQyJiAf_0VSct4,120884
|
|
4
|
+
meshcore_cli-1.2.0.dist-info/METADATA,sha256=OTBsXmevebzoVaxfVclgLgkkXWRYzQr5zySZUCAZd2k,11629
|
|
5
|
+
meshcore_cli-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
meshcore_cli-1.2.0.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
+
meshcore_cli-1.2.0.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
+
meshcore_cli-1.2.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=oWPnTuetuus4oaP-QQlNJx7IIeRRYGCXUG4FSYTF7AM,116312
|
|
4
|
-
meshcore_cli-1.1.40.dist-info/METADATA,sha256=wsHZfVpYP4kvQMI2KvKyHbygTO-Sgq_-8Nr6ghnnPrk,11630
|
|
5
|
-
meshcore_cli-1.1.40.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
meshcore_cli-1.1.40.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
-
meshcore_cli-1.1.40.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
-
meshcore_cli-1.1.40.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|