11#!/usr/bin/python
2- """
2+ """
33 mccli.py : CLI interface to MeschCore BLE companion app
44"""
55import asyncio
@@ -484,8 +484,8 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
484484 "neighbors" : None ,
485485 "req_acl" :None ,
486486 "setperm" :contact_list ,
487- "gps" : {"on" :None ,"off" :None ,"sync" :None ,"setloc" :None ,
488- "advert" : {"none" : None , "share" : None , "prefs" : None },
487+ "gps" : {"on" :None ,"off" :None ,"sync" :None ,"setloc" :None ,
488+ "advert" : {"none" : None , "share" : None , "prefs" : None },
489489 },
490490 "sensor" : {"list" : None , "set" : {"gps" : None }, "get" : {"gps" : None }},
491491 "get" : {"name" : None ,
@@ -681,7 +681,22 @@ def _(event):
681681 pass
682682
683683 # raw meshcli command as on command line
684- elif line .startswith ("$" ) or line .startswith ("/" ) :
684+ elif line .startswith ("/" ) :
685+ path = line .split (" " , 1 )[0 ]
686+ if path .count ("/" ) == 1 :
687+ args = shlex .split (line [1 :])
688+ await process_cmds (mc , args )
689+ else :
690+ cmdline = line [1 :].split ("/" ,1 )[1 ]
691+ contact_name = path [1 :].split ("/" ,1 )[0 ]
692+ tct = mc .get_contact_by_name (contact_name )
693+ if tct is None :
694+ print (f"{ contact_name } is not a contact" )
695+ else :
696+ if not await process_contact_chat_line (mc , tct , cmdline ):
697+ print (f"{ cmdline } not found for { contact_name } " )
698+
699+ elif line .startswith ("$" ) :
685700 args = shlex .split (line [1 :])
686701 await process_cmds (mc , args )
687702
@@ -707,7 +722,7 @@ def _(event):
707722 elif dest == "!" :
708723 nc = process_event_message .last_node
709724 else :
710- chan = await get_channel_by_name (mc , dest )
725+ chan = await get_channel_by_name (mc , dest )
711726 if chan is None :
712727 print (f"Contact '{ dest } ' not found in contacts." )
713728 nc = contact
@@ -752,104 +767,8 @@ def _(event):
752767 args = shlex .split (line )
753768 await process_cmds (mc , args )
754769
755- # commands that take contact as second arg will be sent to recipient
756- elif contact ["type" ] > 0 and (line == "sc" or line == "share_contact" or \
757- line == "ec" or line == "export_contact" or \
758- line == "uc" or line == "upload_contact" or \
759- line == "rp" or line == "reset_path" or \
760- line == "dp" or line == "disc_path" or \
761- line == "contact_info" or line == "ci" or \
762- line == "req_status" or line == "rs" or \
763- line == "req_bstatus" or line == "rbs" or \
764- line == "req_telemetry" or line == "rt" or \
765- line == "req_acl" or \
766- line == "path" or \
767- line == "logout" ) :
768- args = [line , contact ['adv_name' ]]
769- await process_cmds (mc , args )
770-
771- elif contact ["type" ] > 0 and line .startswith ("set timeout " ):
772- cmds = line .split (" " )
773- contact ["timeout" ] = float (cmds [2 ])
774-
775- elif contact ["type" ] > 0 and line == "get timeout" :
776- print (f"timeout: { 0 if not 'timeout' in contact else contact ['timeout' ]} " )
777-
778- elif contact ["type" ] == 4 and \
779- (line .startswith ("get mma " )) or \
780- contact ["type" ] > 1 and \
781- (line .startswith ("get telemetry" ) or line .startswith ("get status" ) or line .startswith ("get acl" )):
782- cmds = line .split (" " )
783- args = [f"req_{ cmds [1 ]} " , contact ['adv_name' ]]
784- if len (cmds ) > 2 :
785- args = args + cmds [2 :]
786- if line .startswith ("get mma " ) and len (args ) < 4 :
787- args .append ("0" )
788- await process_cmds (mc , args )
789-
790- # special treatment for setperm to support contact name as param
791- elif contact ["type" ] > 1 and \
792- (line .startswith ("setperm " ) or line .startswith ("set perm " )):
793- try :
794- cmds = shlex .split (line )
795- off = 1 if line .startswith ("set perm" ) else 0
796- name = cmds [1 + off ]
797- perm_string = cmds [2 + off ]
798- if (perm_string .startswith ("0x" )):
799- perm = int (perm_string ,0 )
800- elif (perm_string .startswith ("#" )):
801- perm = int (perm_string [1 :])
802- else :
803- perm = int (perm_string ,16 )
804- ct = mc .get_contact_by_name (name )
805- if ct is None :
806- ct = mc .get_contact_by_key_prefix (name )
807- if ct is None :
808- if name == "self" or mc .self_info ["public_key" ].startswith (name ):
809- key = mc .self_info ["public_key" ]
810- else :
811- key = name
812- else :
813- key = ct ["public_key" ]
814- newline = f"setperm { key } { perm } "
815- await process_cmds (mc , ["cmd" , contact ["adv_name" ], newline ])
816- except IndexError :
817- print ("Wrong number of parameters" )
818-
819- # trace called on a contact
820- elif contact ["type" ] > 0 and (
821- line == "trace" or line == "tr" ) :
822- await print_trace_to (mc , contact )
823-
824- elif contact ["type" ] > 0 and (
825- line == "dtrace" or line == "dt" ) :
826- await print_disc_trace_to (mc , contact )
827-
828- # same but for commands with a parameter
829- elif contact ["type" ] > 0 and (line .startswith ("cmd " ) or \
830- line .startswith ("cp " ) or line .startswith ("change_path " ) or \
831- line .startswith ("cf " ) or line .startswith ("change_flags " ) or \
832- line .startswith ("req_binary " ) or \
833- line .startswith ("login " )) :
834- cmds = line .split (" " , 1 )
835- args = [cmds [0 ], contact ['adv_name' ], cmds [1 ]]
836- await process_cmds (mc , args )
837-
838- elif contact ["type" ] == 4 and \
839- (line .startswith ("req_mma " ) or line .startswith ('rm ' )) :
840- cmds = line .split (" " )
841- if len (cmds ) < 3 :
842- cmds .append ("0" )
843- args = [cmds [0 ], contact ['adv_name' ], cmds [1 ], cmds [2 ]]
844- await process_cmds (mc , args )
845-
846- elif line .startswith (":" ) : # : will send a command to current recipient
847- args = ["cmd" , contact ['adv_name' ], line [1 :]]
848- await process_cmds (mc , args )
849-
850- elif line == "reset path" : # reset path for compat with terminal chat
851- args = ["reset_path" , contact ['adv_name' ]]
852- await process_cmds (mc , args )
770+ elif await process_contact_chat_line (mc , contact , line ):
771+ pass
853772
854773 elif line == "list" : # list command from chat displays contacts on a line
855774 it = iter (mc .contacts .items ())
@@ -887,6 +806,121 @@ def _(event):
887806interactive_loop .classic = False
888807interactive_loop .print_name = True
889808
809+ async def process_contact_chat_line (mc , contact , line ):
810+ if contact ["type" ] == 0 :
811+ return False
812+
813+ if line .startswith (":" ) : # : will send a command to current recipient
814+ args = ["cmd" , contact ['adv_name' ], line [1 :]]
815+ await process_cmds (mc , args )
816+ return True
817+
818+ if line == "reset path" : # reset path for compat with terminal chat
819+ args = ["reset_path" , contact ['adv_name' ]]
820+ await process_cmds (mc , args )
821+ return True
822+
823+ # commands that take contact as second arg will be sent to recipient
824+ if line == "sc" or line == "share_contact" or \
825+ line == "ec" or line == "export_contact" or \
826+ line == "uc" or line == "upload_contact" or \
827+ line == "rp" or line == "reset_path" or \
828+ line == "dp" or line == "disc_path" or \
829+ line == "contact_info" or line == "ci" or \
830+ line == "req_status" or line == "rs" or \
831+ line == "req_bstatus" or line == "rbs" or \
832+ line == "req_telemetry" or line == "rt" or \
833+ line == "req_acl" or \
834+ line == "path" or \
835+ line == "logout" :
836+ args = [line , contact ['adv_name' ]]
837+ await process_cmds (mc , args )
838+ return True
839+
840+ if line .startswith ("set timeout " ):
841+ cmds = line .split (" " )
842+ contact ["timeout" ] = float (cmds [2 ])
843+ return True
844+
845+ if line == "get timeout" :
846+ print (f"timeout: { 0 if not 'timeout' in contact else contact ['timeout' ]} " )
847+ return True
848+
849+ if contact ["type" ] == 4 and \
850+ (line .startswith ("get mma " )) or \
851+ contact ["type" ] > 1 and \
852+ (line .startswith ("get telemetry" ) or line .startswith ("get status" ) or line .startswith ("get acl" )):
853+ cmds = line .split (" " )
854+ args = [f"req_{ cmds [1 ]} " , contact ['adv_name' ]]
855+ if len (cmds ) > 2 :
856+ args = args + cmds [2 :]
857+ if line .startswith ("get mma " ) and len (args ) < 4 :
858+ args .append ("0" )
859+ await process_cmds (mc , args )
860+ return True
861+
862+ # special treatment for setperm to support contact name as param
863+ if contact ["type" ] > 1 and \
864+ (line .startswith ("setperm " ) or line .startswith ("set perm " )):
865+ try :
866+ cmds = shlex .split (line )
867+ off = 1 if line .startswith ("set perm" ) else 0
868+ name = cmds [1 + off ]
869+ perm_string = cmds [2 + off ]
870+ if (perm_string .startswith ("0x" )):
871+ perm = int (perm_string ,0 )
872+ elif (perm_string .startswith ("#" )):
873+ perm = int (perm_string [1 :])
874+ else :
875+ perm = int (perm_string ,16 )
876+ ct = mc .get_contact_by_name (name )
877+ if ct is None :
878+ ct = mc .get_contact_by_key_prefix (name )
879+ if ct is None :
880+ if name == "self" or mc .self_info ["public_key" ].startswith (name ):
881+ key = mc .self_info ["public_key" ]
882+ else :
883+ key = name
884+ else :
885+ key = ct ["public_key" ]
886+ newline = f"setperm { key } { perm } "
887+ await process_cmds (mc , ["cmd" , contact ["adv_name" ], newline ])
888+ except IndexError :
889+ print ("Wrong number of parameters" )
890+ return True
891+
892+ # trace called on a contact
893+ if line == "trace" or line == "tr" :
894+ await print_trace_to (mc , contact )
895+ return True
896+
897+ if line == "dtrace" or line == "dt" :
898+ await print_disc_trace_to (mc , contact )
899+ return True
900+
901+ # same but for commands with a parameter
902+ if line .startswith ("cmd " ) or \
903+ line .startswith ("cp " ) or line .startswith ("change_path " ) or \
904+ line .startswith ("cf " ) or line .startswith ("change_flags " ) or \
905+ line .startswith ("req_binary " ) or \
906+ line .startswith ("login " ) :
907+ cmds = line .split (" " , 1 )
908+ args = [cmds [0 ], contact ['adv_name' ], cmds [1 ]]
909+ await process_cmds (mc , args )
910+ return True
911+
912+ if contact ["type" ] == 4 and \
913+ (line .startswith ("req_mma " ) or line .startswith ('rm ' )) :
914+ cmds = line .split (" " )
915+ if len (cmds ) < 3 :
916+ cmds .append ("0" )
917+ args = [cmds [0 ], contact ['adv_name' ], cmds [1 ], cmds [2 ]]
918+ await process_cmds (mc , args )
919+ return True
920+
921+ return False
922+
923+
890924async def send_cmd (mc , contact , cmd ) :
891925 res = await mc .commands .send_cmd (contact , cmd )
892926 if not res is None and not res .type == EventType .ERROR :
@@ -910,7 +944,7 @@ async def send_chan_msg(mc, nb, msg):
910944 sent ["text" ] = msg
911945 sent ["txt_type" ] = 0
912946 sent ["name" ] = mc .self_info ['name' ]
913- await log_message (mc , sent )
947+ await log_message (mc , sent )
914948 return res
915949
916950async def send_msg (mc , contact , msg ) :
@@ -1021,7 +1055,7 @@ async def get_contacts (mc, anim=False, lastomod=0, timeout=5) :
10211055 done , pending = await asyncio .wait (
10221056 futures , timeout = timeout , return_when = asyncio .FIRST_COMPLETED
10231057 )
1024-
1058+
10251059 # Check if any future completed successfully
10261060 if len (done ) == 0 :
10271061 logger .debug ("Timeout while getting contacts" )
@@ -1041,7 +1075,7 @@ async def get_contacts (mc, anim=False, lastomod=0, timeout=5) :
10411075 if anim :
10421076 if event .type == EventType .CONTACTS :
10431077 print ((len (event .payload )- contact_nb )* "." + " Done" )
1044- else :
1078+ else :
10451079 print (" Error" )
10461080 for future in pending :
10471081 future .cancel ()
@@ -1703,7 +1737,7 @@ async def next_cmd(mc, cmds, json_output=False):
17031737 res = await set_channel (mc , cmds [1 ], cmds [2 ])
17041738 elif len (cmds [3 ]) != 32 :
17051739 res = None
1706- else :
1740+ else :
17071741 res = await set_channel (mc , cmds [1 ], cmds [2 ], bytes .fromhex (cmds [3 ]))
17081742 if res is None :
17091743 print ("Error setting channel" )
@@ -1723,8 +1757,8 @@ async def next_cmd(mc, cmds, json_output=False):
17231757 case "msg" | "m" | "{" : # sends to a contact from name
17241758 argnum = 2
17251759 dest = None
1726-
1727- if len (cmds [1 ]) == 12 : # possibly an hex prefix
1760+
1761+ if len (cmds [1 ]) == 12 : # possibly an hex prefix
17281762 try :
17291763 dest = bytes .fromhex (cmds [1 ])
17301764 except ValueError :
@@ -1801,11 +1835,18 @@ async def next_cmd(mc, cmds, json_output=False):
18011835
18021836 case "trace" | "tr" :
18031837 argnum = 1
1804- res = await mc .commands .send_trace (path = cmds [1 ])
1838+ path = cmds [1 ]
1839+ plen = int (len (path )/ 2 )
1840+ if plen > 1 and path .count ("," ) == 0 :
1841+ path = cmds [1 ][0 :2 ]
1842+ for i in range (1 , plen ):
1843+ path = path + "," + cmds [1 ][2 * i :2 * i + 2 ]
1844+
1845+ res = await mc .commands .send_trace (path = path )
18051846 if res and res .type != EventType .ERROR :
18061847 tag = int .from_bytes (res .payload ['expected_ack' ], byteorder = "little" )
18071848 timeout = res .payload ["suggested_timeout" ] / 1000 * 1.2
1808- ev = await mc .wait_for_event (EventType .TRACE_DATA ,
1849+ ev = await mc .wait_for_event (EventType .TRACE_DATA ,
18091850 attribute_filters = {"tag" : tag },
18101851 timeout = timeout )
18111852 if ev is None :
@@ -1939,7 +1980,7 @@ async def next_cmd(mc, cmds, json_output=False):
19391980 print ("Timeout waiting telemetry" )
19401981 else :
19411982 print (json .dumps (res .payload , indent = 4 ))
1942-
1983+
19431984 case "disc_path" | "dp" :
19441985 argnum = 1
19451986 await mc .ensure_contacts ()
@@ -2609,7 +2650,7 @@ async def main(argv):
26092650 if not d .name is None and d .name .startswith ("MeshCore-" ):
26102651 print (f" { d .address } { d .name } " )
26112652 except BleakError :
2612- print (" No BLE HW" )
2653+ print (" No BLE HW" )
26132654 print ("\n Serial ports:" )
26142655 ports = serial .tools .list_ports .comports ()
26152656 for port , desc , hwid in sorted (ports ):
@@ -2650,7 +2691,7 @@ async def main(argv):
26502691 else :
26512692 logger .error ("Invalid choice" )
26522693 return
2653-
2694+
26542695 if (debug == True ):
26552696 logger .setLevel (logging .DEBUG )
26562697 elif (json_output ) :
0 commit comments