diff --git a/docs/meshtastic/index.html b/docs/meshtastic/index.html index 7f233c7..e44ae16 100644 --- a/docs/meshtastic/index.html +++ b/docs/meshtastic/index.html @@ -154,11 +154,14 @@ import time import base64 import platform import socket +import timeago from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout from .node import Node from pubsub import pub from dotmap import DotMap +from datetime import datetime +from tabulate import tabulate from typing import * from google.protobuf.json_format import MessageToJson @@ -247,12 +250,65 @@ class MeshInterface: logging.error(f'Traceback: {traceback}') self.close() - def showInfo(self): + def showInfo(self, file=sys.stdout): """Show human readable summary about this object""" - print(f"My info: {stripnl(MessageToJson(self.myInfo))}") - print("\nNodes in mesh:") + + print(f"Owner: {self.getLongName()} ({self.getShortName()})", file=file) + print(f"\nMy info: {stripnl(MessageToJson(self.myInfo))}", file=file) + print("\nNodes in mesh:", file=file) for n in self.nodes.values(): - print(" " + stripnl(n)) + print(f" {stripnl(n)}", file=file) + + def showNodes(self, includeSelf=True, file=sys.stdout): + """Show table summary of nodes in mesh""" + def formatFloat(value, precision=2, unit=''): + return f'{value:.{precision}f}{unit}' if value else None + + def getLH(ts): + return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None + + def getTimeAgo(ts): + return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None + + rows = [] + for node in self.nodes.values(): + if not includeSelf and node['num'] == self.localNode.nodeNum: + continue + + row = { "N": 0 } + + user = node.get('user') + if user: + row.update({ + "User": user['longName'], + "AKA": user['shortName'], + "ID": user['id'], + }) + + pos = node.get('position') + if pos: + row.update({ + "Latitude": formatFloat(pos.get("latitude"), 4, "°"), + "Longitude": formatFloat(pos.get("longitude"), 4, "°"), + "Altitude": formatFloat(pos.get("altitude"), 0, " m"), + "Battery": formatFloat(pos.get("batteryLevel"), 2, "%"), + }) + + row.update({ + "SNR": formatFloat(node.get("snr"), 2, " dB"), + "LastHeard": getLH( node.get("lastHeard")), + "Since": getTimeAgo( node.get("lastHeard")), + }) + + rows.append(row) + + # Why doesn't this way work? + #rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True) + rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True) + for i, row in enumerate(rows): + row['N'] = i+1 + + print(tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid'), file=file) def getNode(self, nodeId): """Return a node object which contains device settings and channel info""" @@ -771,7 +827,7 @@ class StreamInterface(MeshInterface): self._wantExit = False # FIXME, figure out why daemon=True causes reader thread to exit too early - self._rxThread = threading.Thread(target=self.__reader, args=()) + self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True) MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto) @@ -1190,6 +1246,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
  • sendPosition
  • sendText
  • showInfo
  • +
  • showNodes
  • waitForConfig
  • @@ -1295,12 +1352,65 @@ noProto – If True, don't try to run our protocol on the link - just be a d logging.error(f'Traceback: {traceback}') self.close() - def showInfo(self): + def showInfo(self, file=sys.stdout): """Show human readable summary about this object""" - print(f"My info: {stripnl(MessageToJson(self.myInfo))}") - print("\nNodes in mesh:") + + print(f"Owner: {self.getLongName()} ({self.getShortName()})", file=file) + print(f"\nMy info: {stripnl(MessageToJson(self.myInfo))}", file=file) + print("\nNodes in mesh:", file=file) for n in self.nodes.values(): - print(" " + stripnl(n)) + print(f" {stripnl(n)}", file=file) + + def showNodes(self, includeSelf=True, file=sys.stdout): + """Show table summary of nodes in mesh""" + def formatFloat(value, precision=2, unit=''): + return f'{value:.{precision}f}{unit}' if value else None + + def getLH(ts): + return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None + + def getTimeAgo(ts): + return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None + + rows = [] + for node in self.nodes.values(): + if not includeSelf and node['num'] == self.localNode.nodeNum: + continue + + row = { "N": 0 } + + user = node.get('user') + if user: + row.update({ + "User": user['longName'], + "AKA": user['shortName'], + "ID": user['id'], + }) + + pos = node.get('position') + if pos: + row.update({ + "Latitude": formatFloat(pos.get("latitude"), 4, "°"), + "Longitude": formatFloat(pos.get("longitude"), 4, "°"), + "Altitude": formatFloat(pos.get("altitude"), 0, " m"), + "Battery": formatFloat(pos.get("batteryLevel"), 2, "%"), + }) + + row.update({ + "SNR": formatFloat(node.get("snr"), 2, " dB"), + "LastHeard": getLH( node.get("lastHeard")), + "Since": getTimeAgo( node.get("lastHeard")), + }) + + rows.append(row) + + # Why doesn't this way work? + #rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True) + rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True) + for i, row in enumerate(rows): + row['N'] = i+1 + + print(tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid'), file=file) def getNode(self, nodeId): """Return a node object which contains device settings and channel info""" @@ -2003,7 +2113,7 @@ wantResponse – True if you want the service on the other side to send an a
    -def showInfo(self) +def showInfo(self, file=sys.stdout)

    Show human readable summary about this object

    @@ -2011,12 +2121,75 @@ wantResponse – True if you want the service on the other side to send an a Expand source code -
    def showInfo(self):
    +
    def showInfo(self, file=sys.stdout):
         """Show human readable summary about this object"""
    -    print(f"My info: {stripnl(MessageToJson(self.myInfo))}")
    -    print("\nNodes in mesh:")
    +
    +    print(f"Owner: {self.getLongName()} ({self.getShortName()})", file=file)
    +    print(f"\nMy info: {stripnl(MessageToJson(self.myInfo))}", file=file)
    +    print("\nNodes in mesh:", file=file)
         for n in self.nodes.values():
    -        print("  " + stripnl(n))
    + print(f" {stripnl(n)}", file=file)
    + +
    +
    +def showNodes(self, includeSelf=True, file=sys.stdout) +
    +
    +

    Show table summary of nodes in mesh

    +
    + +Expand source code + +
    def showNodes(self, includeSelf=True, file=sys.stdout):
    +    """Show table summary of nodes in mesh"""
    +    def formatFloat(value, precision=2, unit=''):
    +        return f'{value:.{precision}f}{unit}' if value else None
    +
    +    def getLH(ts):
    +        return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
    +
    +    def getTimeAgo(ts):
    +        return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
    +
    +    rows = []
    +    for node in self.nodes.values():
    +        if not includeSelf and node['num'] == self.localNode.nodeNum:
    +            continue
    +
    +        row = { "N": 0 }
    +
    +        user = node.get('user')
    +        if user:
    +            row.update({
    +                "User": user['longName'],
    +                "AKA":  user['shortName'],
    +                "ID":   user['id'],
    +            })
    +
    +        pos = node.get('position')
    +        if pos:
    +            row.update({
    +                "Latitude":  formatFloat(pos.get("latitude"),     4, "°"),
    +                "Longitude": formatFloat(pos.get("longitude"),    4, "°"),
    +                "Altitude":  formatFloat(pos.get("altitude"),     0, " m"),
    +                "Battery":   formatFloat(pos.get("batteryLevel"), 2, "%"),
    +            })
    +
    +        row.update({
    +            "SNR":       formatFloat(node.get("snr"), 2, " dB"),
    +            "LastHeard": getLH(      node.get("lastHeard")),
    +            "Since":     getTimeAgo( node.get("lastHeard")),
    +        })
    +
    +        rows.append(row)
    +
    +    # Why doesn't this way work?
    +    #rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
    +    rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
    +    for i, row in enumerate(rows):
    +        row['N'] = i+1
    +
    +    print(tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid'), file=file)
    @@ -2138,6 +2311,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from
  • sendPosition
  • sendText
  • showInfo
  • +
  • showNodes
  • waitForConfig
  • @@ -2186,7 +2360,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from self._wantExit = False # FIXME, figure out why daemon=True causes reader thread to exit too early - self._rxThread = threading.Thread(target=self.__reader, args=()) + self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True) MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto) @@ -2378,6 +2552,7 @@ start the reading thread later.

  • sendPosition
  • sendText
  • showInfo
  • +
  • showNodes
  • waitForConfig
  • @@ -2457,6 +2632,7 @@ hostname {string} – Hostname/IP address of the device to connect to

    sendPosition
  • sendText
  • showInfo
  • +
  • showNodes
  • waitForConfig
  • @@ -2528,6 +2704,7 @@ hostname {string} – Hostname/IP address of the device to connect to

    sendPosition
  • sendText
  • showInfo
  • +
  • showNodes
  • waitForConfig
  • diff --git a/docs/meshtastic/mesh_pb2.html b/docs/meshtastic/mesh_pb2.html index 3070ba9..a3f8e4a 100644 --- a/docs/meshtastic/mesh_pb2.html +++ b/docs/meshtastic/mesh_pb2.html @@ -50,7 +50,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=b'\n\023com.geeksville.meshB\nMeshProtosH\003', create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\nmesh.proto\x1a\x0eportnums.proto\"v\n\x08Position\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x15\n\rbattery_level\x18\x04 \x01(\x05\x12\x0c\n\x04time\x18\t \x01(\x07J\x04\x08\x07\x10\x08J\x04\x08\x08\x10\t\"\x81\x01\n\x04User\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x0f\n\x07macaddr\x18\x04 \x01(\x0c\x12 \n\x08hw_model\x18\x06 \x01(\x0e\x32\x0e.HardwareModel\x12\x13\n\x0bis_licensed\x18\x07 \x01(\x08\"\x1f\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x02 \x03(\x07\"\xc5\x02\n\x07Routing\x12(\n\rroute_request\x18\x01 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0broute_reply\x18\x02 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0c\x65rror_reason\x18\x03 \x01(\x0e\x32\x0e.Routing.ErrorH\x00\"\xb4\x01\n\x05\x45rror\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08NO_ROUTE\x10\x01\x12\x0b\n\x07GOT_NAK\x10\x02\x12\x0b\n\x07TIMEOUT\x10\x03\x12\x10\n\x0cNO_INTERFACE\x10\x04\x12\x12\n\x0eMAX_RETRANSMIT\x10\x05\x12\x0e\n\nNO_CHANNEL\x10\x06\x12\r\n\tTOO_LARGE\x10\x07\x12\x0f\n\x0bNO_RESPONSE\x10\x08\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10 \x12\x12\n\x0eNOT_AUTHORIZED\x10!B\t\n\x07variant\"{\n\x04\x44\x61ta\x12\x19\n\x07portnum\x18\x01 \x01(\x0e\x32\x08.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x15\n\rwant_response\x18\x03 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x04 \x01(\x07\x12\x0e\n\x06source\x18\x05 \x01(\x07\x12\x12\n\nrequest_id\x18\x06 \x01(\x07\"\xe0\x02\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x07\x12\n\n\x02to\x18\x02 \x01(\x07\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12\x18\n\x07\x64\x65\x63oded\x18\x04 \x01(\x0b\x32\x05.DataH\x00\x12\x13\n\tencrypted\x18\x05 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\x07\x12\x0f\n\x07rx_time\x18\x07 \x01(\x07\x12\x0e\n\x06rx_snr\x18\x08 \x01(\x02\x12\x11\n\thop_limit\x18\n \x01(\r\x12\x10\n\x08want_ack\x18\x0b \x01(\x08\x12&\n\x08priority\x18\x0c \x01(\x0e\x32\x14.MeshPacket.Priority\x12\x0f\n\x07rx_rssi\x18\r \x01(\x05\"[\n\x08Priority\x12\t\n\x05UNSET\x10\x00\x12\x07\n\x03MIN\x10\x01\x12\x0e\n\nBACKGROUND\x10\n\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10@\x12\x0c\n\x08RELIABLE\x10\x46\x12\x07\n\x03\x41\x43K\x10x\x12\x07\n\x03MAX\x10\x7f\x42\x10\n\x0epayloadVariant\"j\n\x08NodeInfo\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x13\n\x04user\x18\x02 \x01(\x0b\x32\x05.User\x12\x1b\n\x08position\x18\x03 \x01(\x0b\x32\t.Position\x12\x0b\n\x03snr\x18\x07 \x01(\x02\x12\x12\n\nlast_heard\x18\x04 \x01(\x07\"\xcb\x02\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x0f\n\x07has_gps\x18\x02 \x01(\x08\x12\x11\n\tnum_bands\x18\x03 \x01(\r\x12\x14\n\x0cmax_channels\x18\x0f \x01(\r\x12\x12\n\x06region\x18\x04 \x01(\tB\x02\x18\x01\x12\x1f\n\x13hw_model_deprecated\x18\x05 \x01(\tB\x02\x18\x01\x12\x18\n\x10\x66irmware_version\x18\x06 \x01(\t\x12&\n\nerror_code\x18\x07 \x01(\x0e\x32\x12.CriticalErrorCode\x12\x15\n\rerror_address\x18\x08 \x01(\r\x12\x13\n\x0b\x65rror_count\x18\t \x01(\r\x12\x14\n\x0creboot_count\x18\n \x01(\r\x12\x1c\n\x14message_timeout_msec\x18\r \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0e \x01(\r\"\xb5\x01\n\tLogRecord\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x0e\n\x06source\x18\x03 \x01(\t\x12\x1f\n\x05level\x18\x04 \x01(\x0e\x32\x10.LogRecord.Level\"X\n\x05Level\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x43RITICAL\x10\x32\x12\t\n\x05\x45RROR\x10(\x12\x0b\n\x07WARNING\x10\x1e\x12\x08\n\x04INFO\x10\x14\x12\t\n\x05\x44\x45\x42UG\x10\n\x12\t\n\x05TRACE\x10\x05\"\xe9\x01\n\tFromRadio\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x1d\n\x06packet\x18\x0b \x01(\x0b\x32\x0b.MeshPacketH\x00\x12\x1e\n\x07my_info\x18\x03 \x01(\x0b\x32\x0b.MyNodeInfoH\x00\x12\x1e\n\tnode_info\x18\x04 \x01(\x0b\x32\t.NodeInfoH\x00\x12 \n\nlog_record\x18\x07 \x01(\x0b\x32\n.LogRecordH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x08 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\t \x01(\x08H\x00\x42\x10\n\x0epayloadVariantJ\x04\x08\x02\x10\x03J\x04\x08\x06\x10\x07\"\x82\x01\n\x07ToRadio\x12\x1d\n\x06packet\x18\x02 \x01(\x0b\x32\x0b.MeshPacketH\x00\x12\x18\n\x0ewant_config_id\x18\x64 \x01(\rH\x00\x12\x14\n\ndisconnect\x18h \x01(\x08H\x00\x42\x10\n\x0epayloadVariantJ\x04\x08\x01\x10\x02J\x04\x08\x65\x10\x66J\x04\x08\x66\x10gJ\x04\x08g\x10h*\xfd\x01\n\rHardwareModel\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08TLORA_V2\x10\x01\x12\x0c\n\x08TLORA_V1\x10\x02\x12\x12\n\x0eTLORA_V2_1_1p6\x10\x03\x12\t\n\x05TBEAM\x10\x04\x12\n\n\x06HELTEC\x10\x05\x12\x0c\n\x08TBEAM0p7\x10\x06\x12\n\n\x06T_ECHO\x10\x07\x12\x10\n\x0cTLORA_V1_1p3\x10\x08\x12\x11\n\rLORA_RELAY_V1\x10 \x12\x0e\n\nNRF52840DK\x10!\x12\x07\n\x03PPR\x10\"\x12\x0f\n\x0bGENIEBLOCKS\x10#\x12\x11\n\rNRF52_UNKNOWN\x10$\x12\r\n\tPORTDUINO\x10%\x12\x0f\n\x0b\x41NDROID_SIM\x10&*.\n\tConstants\x12\n\n\x06Unused\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xf0\x01*\xbd\x01\n\x11\x43riticalErrorCode\x12\x08\n\x04None\x10\x00\x12\x0e\n\nTxWatchdog\x10\x01\x12\x12\n\x0eSleepEnterWait\x10\x02\x12\x0b\n\x07NoRadio\x10\x03\x12\x0f\n\x0bUnspecified\x10\x04\x12\x13\n\x0fUBloxInitFailed\x10\x05\x12\x0c\n\x08NoAXP192\x10\x06\x12\x17\n\x13InvalidRadioSetting\x10\x07\x12\x12\n\x0eTransmitFailed\x10\x08\x12\x0c\n\x08\x42rownout\x10\tB#\n\x13\x63om.geeksville.meshB\nMeshProtosH\x03\x62\x06proto3' + serialized_pb=b'\n\nmesh.proto\x1a\x0eportnums.proto\"v\n\x08Position\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x15\n\rbattery_level\x18\x04 \x01(\x05\x12\x0c\n\x04time\x18\t \x01(\x07J\x04\x08\x07\x10\x08J\x04\x08\x08\x10\t\"\x81\x01\n\x04User\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x0f\n\x07macaddr\x18\x04 \x01(\x0c\x12 \n\x08hw_model\x18\x06 \x01(\x0e\x32\x0e.HardwareModel\x12\x13\n\x0bis_licensed\x18\x07 \x01(\x08\"\x1f\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x02 \x03(\x07\"\xc5\x02\n\x07Routing\x12(\n\rroute_request\x18\x01 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0broute_reply\x18\x02 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0c\x65rror_reason\x18\x03 \x01(\x0e\x32\x0e.Routing.ErrorH\x00\"\xb4\x01\n\x05\x45rror\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08NO_ROUTE\x10\x01\x12\x0b\n\x07GOT_NAK\x10\x02\x12\x0b\n\x07TIMEOUT\x10\x03\x12\x10\n\x0cNO_INTERFACE\x10\x04\x12\x12\n\x0eMAX_RETRANSMIT\x10\x05\x12\x0e\n\nNO_CHANNEL\x10\x06\x12\r\n\tTOO_LARGE\x10\x07\x12\x0f\n\x0bNO_RESPONSE\x10\x08\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10 \x12\x12\n\x0eNOT_AUTHORIZED\x10!B\t\n\x07variant\"{\n\x04\x44\x61ta\x12\x19\n\x07portnum\x18\x01 \x01(\x0e\x32\x08.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x15\n\rwant_response\x18\x03 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x04 \x01(\x07\x12\x0e\n\x06source\x18\x05 \x01(\x07\x12\x12\n\nrequest_id\x18\x06 \x01(\x07\"\xe0\x02\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\x07\x12\n\n\x02to\x18\x02 \x01(\x07\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\r\x12\x18\n\x07\x64\x65\x63oded\x18\x04 \x01(\x0b\x32\x05.DataH\x00\x12\x13\n\tencrypted\x18\x05 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\x07\x12\x0f\n\x07rx_time\x18\x07 \x01(\x07\x12\x0e\n\x06rx_snr\x18\x08 \x01(\x02\x12\x11\n\thop_limit\x18\n \x01(\r\x12\x10\n\x08want_ack\x18\x0b \x01(\x08\x12&\n\x08priority\x18\x0c \x01(\x0e\x32\x14.MeshPacket.Priority\x12\x0f\n\x07rx_rssi\x18\r \x01(\x05\"[\n\x08Priority\x12\t\n\x05UNSET\x10\x00\x12\x07\n\x03MIN\x10\x01\x12\x0e\n\nBACKGROUND\x10\n\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10@\x12\x0c\n\x08RELIABLE\x10\x46\x12\x07\n\x03\x41\x43K\x10x\x12\x07\n\x03MAX\x10\x7f\x42\x10\n\x0epayloadVariant\"j\n\x08NodeInfo\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x13\n\x04user\x18\x02 \x01(\x0b\x32\x05.User\x12\x1b\n\x08position\x18\x03 \x01(\x0b\x32\t.Position\x12\x0b\n\x03snr\x18\x07 \x01(\x02\x12\x12\n\nlast_heard\x18\x04 \x01(\x07\"\xcb\x02\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x0f\n\x07has_gps\x18\x02 \x01(\x08\x12\x11\n\tnum_bands\x18\x03 \x01(\r\x12\x14\n\x0cmax_channels\x18\x0f \x01(\r\x12\x12\n\x06region\x18\x04 \x01(\tB\x02\x18\x01\x12\x1f\n\x13hw_model_deprecated\x18\x05 \x01(\tB\x02\x18\x01\x12\x18\n\x10\x66irmware_version\x18\x06 \x01(\t\x12&\n\nerror_code\x18\x07 \x01(\x0e\x32\x12.CriticalErrorCode\x12\x15\n\rerror_address\x18\x08 \x01(\r\x12\x13\n\x0b\x65rror_count\x18\t \x01(\r\x12\x14\n\x0creboot_count\x18\n \x01(\r\x12\x1c\n\x14message_timeout_msec\x18\r \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0e \x01(\r\"\xb5\x01\n\tLogRecord\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04time\x18\x02 \x01(\x07\x12\x0e\n\x06source\x18\x03 \x01(\t\x12\x1f\n\x05level\x18\x04 \x01(\x0e\x32\x10.LogRecord.Level\"X\n\x05Level\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x43RITICAL\x10\x32\x12\t\n\x05\x45RROR\x10(\x12\x0b\n\x07WARNING\x10\x1e\x12\x08\n\x04INFO\x10\x14\x12\t\n\x05\x44\x45\x42UG\x10\n\x12\t\n\x05TRACE\x10\x05\"\xe9\x01\n\tFromRadio\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x1d\n\x06packet\x18\x0b \x01(\x0b\x32\x0b.MeshPacketH\x00\x12\x1e\n\x07my_info\x18\x03 \x01(\x0b\x32\x0b.MyNodeInfoH\x00\x12\x1e\n\tnode_info\x18\x04 \x01(\x0b\x32\t.NodeInfoH\x00\x12 \n\nlog_record\x18\x07 \x01(\x0b\x32\n.LogRecordH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x08 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\t \x01(\x08H\x00\x42\x10\n\x0epayloadVariantJ\x04\x08\x02\x10\x03J\x04\x08\x06\x10\x07\"\xe1\x01\n\x07ToRadio\x12\x1d\n\x06packet\x18\x02 \x01(\x0b\x32\x0b.MeshPacketH\x00\x12&\n\tpeer_info\x18\x03 \x01(\x0b\x32\x11.ToRadio.PeerInfoH\x00\x12\x18\n\x0ewant_config_id\x18\x64 \x01(\rH\x00\x12\x14\n\ndisconnect\x18h \x01(\x08H\x00\x1a\x35\n\x08PeerInfo\x12\x13\n\x0b\x61pp_version\x18\x01 \x01(\r\x12\x14\n\x0cmqtt_gateway\x18\x02 \x01(\x08\x42\x10\n\x0epayloadVariantJ\x04\x08\x01\x10\x02J\x04\x08\x65\x10\x66J\x04\x08\x66\x10gJ\x04\x08g\x10h*\x8a\x02\n\rHardwareModel\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08TLORA_V2\x10\x01\x12\x0c\n\x08TLORA_V1\x10\x02\x12\x12\n\x0eTLORA_V2_1_1p6\x10\x03\x12\t\n\x05TBEAM\x10\x04\x12\n\n\x06HELTEC\x10\x05\x12\x0c\n\x08TBEAM0p7\x10\x06\x12\n\n\x06T_ECHO\x10\x07\x12\x10\n\x0cTLORA_V1_1p3\x10\x08\x12\x0b\n\x07RAK4631\x10\t\x12\x11\n\rLORA_RELAY_V1\x10 \x12\x0e\n\nNRF52840DK\x10!\x12\x07\n\x03PPR\x10\"\x12\x0f\n\x0bGENIEBLOCKS\x10#\x12\x11\n\rNRF52_UNKNOWN\x10$\x12\r\n\tPORTDUINO\x10%\x12\x0f\n\x0b\x41NDROID_SIM\x10&*.\n\tConstants\x12\n\n\x06Unused\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xf0\x01*\xbd\x01\n\x11\x43riticalErrorCode\x12\x08\n\x04None\x10\x00\x12\x0e\n\nTxWatchdog\x10\x01\x12\x12\n\x0eSleepEnterWait\x10\x02\x12\x0b\n\x07NoRadio\x10\x03\x12\x0f\n\x0bUnspecified\x10\x04\x12\x13\n\x0fUBloxInitFailed\x10\x05\x12\x0c\n\x08NoAXP192\x10\x06\x12\x17\n\x13InvalidRadioSetting\x10\x07\x12\x12\n\x0eTransmitFailed\x10\x08\x12\x0c\n\x08\x42rownout\x10\tB#\n\x13\x63om.geeksville.meshB\nMeshProtosH\x03\x62\x06proto3' , dependencies=[portnums__pb2.DESCRIPTOR,]) @@ -107,45 +107,50 @@ _HARDWAREMODEL = _descriptor.EnumDescriptor( type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='LORA_RELAY_V1', index=9, number=32, + name='RAK4631', index=9, number=9, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='NRF52840DK', index=10, number=33, + name='LORA_RELAY_V1', index=10, number=32, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='PPR', index=11, number=34, + name='NRF52840DK', index=11, number=33, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='GENIEBLOCKS', index=12, number=35, + name='PPR', index=12, number=34, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='NRF52_UNKNOWN', index=13, number=36, + name='GENIEBLOCKS', index=13, number=35, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='PORTDUINO', index=14, number=37, + name='NRF52_UNKNOWN', index=14, number=36, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='ANDROID_SIM', index=15, number=38, + name='PORTDUINO', index=15, number=37, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ANDROID_SIM', index=16, number=38, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, - serialized_start=2119, - serialized_end=2372, + serialized_start=2214, + serialized_end=2480, ) _sym_db.RegisterEnumDescriptor(_HARDWAREMODEL) @@ -170,8 +175,8 @@ _CONSTANTS = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=2374, - serialized_end=2420, + serialized_start=2482, + serialized_end=2528, ) _sym_db.RegisterEnumDescriptor(_CONSTANTS) @@ -236,8 +241,8 @@ _CRITICALERRORCODE = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=2423, - serialized_end=2612, + serialized_start=2531, + serialized_end=2720, ) _sym_db.RegisterEnumDescriptor(_CRITICALERRORCODE) @@ -251,6 +256,7 @@ HELTEC = 5 TBEAM0p7 = 6 T_ECHO = 7 TLORA_V1_1p3 = 8 +RAK4631 = 9 LORA_RELAY_V1 = 32 NRF52840DK = 33 PPR = 34 @@ -1145,6 +1151,44 @@ _FROMRADIO = _descriptor.Descriptor( ) +_TORADIO_PEERINFO = _descriptor.Descriptor( + name='PeerInfo', + full_name='ToRadio.PeerInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='app_version', full_name='ToRadio.PeerInfo.app_version', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='mqtt_gateway', full_name='ToRadio.PeerInfo.mqtt_gateway', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2116, + serialized_end=2169, +) + _TORADIO = _descriptor.Descriptor( name='ToRadio', full_name='ToRadio', @@ -1161,14 +1205,21 @@ _TORADIO = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='want_config_id', full_name='ToRadio.want_config_id', index=1, + name='peer_info', full_name='ToRadio.peer_info', index=1, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='want_config_id', full_name='ToRadio.want_config_id', index=2, number=100, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='disconnect', full_name='ToRadio.disconnect', index=2, + name='disconnect', full_name='ToRadio.disconnect', index=3, number=104, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, @@ -1177,7 +1228,7 @@ _TORADIO = _descriptor.Descriptor( ], extensions=[ ], - nested_types=[], + nested_types=[_TORADIO_PEERINFO, ], enum_types=[ ], serialized_options=None, @@ -1192,7 +1243,7 @@ _TORADIO = _descriptor.Descriptor( fields=[]), ], serialized_start=1986, - serialized_end=2116, + serialized_end=2211, ) _USER.fields_by_name['hw_model'].enum_type = _HARDWAREMODEL @@ -1246,10 +1297,15 @@ _FROMRADIO.fields_by_name['config_complete_id'].containing_oneof = _FROM _FROMRADIO.oneofs_by_name['payloadVariant'].fields.append( _FROMRADIO.fields_by_name['rebooted']) _FROMRADIO.fields_by_name['rebooted'].containing_oneof = _FROMRADIO.oneofs_by_name['payloadVariant'] +_TORADIO_PEERINFO.containing_type = _TORADIO _TORADIO.fields_by_name['packet'].message_type = _MESHPACKET +_TORADIO.fields_by_name['peer_info'].message_type = _TORADIO_PEERINFO _TORADIO.oneofs_by_name['payloadVariant'].fields.append( _TORADIO.fields_by_name['packet']) _TORADIO.fields_by_name['packet'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant'] +_TORADIO.oneofs_by_name['payloadVariant'].fields.append( + _TORADIO.fields_by_name['peer_info']) +_TORADIO.fields_by_name['peer_info'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant'] _TORADIO.oneofs_by_name['payloadVariant'].fields.append( _TORADIO.fields_by_name['want_config_id']) _TORADIO.fields_by_name['want_config_id'].containing_oneof = _TORADIO.oneofs_by_name['payloadVariant'] @@ -1343,11 +1399,19 @@ FromRadio = _reflection.GeneratedProtocolMessageType('FromRadio', (_mess _sym_db.RegisterMessage(FromRadio) ToRadio = _reflection.GeneratedProtocolMessageType('ToRadio', (_message.Message,), { + + 'PeerInfo' : _reflection.GeneratedProtocolMessageType('PeerInfo', (_message.Message,), { + 'DESCRIPTOR' : _TORADIO_PEERINFO, + '__module__' : 'mesh_pb2' + # @@protoc_insertion_point(class_scope:ToRadio.PeerInfo) + }) + , 'DESCRIPTOR' : _TORADIO, '__module__' : 'mesh_pb2' # @@protoc_insertion_point(class_scope:ToRadio) }) _sym_db.RegisterMessage(ToRadio) +_sym_db.RegisterMessage(ToRadio.PeerInfo) DESCRIPTOR._options = None @@ -1916,6 +1980,10 @@ _MYNODEINFO.fields_by_name['hw_model_deprecated']._options = None
    +
    var PeerInfo
    +
    +

    A ProtocolMessage

    +

    Instance variables

    @@ -1927,6 +1995,10 @@ _MYNODEINFO.fields_by_name['hw_model_deprecated']._options = None

    Field ToRadio.packet

    +
    var peer_info
    +
    +

    Field ToRadio.peer_info

    +
    var want_config_id

    Field ToRadio.want_config_id

    @@ -2135,10 +2207,12 @@ _MYNODEINFO.fields_by_name['hw_model_deprecated']._options = None
  • ToRadio

    -
      + diff --git a/docs/meshtastic/test.html b/docs/meshtastic/test.html index 653ca1b..d6ee1f1 100644 --- a/docs/meshtastic/test.html +++ b/docs/meshtastic/test.html @@ -32,7 +32,7 @@ from . import SerialInterface, TCPInterface, BROADCAST_NUM from pubsub import pub import time import sys -import threading +import threading, traceback from dotmap import DotMap """The interfaces we are using for our tests""" @@ -195,13 +195,18 @@ def testSimulator(): """ logging.basicConfig(level=logging.DEBUG if False else logging.INFO) logging.info("Connecting to simulator on localhost!") - iface = TCPInterface("localhost") - iface.showInfo() - iface.localNode.showInfo() - iface.localNode.exitSimulator() - iface.close() - logging.info("Integration test successful!") - sys.exit(0) + try: + iface = TCPInterface("localhost") + iface.showInfo() + iface.localNode.showInfo() + iface.localNode.exitSimulator() + iface.close() + logging.info("Integration test successful!") + sys.exit(0) + except: + print("Error while testing simulator:", sys.exc_info()[0]) + traceback.print_exc() + sys.exit(1)
      @@ -453,13 +458,18 @@ python3 -c 'from meshtastic.test import testSimulator; testSimulator()'

      + try: + iface = TCPInterface("localhost") + iface.showInfo() + iface.localNode.showInfo() + iface.localNode.exitSimulator() + iface.close() + logging.info("Integration test successful!") + sys.exit(0) + except: + print("Error while testing simulator:", sys.exc_info()[0]) + traceback.print_exc() + sys.exit(1)
  • diff --git a/setup.py b/setup.py index bf99f45..23e7043 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ with open("README.md", "r") as fh: # This call to setup() does all the work setup( name="meshtastic", - version="1.2.25", + version="1.2.29", description="Python API & client shell for talking to Meshtastic devices", long_description=long_description, long_description_content_type="text/markdown",