Add optional parameter to specify what fields to show with --nodes

This commit is contained in:
David Andrzejewski
2025-02-15 02:23:13 -05:00
parent 6ec506fe3b
commit 82554a1f18
3 changed files with 64 additions and 4 deletions

17
.vscode/launch.json vendored
View File

@@ -245,6 +245,23 @@
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--nodes"]
},
{
"name": "meshtastic nodes table",
"type": "debugpy",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--nodes"]
},
{
"name": "meshtastic nodes table with show-fields",
"type": "debugpy",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--nodes", "--show-fields", "AKA,Pubkey,Role,Role,Role,Latitude,Latitude,deviceMetrics.voltage"]
}
]
}

View File

@@ -921,7 +921,11 @@ def onConnected(interface):
if args.dest != BROADCAST_ADDR:
print("Showing node list of a remote node is not supported.")
return
interface.showNodes()
interface.showNodes(True, args.show_fields)
if args.show_fields and not args.nodes:
print("--show-fields can only be used with --nodes")
return
if args.qr or args.qr_all:
closeNow = True
@@ -1626,6 +1630,13 @@ def addLocalActionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPars
help="Print Node List in a pretty formatted table",
action="store_true",
)
group.add_argument(
"--show-fields",
help="Specify fields to show (comma-separated) when using --nodes",
type=lambda s: s.split(','),
default=None
)
return parser

View File

@@ -222,7 +222,7 @@ class MeshInterface: # pylint: disable=R0902
return infos
def showNodes(
self, includeSelf: bool = True
self, includeSelf: bool = True, showFields: Optional[List[str]] = None
) -> str: # pylint: disable=W0613
"""Show table summary of nodes in mesh"""
@@ -246,6 +246,23 @@ class MeshInterface: # pylint: disable=R0902
return None # not handling a timestamp from the future
return _timeago(delta_secs)
def getNestedValue(node_dict: Dict[str, Any], key_path: str) -> Any:
keys = key_path.split(".")
value = node_dict
for key in keys:
if isinstance(value, dict):
value = value.get(key)
else:
return None
return value
if showFields is None or showFields.count == 0:
# The default set of fields to show (e.g., the status quo)
showFields = ["N", "User", "ID", "AKA", "Hardware", "Pubkey", "Role", "Latitude", "Longitude", "Altitude", "Battery", "Channel util.", "Tx air util.", "SNR", "Hops", "Channel", "LastHeard", "Since"]
else:
# Always at least include the row number.
showFields.insert(0, "N")
rows: List[Dict[str, Any]] = []
if self.nodesByNum:
logging.debug(f"self.nodes:{self.nodes}")
@@ -287,11 +304,12 @@ class MeshInterface: # pylint: disable=R0902
if metrics:
batteryLevel = metrics.get("batteryLevel")
if batteryLevel is not None:
if batteryLevel == 0:
if batteryLevel in (0, 101): # Note: for boards without battery pin or PMU, 101% battery means 'the board is using external power'
batteryString = "Powered"
else:
batteryString = str(batteryLevel) + "%"
row.update({"Battery": batteryString})
row.update(
{
"Channel util.": formatFloat(
@@ -313,7 +331,21 @@ class MeshInterface: # pylint: disable=R0902
}
)
rows.append(row)
# This allows the user to specify fields that wouldn't otherwise be included.
extraFields = {}
for field in showFields:
if field in row:
# We already have it, move along.
continue
elif "." in field:
extraFields[field] = getNestedValue(node, field)
else:
extraFields[field] = node.get(field)
# Filter out any field in the data set that was not specified.
filteredData = {key: value for key, value in row.items() if key in showFields}
filteredData.update(extraFields)
rows.append(filteredData)
rows.sort(key=lambda r: r.get("LastHeard") or "0000", reverse=True)
for i, row in enumerate(rows):