Merge pull request #457 from GUVWAF/reqTelemetry

Add `request-telemetry` option
This commit is contained in:
Ben Meadors
2023-08-21 14:50:27 -05:00
committed by GitHub
3 changed files with 98 additions and 2 deletions

View File

@@ -412,6 +412,13 @@ def onConnected(interface):
print(f"Sending traceroute request to {dest} (this could take a while)")
interface.sendTraceRoute(dest, hopLimit)
if args.request_telemetry:
if args.dest == BROADCAST_ADDR:
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
else:
print(f"Sending telemetry request to {args.dest} (this could take a while)")
interface.sendTelemetry(destinationId=args.dest, wantResponse=True)
if args.gpio_wrb or args.gpio_rd or args.gpio_watch:
if args.dest == BROADCAST_ADDR:
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
@@ -1188,6 +1195,14 @@ def initParser():
"Only nodes that have the encryption key can be traced.",
)
parser.add_argument(
"--request-telemetry",
help="Request telemetry from a node. "
"You need pass the destination ID as argument with '--dest'. "
"For repeaters, the nodeNum is required.",
action="store_true",
)
parser.add_argument(
"--ack",
help="Use in combination with --sendtext to wait for an acknowledgment.",

View File

@@ -18,7 +18,7 @@ from pubsub import pub
from tabulate import tabulate
import meshtastic.node
from meshtastic import mesh_pb2, portnums_pb2
from meshtastic import mesh_pb2, portnums_pb2, telemetry_pb2
from meshtastic.__init__ import (
BROADCAST_ADDR,
BROADCAST_NUM,
@@ -409,6 +409,70 @@ class MeshInterface:
self._acknowledgment.receivedTraceRoute = True
def sendTelemetry(self, destinationId=BROADCAST_ADDR, wantResponse=False):
"""Send telemetry and optionally ask for a response"""
r = telemetry_pb2.Telemetry()
node = next(n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum)
if node is not None:
metrics = node.get("deviceMetrics")
if metrics:
batteryLevel = metrics.get("batteryLevel")
if batteryLevel is not None:
r.device_metrics.battery_level = batteryLevel
voltage = metrics.get("voltage")
if voltage is not None:
r.device_metrics.voltage = voltage
channel_utilization = metrics.get("channelUtilization")
if channel_utilization is not None:
r.device_metrics.channel_utilization = channel_utilization
air_util_tx = metrics.get("airUtilTx")
if air_util_tx is not None:
r.device_metrics.air_util_tx = air_util_tx
if wantResponse:
onResponse = self.onResponseTelemetry
else:
onResponse = None
if destinationId.startswith("!"):
destinationId = int(destinationId[1:], 16)
else:
destinationId = int(destinationId)
self.sendData(
r,
destinationId=destinationId,
portNum=portnums_pb2.PortNum.TELEMETRY_APP,
wantResponse=wantResponse,
onResponse=onResponse,
)
if wantResponse:
self.waitForTelemetry()
def onResponseTelemetry(self, p):
"""on response for telemetry"""
if p["decoded"]["portnum"] == 'TELEMETRY_APP':
self._acknowledgment.receivedTelemetry = True
telemetry = telemetry_pb2.Telemetry()
telemetry.ParseFromString(p["decoded"]["payload"])
print("Telemetry received:")
if telemetry.device_metrics.battery_level is not None:
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
if telemetry.device_metrics.voltage is not None:
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
if telemetry.device_metrics.channel_utilization is not None:
print(
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
)
if telemetry.device_metrics.air_util_tx is not None:
print(f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%")
elif p["decoded"]["portnum"] == 'ROUTING_APP':
if p["decoded"]["routing"]["errorReason"] == 'NO_RESPONSE':
our_exit("No response from node. At least firmware 2.1.22 is required on the destination node.")
def _addResponseHandler(self, requestId, callback):
self.responseHandlers[requestId] = ResponseHandler(callback)
@@ -491,6 +555,12 @@ class MeshInterface:
success = self._timeout.waitForTraceRoute(waitFactor, self._acknowledgment)
if not success:
raise Exception("Timed out waiting for traceroute")
def waitForTelemetry(self):
"""Wait for telemetry"""
success = self._timeout.waitForTelemetry(self._acknowledgment)
if not success:
raise Exception("Timed out waiting for telemetry")
def getMyNodeInfo(self):
"""Get info about my node."""

View File

@@ -192,7 +192,16 @@ class Timeout:
return True
time.sleep(self.sleepInterval)
return False
def waitForTelemetry(self, acknowledgment):
"""Block until telemetry response is received. Returns True if telemetry response has been received."""
self.reset()
while time.time() < self.expireTime:
if getattr(acknowledgment, "receivedTelemetry", None):
acknowledgment.reset()
return True
time.sleep(self.sleepInterval)
return False
class Acknowledgment:
"A class that records which type of acknowledgment was just received, if any."
@@ -203,6 +212,7 @@ class Acknowledgment:
self.receivedNak = False
self.receivedImplAck = False
self.receivedTraceRoute = False
self.receivedTelemetry = False
def reset(self):
"""reset"""
@@ -210,6 +220,7 @@ class Acknowledgment:
self.receivedNak = False
self.receivedImplAck = False
self.receivedTraceRoute = False
self.receivedTelemetry = False
class DeferredExecution: