some cleanings and new prompt

This commit is contained in:
Florent 2025-11-09 19:23:01 +01:00
parent 2700b55b7f
commit efcec63711
2 changed files with 48 additions and 91 deletions

View file

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "meshcore-cli" name = "meshcore-cli"
version = "1.2.12" version = "1.2.13"
authors = [ authors = [
{ name="Florent de Lamotte", email="florent@frizoncorrea.fr" }, { name="Florent de Lamotte", email="florent@frizoncorrea.fr" },
] ]

View file

@ -4,7 +4,7 @@
""" """
import asyncio import asyncio
import os, sys, io, platform import os, sys, io
import time, datetime import time, datetime
import getopt, json, shlex, re import getopt, json, shlex, re
import logging import logging
@ -32,7 +32,7 @@ import re
from meshcore import MeshCore, EventType, logger from meshcore import MeshCore, EventType, logger
# Version # Version
VERSION = "v1.2.12" VERSION = "v1.2.13"
# default ble address is stored in a config file # default ble address is stored in a config file
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/" MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
@ -76,13 +76,11 @@ ANSI_YELLOW = "\033[0;33m"
ANSI_BYELLOW = "\033[1;33m" ANSI_BYELLOW = "\033[1;33m"
#Unicode chars #Unicode chars
# some possible symbols for prompts 🭬🬛🬗🭬🬛🬃🬗🭬🬛🬃🬗🬏🭀🭋🭨🮋 # some possible symbols for prompts 🭬🬛🬗🭬🬛🬃🬗🭬🬛🬃🬗🬏🭀🭋🭨🮋
ARROW_TAIL = "🭨" ARROW_HEAD = ""
ARROW_HEAD = "🭬" SLASH_END = ""
SLASH_START = ""
if platform.system() == 'Windows' or platform.system() == 'Darwin': INVERT_SLASH = False
ARROW_TAIL = ""
ARROW_HEAD = ""
def escape_ansi(line): def escape_ansi(line):
ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
@ -470,7 +468,7 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
"login" : contact_list, "login" : contact_list,
"cmd" : contact_list, "cmd" : contact_list,
"req_status" : contact_list, "req_status" : contact_list,
"req_bstatus" : contact_list, "req_neighbours": contact_list,
"logout" : contact_list, "logout" : contact_list,
"req_telemetry" : contact_list, "req_telemetry" : contact_list,
"req_binary" : contact_list, "req_binary" : contact_list,
@ -495,7 +493,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
"print_snr" : {"on":None, "off": None}, "print_snr" : {"on":None, "off": None},
"json_msgs" : {"on":None, "off": None}, "json_msgs" : {"on":None, "off": None},
"color" : {"on":None, "off":None}, "color" : {"on":None, "off":None},
"print_name" : {"on":None, "off":None},
"print_adverts" : {"on":None, "off":None}, "print_adverts" : {"on":None, "off":None},
"json_log_rx" : {"on":None, "off":None}, "json_log_rx" : {"on":None, "off":None},
"channel_echoes" : {"on":None, "off":None}, "channel_echoes" : {"on":None, "off":None},
@ -525,7 +522,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
"print_snr":None, "print_snr":None,
"json_msgs":None, "json_msgs":None,
"color":None, "color":None,
"print_name":None,
"print_adverts":None, "print_adverts":None,
"json_log_rx":None, "json_log_rx":None,
"channel_echoes":None, "channel_echoes":None,
@ -577,7 +573,6 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
"login" : None, "login" : None,
"logout" : None, "logout" : None,
"req_status" : None, "req_status" : None,
"req_bstatus" : None,
"req_neighbours": None, "req_neighbours": None,
"cmd" : None, "cmd" : None,
"ver" : None, "ver" : None,
@ -765,26 +760,32 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
color = process_event_message.color color = process_event_message.color
classic = interactive_loop.classic or not color classic = interactive_loop.classic or not color
print_name = interactive_loop.print_name
if classic: if classic:
prompt = "" prompt = ""
else: else:
prompt = f"{ANSI_INVERT}" prompt = f"{ANSI_INVERT}"
if print_name or contact is None : prompt = prompt + f"{ANSI_BGRAY}"
if color: prompt = prompt + f"{mc.self_info['name']}"
prompt = prompt + f"{ANSI_BGRAY}" if contact is None: # display scope
prompt = prompt + f"{mc.self_info['name']}" if not scope is None:
if contact is None: # display scope prompt = prompt + f"|{scope}"
if not scope is None:
prompt = prompt + f"|{scope}"
if classic :
prompt = prompt + "> "
else :
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}{ANSI_INVERT}"
if not contact is None : if contact is None :
if classic :
prompt = prompt + ">"
else :
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}"
else:
if classic :
prompt = prompt + "/"
else :
if INVERT_SLASH:
prompt = prompt + f"{ANSI_INVERT}"
else:
prompt = prompt + f"{ANSI_NORMAL}"
prompt = prompt + f"{SLASH_START}"
if not last_ack: if not last_ack:
prompt = prompt + f"{ANSI_BRED}" prompt = prompt + f"{ANSI_BRED}"
if classic : if classic :
@ -800,11 +801,9 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
else : else :
prompt = prompt + f"{ANSI_BBLUE}" prompt = prompt + f"{ANSI_BBLUE}"
if not classic: if not classic:
prompt = prompt + f"{SLASH_END}"
prompt = prompt + f"{ANSI_INVERT}" prompt = prompt + f"{ANSI_INVERT}"
if print_name and not classic :
prompt = prompt + f"{ANSI_NORMAL}{ARROW_TAIL}{ANSI_INVERT}"
prompt = prompt + f"{contact['adv_name']}" prompt = prompt + f"{contact['adv_name']}"
if contact["type"] == 0 or contact["out_path_len"]==-1: if contact["type"] == 0 or contact["out_path_len"]==-1:
if scope is None: if scope is None:
@ -818,14 +817,15 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
prompt = prompt + "|" + contact["out_path"] prompt = prompt + "|" + contact["out_path"]
if classic : if classic :
prompt = prompt + f"{ANSI_NORMAL}> " prompt = prompt + f"{ANSI_NORMAL}>"
else: else:
prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}" prompt = prompt + f"{ANSI_NORMAL}{ARROW_HEAD}"
prompt = prompt + f"{ANSI_END}" prompt = prompt + f"{ANSI_END}"
if not color : prompt = prompt + " "
prompt=escape_ansi(prompt) if not color :
prompt=escape_ansi(prompt)
session.app.ttimeoutlen = 0.2 session.app.ttimeoutlen = 0.2
session.app.timeoutlen = 0.2 session.app.timeoutlen = 0.2
@ -1037,7 +1037,6 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
# Handle task cancellation from KeyboardInterrupt in asyncio.run() # Handle task cancellation from KeyboardInterrupt in asyncio.run()
print("Exiting cli") print("Exiting cli")
interactive_loop.classic = False interactive_loop.classic = False
interactive_loop.print_name = True
async def process_contact_chat_line(mc, contact, line): async def process_contact_chat_line(mc, contact, line):
if contact["type"] == 0: if contact["type"] == 0:
@ -1085,7 +1084,6 @@ async def process_contact_chat_line(mc, contact, line):
line == "contact_info" or line == "ci" or\ line == "contact_info" or line == "ci" or\
line == "req_status" or line == "rs" or\ line == "req_status" or line == "rs" or\
line == "req_neighbours" or line == "rn" or\ line == "req_neighbours" or line == "rn" or\
line == "req_bstatus" or line == "rbs" or\
line == "req_telemetry" or line == "rt" or\ line == "req_telemetry" or line == "rt" or\
line == "req_acl" or\ line == "req_acl" or\
line == "path" or\ line == "path" or\
@ -1615,7 +1613,7 @@ async def print_disc_trace_to (mc, contact):
async def next_cmd(mc, cmds, json_output=False): async def next_cmd(mc, cmds, json_output=False):
""" process next command """ """ process next command """
global ARROW_TAIL, ARROW_HEAD global ARROW_HEAD, SLASH_START, SLASH_END, INVERT_SLASH
try : try :
argnum = 0 argnum = 0
@ -1741,18 +1739,18 @@ async def next_cmd(mc, cmds, json_output=False):
msg_ack.max_attempts=int(cmds[2]) msg_ack.max_attempts=int(cmds[2])
case "flood_after": case "flood_after":
msg_ack.flood_after=int(cmds[2]) msg_ack.flood_after=int(cmds[2])
case "print_name":
interactive_loop.print_name = (cmds[2] == "on")
if json_output :
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
case "classic_prompt": case "classic_prompt":
interactive_loop.classic = (cmds[2] == "on") interactive_loop.classic = (cmds[2] == "on")
if json_output : if json_output :
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]})) print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
case "arrow_tail":
ARROW_TAIL = cmds[2]
case "arrow_head": case "arrow_head":
ARROW_HEAD = cmds[2] ARROW_HEAD = cmds[2]
case "slash_start":
SLASH_START = cmds[2]
case "slash_end":
SLASH_END = cmds[2]
case "invert_slash":
INVERT_SLASH = cmds[2] == "on"
case "color" : case "color" :
process_event_message.color = (cmds[2] == "on") process_event_message.color = (cmds[2] == "on")
if json_output : if json_output :
@ -1983,11 +1981,6 @@ async def next_cmd(mc, cmds, json_output=False):
print(json.dumps({"flood_after" : msg_ack.flood_after})) print(json.dumps({"flood_after" : msg_ack.flood_after}))
else: else:
print(f"flood_after: {msg_ack.flood_after}") print(f"flood_after: {msg_ack.flood_after}")
case "print_name":
if json_output :
print(json.dumps({"print_name" : interactive_loop.print_name}))
else:
print(f"{'on' if interactive_loop.print_name else 'off'}")
case "classic_prompt": case "classic_prompt":
if json_output : if json_output :
print(json.dumps({"classic_prompt" : interactive_loop.classic})) print(json.dumps({"classic_prompt" : interactive_loop.classic}))
@ -2362,7 +2355,7 @@ async def next_cmd(mc, cmds, json_output=False):
if classic : if classic :
print("",end="") print("",end="")
else : else :
print(f"{ANSI_NORMAL}{ARROW_HEAD}",end="") print(f"{ANSI_NORMAL}{ARROW_HEAD} ",end="")
if color: if color:
print(ANSI_END, end="") print(ANSI_END, end="")
if "hash" in t: if "hash" in t:
@ -2428,46 +2421,6 @@ async def next_cmd(mc, cmds, json_output=False):
contact = mc.get_contact_by_name(cmds[1]) contact = mc.get_contact_by_name(cmds[1])
contact["timeout"] = float(cmds[2]) contact["timeout"] = float(cmds[2])
case "req_status" | "rs" :
argnum = 1
await mc.ensure_contacts()
contact = mc.get_contact_by_name(cmds[1])
res = await mc.commands.send_statusreq(contact)
logger.debug(res)
if res.type == EventType.ERROR:
print(f"Error while requesting status: {res}")
else :
timeout = res.payload["suggested_timeout"]/800 if not "timeout" in contact or contact['timeout']==0 else contact["timeout"]
res = await mc.wait_for_event(EventType.STATUS_RESPONSE, timeout=timeout)
logger.debug(res)
if res is None:
if json_output :
print(json.dumps({"error" : "Timeout waiting status"}))
else:
print("Timeout waiting status")
else :
print(json.dumps(res.payload, indent=4))
case "req_telemetry" | "rt" :
argnum = 1
await mc.ensure_contacts()
contact = mc.get_contact_by_name(cmds[1])
res = await mc.commands.send_telemetry_req(contact)
logger.debug(res)
if res.type == EventType.ERROR:
print(f"Error while requesting telemetry")
else:
timeout = res.payload["suggested_timeout"]/800 if not "timeout" in contact or contact['timeout']==0 else contact["timeout"]
res = await mc.wait_for_event(EventType.TELEMETRY_RESPONSE, timeout=timeout)
logger.debug(res)
if res is None:
if json_output :
print(json.dumps({"error" : "Timeout waiting telemetry"}))
else:
print("Timeout waiting telemetry")
else :
print(json.dumps(res.payload, indent=4))
case "disc_path" | "dp" : case "disc_path" | "dp" :
argnum = 1 argnum = 1
await mc.ensure_contacts() await mc.ensure_contacts()
@ -2553,7 +2506,7 @@ async def next_cmd(mc, cmds, json_output=False):
print(f" {name:16} {type:>4} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}") print(f" {name:16} {type:>4} SNR: {n['SNR_in']:6,.2f}->{n['SNR']:6,.2f} RSSI: ->{n['RSSI']:4}")
case "req_btelemetry"|"rbt" : case "req_telemetry"|"rt" :
argnum = 1 argnum = 1
await mc.ensure_contacts() await mc.ensure_contacts()
contact = mc.get_contact_by_name(cmds[1]) contact = mc.get_contact_by_name(cmds[1])
@ -2565,9 +2518,13 @@ async def next_cmd(mc, cmds, json_output=False):
else: else:
print("Error getting data") print("Error getting data")
else : else :
print(json.dumps(res)) print(json.dumps({
"name": contact["adv_name"],
"pubkey_pre": contact["public_key"][0:12],
"lpp": res,
}, indent = 4))
case "req_bstatus"|"rbs" : case "req_status"|"rs" :
argnum = 1 argnum = 1
await mc.ensure_contacts() await mc.ensure_contacts()
contact = mc.get_contact_by_name(cmds[1]) contact = mc.get_contact_by_name(cmds[1])