From 449c0a2dac3581ca384bd468b89c45ed74812efa Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 9 Dec 2020 13:44:31 +0800 Subject: [PATCH] 1.1.20 --- docs/meshtastic/index.html | 325 ++++++--- docs/meshtastic/mesh_pb2.html | 882 +++-------------------- docs/meshtastic/portnums_pb2.html | 150 ++++ docs/meshtastic/remote_hardware.html | 238 ++++++ docs/meshtastic/remote_hardware_pb2.html | 837 +++++++++++++++++++++ setup.py | 2 +- 6 files changed, 1543 insertions(+), 891 deletions(-) create mode 100644 docs/meshtastic/portnums_pb2.html create mode 100644 docs/meshtastic/remote_hardware.html create mode 100644 docs/meshtastic/remote_hardware_pb2.html diff --git a/docs/meshtastic/index.html b/docs/meshtastic/index.html index e74d5f7..a62ad46 100644 --- a/docs/meshtastic/index.html +++ b/docs/meshtastic/index.html @@ -48,7 +48,7 @@ type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive".
  • meshtastic.receive.position(packet)
  • meshtastic.receive.user(packet)
  • -
  • meshtastic.receive.data(packet)
  • +
  • meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
  • meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc…)
  • We receive position, user, or data packets from the mesh. @@ -110,7 +110,7 @@ topics: type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive". - meshtastic.receive.position(packet) - meshtastic.receive.user(packet) -- meshtastic.receive.data(packet) +- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum) - meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc...) We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for @@ -152,8 +152,7 @@ import traceback import time import base64 import platform -from . import mesh_pb2 -from . import util +from . import mesh_pb2, portnums_pb2, util from pubsub import pub from dotmap import DotMap @@ -169,9 +168,11 @@ BROADCAST_NUM = 0xffffffff MY_CONFIG_ID = 42 -"""The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand""" -OUR_APP_VERSION = 172 +"""The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand +format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20 +""" +OUR_APP_VERSION = 20120 class MeshInterface: """Interface class for meshtastic devices @@ -184,10 +185,15 @@ class MeshInterface: """ def __init__(self, debugOut=None, noProto=False): - """Constructor""" + """Constructor + + Keyword Arguments: + noProto -- If True, don't try to run our protocol on the link - just be a dumb serial client. + """ self.debugOut = debugOut self.nodes = None # FIXME - self.isConnected = False + self.isConnected = threading.Event() + self.noProto = noProto if not noProto: self._startConfig() @@ -209,25 +215,34 @@ class MeshInterface: Keyword Arguments: destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) + portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ return self.sendData(text.encode("utf-8"), destinationId, - dataType=mesh_pb2.Data.CLEAR_TEXT, wantAck=wantAck, wantResponse=wantResponse) + portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse) - def sendData(self, byteData, destinationId=BROADCAST_ADDR, dataType=mesh_pb2.Data.OPAQUE, wantAck=False, wantResponse=False): + def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False): """Send a data packet to some other node Keyword Arguments: + data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes) destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) + portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ + if getattr(data, "SerializeToString", None): + logging.debug(f"Serializing protobuf as data: {data}") + data = data.SerializeToString() + + if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN: + raise Exception("Data payload too big") meshPacket = mesh_pb2.MeshPacket() - meshPacket.decoded.data.payload = byteData - meshPacket.decoded.data.typ = dataType + meshPacket.decoded.data.payload = data + meshPacket.decoded.data.portnum = portNum meshPacket.decoded.want_response = wantResponse return self.sendPacket(meshPacket, destinationId, wantAck=wantAck) @@ -242,22 +257,21 @@ class MeshInterface: Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ - meshPacket = mesh_pb2.MeshPacket() + p = mesh_pb2.Position() if(latitude != 0.0): - meshPacket.decoded.position.latitude_i = int(latitude / 1e-7) + p.latitude_i = int(latitude / 1e-7) if(longitude != 0.0): - meshPacket.decoded.position.longitude_i = int(longitude / 1e-7) + p.longitude_i = int(longitude / 1e-7) if(altitude != 0): - meshPacket.decoded.position.altitude = int(altitude) + p.altitude = int(altitude) if timeSec == 0: timeSec = time.time() # returns unix timestamp in seconds - meshPacket.decoded.position.time = int(timeSec) + p.time = int(timeSec) - meshPacket.decoded.want_response = wantResponse - return self.sendPacket(meshPacket, destinationId, wantAck=wantAck) + return self.sendData(p, destinationId, portNum=portnums_pb2.PortNum.POSITION_APP, wantAck=wantAck, wantResponse=wantResponse) def sendPacket(self, meshPacket, destinationId=BROADCAST_ADDR, wantAck=False): """Send a MeshPacket to the specified node (or if unspecified, broadcast). @@ -265,10 +279,14 @@ class MeshInterface: Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ + self._waitConnected() + toRadio = mesh_pb2.ToRadio() # FIXME add support for non broadcast addresses - if isinstance(destinationId, int): + if destinationId is None: + raise Exception("destinationId must not be None") + elif isinstance(destinationId, int): nodeNum = destinationId elif destinationId == BROADCAST_ADDR: nodeNum = BROADCAST_NUM @@ -303,6 +321,7 @@ class MeshInterface: t = mesh_pb2.ToRadio() t.set_radio.CopyFrom(self.radioConfig) self._sendToRadio(t) + logging.debug("Wrote config") def getMyNode(self): if self.myInfo is None: @@ -374,6 +393,12 @@ class MeshInterface: if write: self.writeConfig() + def _waitConnected(self): + """Block until the initial node db download is complete, or timeout + and raise an exception""" + if not self.isConnected.wait(5.0): # timeout after 5 seconds + raise Exception("Timed out waiting for connection completion") + def _generatePacketId(self): """Get a new unique packet ID""" if self.currentPacketId is None: @@ -384,13 +409,13 @@ class MeshInterface: def _disconnected(self): """Called by subclasses to tell clients this interface has disconnected""" - self.isConnected = False + self.isConnected.clear() pub.sendMessage("meshtastic.connection.lost", interface=self) def _connected(self): """Called by this class to tell clients we are now fully connected to a node """ - self.isConnected = True + self.isConnected.set() pub.sendMessage("meshtastic.connection.established", interface=self) def _startConfig(self): @@ -510,32 +535,29 @@ class MeshInterface: # We could provide our objects as DotMaps - which work with . notation or as dictionaries # asObj = DotMap(asDict) topic = "meshtastic.receive" # Generic unknown packet type - if meshPacket.decoded.HasField("position"): - topic = "meshtastic.receive.position" - p = asDict["decoded"]["position"] - self._fixupPosition(p) - # update node DB as needed - self._getOrCreateByNum(asDict["from"])["position"] = p - if meshPacket.decoded.HasField("user"): - topic = "meshtastic.receive.user" - u = asDict["decoded"]["user"] - # update node DB as needed - n = self._getOrCreateByNum(asDict["from"]) - n["user"] = u - # We now have a node ID, make sure it is uptodate in that table - self.nodes[u["id"]] = u + # Warn users if firmware doesn't use new portnum based data encodings + # But do not crash, because the lib will still basically work and ignore those packet types + if meshPacket.decoded.HasField("user") or meshPacket.decoded.HasField("position"): + logging.error("The device firmware is too old to work with this version of the python library. Please update firmware to 1.20 or later") if meshPacket.decoded.HasField("data"): - topic = "meshtastic.receive.data" - # OPAQUE is the default protobuf typ value, and therefore if not set it will not be populated at all + # The default MessageToDict converts byte arrays into base64 strings. + # We don't want that - it messes up data payload. So slam in the correct + # byte array. + asDict["decoded"]["data"]["payload"] = meshPacket.decoded.data.payload + + # UNKNOWN_APP is the default protobuf pornum value, and therefore if not set it will not be populated at all # to make API usage easier, set it to prevent confusion - if not "typ" in asDict["decoded"]["data"]: - asDict["decoded"]["data"]["typ"] = "OPAQUE" + if not "portnum" in asDict["decoded"]["data"]: + asDict["decoded"]["data"]["portnum"] = portnums_pb2.PortNum.UNKNOWN_APP + + portnum = asDict["decoded"]["data"]["portnum"] + topic = f"meshtastic.receive.data.{portnum}" # For text messages, we go ahead and decode the text to ascii for our users - if asDict["decoded"]["data"]["typ"] == "CLEAR_TEXT": + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.TEXT_MESSAGE_APP: topic = "meshtastic.receive.text" # We don't throw if the utf8 is invalid in the text message. Instead we just don't populate @@ -549,6 +571,31 @@ class MeshInterface: except Exception as ex: logging.error(f"Malformatted utf8 in text message: {ex}") + # decode position protobufs and update nodedb, provide decoded version as "position" in the published msg + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.POSITION_APP: + topic = "meshtastic.receive.position" + pb = mesh_pb2.Position() + pb.ParseFromString(meshPacket.decoded.data.payload) + p = google.protobuf.json_format.MessageToDict(pb) + self._fixupPosition(p) + asDict["decoded"]["data"]["position"] = p + # update node DB as needed + self._getOrCreateByNum(asDict["from"])["position"] = p + + # decode user protobufs and update nodedb, provide decoded version as "position" in the published msg + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.NODEINFO_APP: + topic = "meshtastic.receive.user" + pb = mesh_pb2.User() + pb.ParseFromString(meshPacket.decoded.data.payload) + u = google.protobuf.json_format.MessageToDict(pb) + asDict["decoded"]["data"]["user"] = u + # update node DB as needed + n = self._getOrCreateByNum(asDict["from"]) + n["user"] = u + # We now have a node ID, make sure it is uptodate in that table + self.nodes[u["id"]] = u + + logging.debug(f"Publishing topic {topic}") pub.sendMessage(topic, packet=asDict, interface=self) @@ -617,6 +664,7 @@ class StreamInterface(MeshInterface): self._rxBuf = bytes() # empty self._wantExit = False + # FIXME, figure out why daemon=True causes reader thread to exit too early self._rxThread = threading.Thread(target=self.__reader, args=()) MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto) @@ -637,6 +685,8 @@ class StreamInterface(MeshInterface): time.sleep(0.1) # wait 100ms to give device time to start running self._rxThread.start() + if not self.noProto: # Wait for the db download if using the protocol + self._waitConnected() def _disconnected(self): """We override the superclass implementation to close our port""" @@ -827,6 +877,18 @@ class TCPInterface(StreamInterface):

    Generated protocol buffer code.

    +
    meshtastic.portnums_pb2
    +
    +

    Generated protocol buffer code.

    +
    +
    meshtastic.remote_hardware
    +
    +
    +
    +
    meshtastic.remote_hardware_pb2
    +
    +

    Generated protocol buffer code.

    +
    meshtastic.test
    @@ -842,7 +904,8 @@ class TCPInterface(StreamInterface):
    var MY_CONFIG_ID
    -

    The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand

    +

    The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand

    +

    format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20

    @@ -857,7 +920,9 @@ class TCPInterface(StreamInterface):

    A not quite ready - FIXME - BLE interface to devices

    -

    Constructor

    +

    Constructor

    +

    Keyword Arguments: +noProto – If True, don't try to run our protocol on the link - just be a dumb serial client.

    Expand source code @@ -946,7 +1011,9 @@ class TCPInterface(StreamInterface):

    isConnected nodes debugOut

    -

    Constructor

    +

    Constructor

    +

    Keyword Arguments: +noProto – If True, don't try to run our protocol on the link - just be a dumb serial client.

    Expand source code @@ -962,10 +1029,15 @@ debugOut

    """ def __init__(self, debugOut=None, noProto=False): - """Constructor""" + """Constructor + + Keyword Arguments: + noProto -- If True, don't try to run our protocol on the link - just be a dumb serial client. + """ self.debugOut = debugOut self.nodes = None # FIXME - self.isConnected = False + self.isConnected = threading.Event() + self.noProto = noProto if not noProto: self._startConfig() @@ -987,25 +1059,34 @@ debugOut

    Keyword Arguments: destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) + portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ return self.sendData(text.encode("utf-8"), destinationId, - dataType=mesh_pb2.Data.CLEAR_TEXT, wantAck=wantAck, wantResponse=wantResponse) + portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse) - def sendData(self, byteData, destinationId=BROADCAST_ADDR, dataType=mesh_pb2.Data.OPAQUE, wantAck=False, wantResponse=False): + def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False): """Send a data packet to some other node Keyword Arguments: + data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes) destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) + portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ + if getattr(data, "SerializeToString", None): + logging.debug(f"Serializing protobuf as data: {data}") + data = data.SerializeToString() + + if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN: + raise Exception("Data payload too big") meshPacket = mesh_pb2.MeshPacket() - meshPacket.decoded.data.payload = byteData - meshPacket.decoded.data.typ = dataType + meshPacket.decoded.data.payload = data + meshPacket.decoded.data.portnum = portNum meshPacket.decoded.want_response = wantResponse return self.sendPacket(meshPacket, destinationId, wantAck=wantAck) @@ -1020,22 +1101,21 @@ debugOut

    Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ - meshPacket = mesh_pb2.MeshPacket() + p = mesh_pb2.Position() if(latitude != 0.0): - meshPacket.decoded.position.latitude_i = int(latitude / 1e-7) + p.latitude_i = int(latitude / 1e-7) if(longitude != 0.0): - meshPacket.decoded.position.longitude_i = int(longitude / 1e-7) + p.longitude_i = int(longitude / 1e-7) if(altitude != 0): - meshPacket.decoded.position.altitude = int(altitude) + p.altitude = int(altitude) if timeSec == 0: timeSec = time.time() # returns unix timestamp in seconds - meshPacket.decoded.position.time = int(timeSec) + p.time = int(timeSec) - meshPacket.decoded.want_response = wantResponse - return self.sendPacket(meshPacket, destinationId, wantAck=wantAck) + return self.sendData(p, destinationId, portNum=portnums_pb2.PortNum.POSITION_APP, wantAck=wantAck, wantResponse=wantResponse) def sendPacket(self, meshPacket, destinationId=BROADCAST_ADDR, wantAck=False): """Send a MeshPacket to the specified node (or if unspecified, broadcast). @@ -1043,10 +1123,14 @@ debugOut

    Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ + self._waitConnected() + toRadio = mesh_pb2.ToRadio() # FIXME add support for non broadcast addresses - if isinstance(destinationId, int): + if destinationId is None: + raise Exception("destinationId must not be None") + elif isinstance(destinationId, int): nodeNum = destinationId elif destinationId == BROADCAST_ADDR: nodeNum = BROADCAST_NUM @@ -1081,6 +1165,7 @@ debugOut

    t = mesh_pb2.ToRadio() t.set_radio.CopyFrom(self.radioConfig) self._sendToRadio(t) + logging.debug("Wrote config") def getMyNode(self): if self.myInfo is None: @@ -1152,6 +1237,12 @@ debugOut

    if write: self.writeConfig() + def _waitConnected(self): + """Block until the initial node db download is complete, or timeout + and raise an exception""" + if not self.isConnected.wait(5.0): # timeout after 5 seconds + raise Exception("Timed out waiting for connection completion") + def _generatePacketId(self): """Get a new unique packet ID""" if self.currentPacketId is None: @@ -1162,13 +1253,13 @@ debugOut

    def _disconnected(self): """Called by subclasses to tell clients this interface has disconnected""" - self.isConnected = False + self.isConnected.clear() pub.sendMessage("meshtastic.connection.lost", interface=self) def _connected(self): """Called by this class to tell clients we are now fully connected to a node """ - self.isConnected = True + self.isConnected.set() pub.sendMessage("meshtastic.connection.established", interface=self) def _startConfig(self): @@ -1288,32 +1379,29 @@ debugOut

    # We could provide our objects as DotMaps - which work with . notation or as dictionaries # asObj = DotMap(asDict) topic = "meshtastic.receive" # Generic unknown packet type - if meshPacket.decoded.HasField("position"): - topic = "meshtastic.receive.position" - p = asDict["decoded"]["position"] - self._fixupPosition(p) - # update node DB as needed - self._getOrCreateByNum(asDict["from"])["position"] = p - if meshPacket.decoded.HasField("user"): - topic = "meshtastic.receive.user" - u = asDict["decoded"]["user"] - # update node DB as needed - n = self._getOrCreateByNum(asDict["from"]) - n["user"] = u - # We now have a node ID, make sure it is uptodate in that table - self.nodes[u["id"]] = u + # Warn users if firmware doesn't use new portnum based data encodings + # But do not crash, because the lib will still basically work and ignore those packet types + if meshPacket.decoded.HasField("user") or meshPacket.decoded.HasField("position"): + logging.error("The device firmware is too old to work with this version of the python library. Please update firmware to 1.20 or later") if meshPacket.decoded.HasField("data"): - topic = "meshtastic.receive.data" - # OPAQUE is the default protobuf typ value, and therefore if not set it will not be populated at all + # The default MessageToDict converts byte arrays into base64 strings. + # We don't want that - it messes up data payload. So slam in the correct + # byte array. + asDict["decoded"]["data"]["payload"] = meshPacket.decoded.data.payload + + # UNKNOWN_APP is the default protobuf pornum value, and therefore if not set it will not be populated at all # to make API usage easier, set it to prevent confusion - if not "typ" in asDict["decoded"]["data"]: - asDict["decoded"]["data"]["typ"] = "OPAQUE" + if not "portnum" in asDict["decoded"]["data"]: + asDict["decoded"]["data"]["portnum"] = portnums_pb2.PortNum.UNKNOWN_APP + + portnum = asDict["decoded"]["data"]["portnum"] + topic = f"meshtastic.receive.data.{portnum}" # For text messages, we go ahead and decode the text to ascii for our users - if asDict["decoded"]["data"]["typ"] == "CLEAR_TEXT": + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.TEXT_MESSAGE_APP: topic = "meshtastic.receive.text" # We don't throw if the utf8 is invalid in the text message. Instead we just don't populate @@ -1327,6 +1415,31 @@ debugOut

    except Exception as ex: logging.error(f"Malformatted utf8 in text message: {ex}") + # decode position protobufs and update nodedb, provide decoded version as "position" in the published msg + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.POSITION_APP: + topic = "meshtastic.receive.position" + pb = mesh_pb2.Position() + pb.ParseFromString(meshPacket.decoded.data.payload) + p = google.protobuf.json_format.MessageToDict(pb) + self._fixupPosition(p) + asDict["decoded"]["data"]["position"] = p + # update node DB as needed + self._getOrCreateByNum(asDict["from"])["position"] = p + + # decode user protobufs and update nodedb, provide decoded version as "position" in the published msg + if asDict["decoded"]["data"]["portnum"] == portnums_pb2.PortNum.NODEINFO_APP: + topic = "meshtastic.receive.user" + pb = mesh_pb2.User() + pb.ParseFromString(meshPacket.decoded.data.payload) + u = google.protobuf.json_format.MessageToDict(pb) + asDict["decoded"]["data"]["user"] = u + # update node DB as needed + n = self._getOrCreateByNum(asDict["from"]) + n["user"] = u + # We now have a node ID, make sure it is uptodate in that table + self.nodes[u["id"]] = u + + logging.debug(f"Publishing topic {topic}") pub.sendMessage(topic, packet=asDict, interface=self)

    Subclasses

    @@ -1408,30 +1521,40 @@ def channelURL(self):
    -def sendData(self, byteData, destinationId='^all', dataType=0, wantAck=False, wantResponse=False) +def sendData(self, data, destinationId='^all', portNum=256, wantAck=False, wantResponse=False)

    Send a data packet to some other node

    Keyword Arguments: +data – the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes) destinationId {nodeId or nodeNum} – where to send this message (default: {BROADCAST_ADDR}) +portNum – the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck – True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)

    Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.

    Expand source code -
    def sendData(self, byteData, destinationId=BROADCAST_ADDR, dataType=mesh_pb2.Data.OPAQUE, wantAck=False, wantResponse=False):
    +
    def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False):
         """Send a data packet to some other node
     
         Keyword Arguments:
    +        data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes)
             destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
    +        portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
             wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
     
         Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
         """
    +    if getattr(data, "SerializeToString", None):
    +        logging.debug(f"Serializing protobuf as data: {data}")
    +        data = data.SerializeToString()
    +
    +    if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
    +        raise Exception("Data payload too big")
         meshPacket = mesh_pb2.MeshPacket()
    -    meshPacket.decoded.data.payload = byteData
    -    meshPacket.decoded.data.typ = dataType
    +    meshPacket.decoded.data.payload = data
    +    meshPacket.decoded.data.portnum = portNum
         meshPacket.decoded.want_response = wantResponse
         return self.sendPacket(meshPacket, destinationId, wantAck=wantAck)
    @@ -1453,10 +1576,14 @@ You probably don't want this - use sendData instead.

    Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ + self._waitConnected() + toRadio = mesh_pb2.ToRadio() # FIXME add support for non broadcast addresses - if isinstance(destinationId, int): + if destinationId is None: + raise Exception("destinationId must not be None") + elif isinstance(destinationId, int): nodeNum = destinationId elif destinationId == BROADCAST_ADDR: nodeNum = BROADCAST_NUM @@ -1500,22 +1627,21 @@ the local position.

    Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ - meshPacket = mesh_pb2.MeshPacket() + p = mesh_pb2.Position() if(latitude != 0.0): - meshPacket.decoded.position.latitude_i = int(latitude / 1e-7) + p.latitude_i = int(latitude / 1e-7) if(longitude != 0.0): - meshPacket.decoded.position.longitude_i = int(longitude / 1e-7) + p.longitude_i = int(longitude / 1e-7) if(altitude != 0): - meshPacket.decoded.position.altitude = int(altitude) + p.altitude = int(altitude) if timeSec == 0: timeSec = time.time() # returns unix timestamp in seconds - meshPacket.decoded.position.time = int(timeSec) + p.time = int(timeSec) - meshPacket.decoded.want_response = wantResponse - return self.sendPacket(meshPacket, destinationId, wantAck=wantAck)
    + return self.sendData(p, destinationId, portNum=portnums_pb2.PortNum.POSITION_APP, wantAck=wantAck, wantResponse=wantResponse)
    @@ -1527,6 +1653,7 @@ the local position.

    text {string} – The text to send

    Keyword Arguments: destinationId {nodeId or nodeNum} – where to send this message (default: {BROADCAST_ADDR}) +portNum – the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck – True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)

    Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.

    @@ -1541,12 +1668,13 @@ wantAck – True if you want the message sent in a reliable manner (with ret Keyword Arguments: destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR}) + portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery) Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ return self.sendData(text.encode("utf-8"), destinationId, - dataType=mesh_pb2.Data.CLEAR_TEXT, wantAck=wantAck, wantResponse=wantResponse) + portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse)
    @@ -1643,7 +1771,8 @@ wantAck – True if you want the message sent in a reliable manner (with ret t = mesh_pb2.ToRadio() t.set_radio.CopyFrom(self.radioConfig) - self._sendToRadio(t) + self._sendToRadio(t) + logging.debug("Wrote config") @@ -1778,6 +1907,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from self._rxBuf = bytes() # empty self._wantExit = False + # FIXME, figure out why daemon=True causes reader thread to exit too early self._rxThread = threading.Thread(target=self.__reader, args=()) MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto) @@ -1798,6 +1928,8 @@ debugOut {stream} – If a stream is provided, any debug serial output from time.sleep(0.1) # wait 100ms to give device time to start running self._rxThread.start() + if not self.noProto: # Wait for the db download if using the protocol + self._waitConnected() def _disconnected(self): """We override the superclass implementation to close our port""" @@ -1937,7 +2069,9 @@ start the reading thread later.

    self._writeBytes(bytes([START1, START1, START1, START1])) time.sleep(0.1) # wait 100ms to give device time to start running - self._rxThread.start() + self._rxThread.start() + if not self.noProto: # Wait for the db download if using the protocol + self._waitConnected() @@ -2051,6 +2185,9 @@ hostname {string} – Hostname/IP address of the device to connect to

  • meshtastic.ble
  • meshtastic.mesh_pb2
  • +
  • meshtastic.portnums_pb2
  • +
  • meshtastic.remote_hardware
  • +
  • meshtastic.remote_hardware_pb2
  • meshtastic.test
  • meshtastic.util
  • diff --git a/docs/meshtastic/mesh_pb2.html b/docs/meshtastic/mesh_pb2.html index 673292c..32c25f0 100644 --- a/docs/meshtastic/mesh_pb2.html +++ b/docs/meshtastic/mesh_pb2.html @@ -41,16 +41,18 @@ from google.protobuf import symbol_database as _symbol_database _sym_db = _symbol_database.Default() +from . import portnums_pb2 as portnums__pb2 DESCRIPTOR = _descriptor.FileDescriptor( name='mesh.proto', package='', syntax='proto3', - serialized_options=b'\n\023com.geeksville.meshB\nMeshProtos', + serialized_options=b'\n\023com.geeksville.meshB\nMeshProtosH\003', create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\nmesh.proto\"j\n\x08Position\x12\x12\n\nlatitude_i\x18\x07 \x01(\x11\x12\x13\n\x0blongitude_i\x18\x08 \x01(\x11\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(\x07\"g\n\x04\x44\x61ta\x12\x17\n\x03typ\x18\x01 \x01(\x0e\x32\n.Data.Type\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"5\n\x04Type\x12\n\n\x06OPAQUE\x10\x00\x12\x0e\n\nCLEAR_TEXT\x10\x01\x12\x11\n\rCLEAR_READACK\x10\x02\"J\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\"\x1f\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x02 \x03(\x05\"\xd3\x02\n\tSubPacket\x12\x1d\n\x08position\x18\x01 \x01(\x0b\x32\t.PositionH\x00\x12\x15\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x05.DataH\x00\x12\x15\n\x04user\x18\x04 \x01(\x0b\x32\x05.UserH\x00\x12(\n\rroute_request\x18\x06 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0broute_reply\x18\x07 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12\"\n\x0broute_error\x18\r \x01(\x0e\x32\x0b.RouteErrorH\x00\x12\x15\n\rwant_response\x18\x05 \x01(\x08\x12\x14\n\nsuccess_id\x18\n \x01(\rH\x01\x12\x11\n\x07\x66\x61il_id\x18\x0b \x01(\rH\x01\x12\x0c\n\x04\x64\x65st\x18\t \x01(\r\x12\x0e\n\x06source\x18\x0c \x01(\r\x12\x13\n\x0boriginal_id\x18\x02 \x01(\rB\t\n\x07payloadB\x05\n\x03\x61\x63k\"\xb7\x01\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\r\x12\n\n\x02to\x18\x02 \x01(\r\x12\x1d\n\x07\x64\x65\x63oded\x18\x03 \x01(\x0b\x32\n.SubPacketH\x00\x12\x13\n\tencrypted\x18\x08 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\r\x12\x0f\n\x07rx_time\x18\t \x01(\x07\x12\x0e\n\x06rx_snr\x18\x07 \x01(\x02\x12\x11\n\thop_limit\x18\n \x01(\r\x12\x10\n\x08want_ack\x18\x0b \x01(\x08\x42\t\n\x07payload\"\xa8\x02\n\x0f\x43hannelSettings\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x32\n\x0cmodem_config\x18\x03 \x01(\x0e\x32\x1c.ChannelSettings.ModemConfig\x12\x11\n\tbandwidth\x18\x06 \x01(\r\x12\x15\n\rspread_factor\x18\x07 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x08 \x01(\r\x12\x13\n\x0b\x63hannel_num\x18\t \x01(\r\x12\x0b\n\x03psk\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\"`\n\x0bModemConfig\x12\x12\n\x0e\x42w125Cr45Sf128\x10\x00\x12\x12\n\x0e\x42w500Cr45Sf128\x10\x01\x12\x14\n\x10\x42w31_25Cr48Sf512\x10\x02\x12\x13\n\x0f\x42w125Cr48Sf4096\x10\x03\"\xe0\x05\n\x0bRadioConfig\x12\x31\n\x0bpreferences\x18\x01 \x01(\x0b\x32\x1c.RadioConfig.UserPreferences\x12*\n\x10\x63hannel_settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x1a\xf1\x04\n\x0fUserPreferences\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12\x1b\n\x13send_owner_interval\x18\x02 \x01(\r\x12\x1a\n\x12num_missed_to_fail\x18\x03 \x01(\r\x12\x1b\n\x13wait_bluetooth_secs\x18\x04 \x01(\r\x12\x16\n\x0escreen_on_secs\x18\x05 \x01(\r\x12\x1a\n\x12phone_timeout_secs\x18\x06 \x01(\r\x12\x1d\n\x15phone_sds_timeout_sec\x18\x07 \x01(\r\x12\x1d\n\x15mesh_sds_timeout_secs\x18\x08 \x01(\r\x12\x10\n\x08sds_secs\x18\t \x01(\r\x12\x0f\n\x07ls_secs\x18\n \x01(\r\x12\x15\n\rmin_wake_secs\x18\x0b \x01(\r\x12\x11\n\twifi_ssid\x18\x0c \x01(\t\x12\x15\n\rwifi_password\x18\r \x01(\t\x12\x14\n\x0cwifi_ap_mode\x18\x0e \x01(\x08\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\x12\x11\n\tis_router\x18% \x01(\x08\x12\x14\n\x0cis_low_power\x18& \x01(\x08\x12\x15\n\rfactory_reset\x18\x64 \x01(\x08\x12(\n\x0elocation_share\x18 \x01(\x0e\x32\x10.LocationSharing\x12$\n\rgps_operation\x18! \x01(\x0e\x32\r.GpsOperation\x12\x1b\n\x13gps_update_interval\x18\" \x01(\r\x12\x18\n\x10gps_attempt_time\x18$ \x01(\r\x12\x17\n\x0fignore_incoming\x18g \x03(\r\"h\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\x10\n\x08next_hop\x18\x05 \x01(\r\"\xc5\x02\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x0f\n\x07has_gps\x18\x02 \x01(\x08\x12\x14\n\x0cnum_channels\x18\x03 \x01(\x05\x12\x0e\n\x06region\x18\x04 \x01(\t\x12\x10\n\x08hw_model\x18\x05 \x01(\t\x12\x18\n\x10\x66irmware_version\x18\x06 \x01(\t\x12\x12\n\nerror_code\x18\x07 \x01(\r\x12\x15\n\rerror_address\x18\x08 \x01(\r\x12\x13\n\x0b\x65rror_count\x18\t \x01(\r\x12\x16\n\x0epacket_id_bits\x18\n \x01(\r\x12\x19\n\x11\x63urrent_packet_id\x18\x0b \x01(\r\x12\x15\n\rnode_num_bits\x18\x0c \x01(\r\x12\x1c\n\x14message_timeout_msec\x18\r \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0e \x01(\r\"\xfd\x01\n\x0b\x44\x65viceState\x12\x1b\n\x05radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\"\x1e\n\x0b\x44\x65\x62ugString\x12\x0f\n\x07message\x18\x01 \x01(\t\"\xf9\x01\n\tFromRadio\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x1d\n\x06packet\x18\x02 \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\x1d\n\x05radio\x18\x06 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12$\n\x0c\x64\x65\x62ug_string\x18\x07 \x01(\x0b\x32\x0c.DebugStringH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x08 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\t \x01(\x08H\x00\x42\t\n\x07variant\"\x8c\x01\n\x07ToRadio\x12\x1d\n\x06packet\x18\x01 \x01(\x0b\x32\x0b.MeshPacketH\x00\x12\x18\n\x0ewant_config_id\x18\x64 \x01(\rH\x00\x12!\n\tset_radio\x18\x65 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x66 \x01(\x0b\x32\x05.UserH\x00\x42\t\n\x07variant\"f\n\x11ManufacturingData\x12\x12\n\nfradioFreq\x18\x01 \x01(\r\x12\x10\n\x08hw_model\x18\x02 \x01(\t\x12\x12\n\nhw_version\x18\x03 \x01(\t\x12\x17\n\x0fselftest_result\x18\x04 \x01(\x11*>\n\nRouteError\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*\x17\n\tConstants\x12\n\n\x06Unused\x10\x00*^\n\nRegionCode\x12\t\n\x05Unset\x10\x00\x12\x06\n\x02US\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x45U865\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08*U\n\x0cGpsOperation\x12\x0e\n\nGpsOpUnset\x10\x00\x12\x0f\n\x0bGpsOpMobile\x10\x02\x12\x11\n\rGpsOpTimeOnly\x10\x03\x12\x11\n\rGpsOpDisabled\x10\x04*@\n\x0fLocationSharing\x12\x0c\n\x08LocUnset\x10\x00\x12\x0e\n\nLocEnabled\x10\x01\x12\x0f\n\x0bLocDisabled\x10\x02\x42!\n\x13\x63om.geeksville.meshB\nMeshProtosb\x06proto3' -) + serialized_pb=b'\n\nmesh.proto\x1a\x0eportnums.proto\"j\n\x08Position\x12\x12\n\nlatitude_i\x18\x07 \x01(\x11\x12\x13\n\x0blongitude_i\x18\x08 \x01(\x11\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(\x07\"2\n\x04\x44\x61ta\x12\x19\n\x07portnum\x18\x01 \x01(\x0e\x32\x08.PortNum\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\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\"\x1f\n\x0eRouteDiscovery\x12\r\n\x05route\x18\x02 \x03(\x05\"\xdb\x02\n\tSubPacket\x12!\n\x08position\x18\x01 \x01(\x0b\x32\t.PositionB\x02\x18\x01H\x00\x12\x15\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x05.DataH\x00\x12\x19\n\x04user\x18\x04 \x01(\x0b\x32\x05.UserB\x02\x18\x01H\x00\x12(\n\rroute_request\x18\x06 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12&\n\x0broute_reply\x18\x07 \x01(\x0b\x32\x0f.RouteDiscoveryH\x00\x12\"\n\x0broute_error\x18\r \x01(\x0e\x32\x0b.RouteErrorH\x00\x12\x15\n\rwant_response\x18\x05 \x01(\x08\x12\x14\n\nsuccess_id\x18\n \x01(\rH\x01\x12\x11\n\x07\x66\x61il_id\x18\x0b \x01(\rH\x01\x12\x0c\n\x04\x64\x65st\x18\t \x01(\r\x12\x0e\n\x06source\x18\x0c \x01(\r\x12\x13\n\x0boriginal_id\x18\x02 \x01(\rB\t\n\x07payloadB\x05\n\x03\x61\x63k\"\xb7\x01\n\nMeshPacket\x12\x0c\n\x04\x66rom\x18\x01 \x01(\r\x12\n\n\x02to\x18\x02 \x01(\r\x12\x1d\n\x07\x64\x65\x63oded\x18\x03 \x01(\x0b\x32\n.SubPacketH\x00\x12\x13\n\tencrypted\x18\x08 \x01(\x0cH\x00\x12\n\n\x02id\x18\x06 \x01(\r\x12\x0f\n\x07rx_time\x18\t \x01(\x07\x12\x0e\n\x06rx_snr\x18\x07 \x01(\x02\x12\x11\n\thop_limit\x18\n \x01(\r\x12\x10\n\x08want_ack\x18\x0b \x01(\x08\x42\t\n\x07payload\"\xac\x02\n\x0f\x43hannelSettings\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x36\n\x0cmodem_config\x18\x03 \x01(\x0e\x32\x1c.ChannelSettings.ModemConfigB\x02\x18\x01\x12\x11\n\tbandwidth\x18\x06 \x01(\r\x12\x15\n\rspread_factor\x18\x07 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x08 \x01(\r\x12\x13\n\x0b\x63hannel_num\x18\t \x01(\r\x12\x0b\n\x03psk\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\"`\n\x0bModemConfig\x12\x12\n\x0e\x42w125Cr45Sf128\x10\x00\x12\x12\n\x0e\x42w500Cr45Sf128\x10\x01\x12\x14\n\x10\x42w31_25Cr48Sf512\x10\x02\x12\x13\n\x0f\x42w125Cr48Sf4096\x10\x03\"\x93\x06\n\x0bRadioConfig\x12\x31\n\x0bpreferences\x18\x01 \x01(\x0b\x32\x1c.RadioConfig.UserPreferences\x12*\n\x10\x63hannel_settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x1a\xa4\x05\n\x0fUserPreferences\x12\x1f\n\x17position_broadcast_secs\x18\x01 \x01(\r\x12\x1b\n\x13send_owner_interval\x18\x02 \x01(\r\x12\x1a\n\x12num_missed_to_fail\x18\x03 \x01(\r\x12\x1b\n\x13wait_bluetooth_secs\x18\x04 \x01(\r\x12\x16\n\x0escreen_on_secs\x18\x05 \x01(\r\x12\x1a\n\x12phone_timeout_secs\x18\x06 \x01(\r\x12\x1d\n\x15phone_sds_timeout_sec\x18\x07 \x01(\r\x12\x1d\n\x15mesh_sds_timeout_secs\x18\x08 \x01(\r\x12\x10\n\x08sds_secs\x18\t \x01(\r\x12\x0f\n\x07ls_secs\x18\n \x01(\r\x12\x15\n\rmin_wake_secs\x18\x0b \x01(\r\x12\x11\n\twifi_ssid\x18\x0c \x01(\t\x12\x15\n\rwifi_password\x18\r \x01(\t\x12\x14\n\x0cwifi_ap_mode\x18\x0e \x01(\x08\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\x12\x11\n\tis_router\x18% \x01(\x08\x12\x14\n\x0cis_low_power\x18& \x01(\x08\x12\x16\n\x0e\x66ixed_position\x18\' \x01(\x08\x12\x15\n\rfactory_reset\x18\x64 \x01(\x08\x12\x19\n\x11\x64\x65\x62ug_log_enabled\x18\x65 \x01(\x08\x12(\n\x0elocation_share\x18 \x01(\x0e\x32\x10.LocationSharing\x12$\n\rgps_operation\x18! \x01(\x0e\x32\r.GpsOperation\x12\x1b\n\x13gps_update_interval\x18\" \x01(\r\x12\x18\n\x10gps_attempt_time\x18$ \x01(\r\x12\x17\n\x0fignore_incoming\x18g \x03(\r\"h\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\x10\n\x08next_hop\x18\x05 \x01(\r\"\xc5\x02\n\nMyNodeInfo\x12\x13\n\x0bmy_node_num\x18\x01 \x01(\r\x12\x0f\n\x07has_gps\x18\x02 \x01(\x08\x12\x14\n\x0cnum_channels\x18\x03 \x01(\x05\x12\x0e\n\x06region\x18\x04 \x01(\t\x12\x10\n\x08hw_model\x18\x05 \x01(\t\x12\x18\n\x10\x66irmware_version\x18\x06 \x01(\t\x12\x12\n\nerror_code\x18\x07 \x01(\r\x12\x15\n\rerror_address\x18\x08 \x01(\r\x12\x13\n\x0b\x65rror_count\x18\t \x01(\r\x12\x16\n\x0epacket_id_bits\x18\n \x01(\r\x12\x19\n\x11\x63urrent_packet_id\x18\x0b \x01(\r\x12\x15\n\rnode_num_bits\x18\x0c \x01(\r\x12\x1c\n\x14message_timeout_msec\x18\r \x01(\r\x12\x17\n\x0fmin_app_version\x18\x0e \x01(\r\"\xfd\x01\n\x0b\x44\x65viceState\x12\x1b\n\x05radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\"\x1e\n\x0b\x44\x65\x62ugString\x12\x0f\n\x07message\x18\x01 \x01(\t\"\xf9\x01\n\tFromRadio\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\x1d\n\x06packet\x18\x02 \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\x1d\n\x05radio\x18\x06 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12$\n\x0c\x64\x65\x62ug_string\x18\x07 \x01(\x0b\x32\x0c.DebugStringH\x00\x12\x1c\n\x12\x63onfig_complete_id\x18\x08 \x01(\rH\x00\x12\x12\n\x08rebooted\x18\t \x01(\x08H\x00\x42\t\n\x07variant\"\x8c\x01\n\x07ToRadio\x12\x1d\n\x06packet\x18\x01 \x01(\x0b\x32\x0b.MeshPacketH\x00\x12\x18\n\x0ewant_config_id\x18\x64 \x01(\rH\x00\x12!\n\tset_radio\x18\x65 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x66 \x01(\x0b\x32\x05.UserH\x00\x42\t\n\x07variant*>\n\nRouteError\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*.\n\tConstants\x12\n\n\x06Unused\x10\x00\x12\x15\n\x10\x44\x41TA_PAYLOAD_LEN\x10\xf0\x01*^\n\nRegionCode\x12\t\n\x05Unset\x10\x00\x12\x06\n\x02US\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x45U865\x10\x03\x12\x06\n\x02\x43N\x10\x04\x12\x06\n\x02JP\x10\x05\x12\x07\n\x03\x41NZ\x10\x06\x12\x06\n\x02KR\x10\x07\x12\x06\n\x02TW\x10\x08*U\n\x0cGpsOperation\x12\x0e\n\nGpsOpUnset\x10\x00\x12\x0f\n\x0bGpsOpMobile\x10\x02\x12\x11\n\rGpsOpTimeOnly\x10\x03\x12\x11\n\rGpsOpDisabled\x10\x04*@\n\x0fLocationSharing\x12\x0c\n\x08LocUnset\x10\x00\x12\x0e\n\nLocEnabled\x10\x01\x12\x0f\n\x0bLocDisabled\x10\x02\x42#\n\x13\x63om.geeksville.meshB\nMeshProtosH\x03\x62\x06proto3' + , + dependencies=[portnums__pb2.DESCRIPTOR,]) _ROUTEERROR = _descriptor.EnumDescriptor( name='RouteError', @@ -82,8 +84,8 @@ _ROUTEERROR = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3123, - serialized_end=3185, + serialized_start=3045, + serialized_end=3107, ) _sym_db.RegisterEnumDescriptor(_ROUTEERROR) @@ -100,11 +102,16 @@ _CONSTANTS = _descriptor.EnumDescriptor( serialized_options=None, type=None, create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DATA_PAYLOAD_LEN', index=1, number=240, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, - serialized_start=3187, - serialized_end=3210, + serialized_start=3109, + serialized_end=3155, ) _sym_db.RegisterEnumDescriptor(_CONSTANTS) @@ -164,8 +171,8 @@ _REGIONCODE = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3212, - serialized_end=3306, + serialized_start=3157, + serialized_end=3251, ) _sym_db.RegisterEnumDescriptor(_REGIONCODE) @@ -200,8 +207,8 @@ _GPSOPERATION = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3308, - serialized_end=3393, + serialized_start=3253, + serialized_end=3338, ) _sym_db.RegisterEnumDescriptor(_GPSOPERATION) @@ -231,8 +238,8 @@ _LOCATIONSHARING = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3395, - serialized_end=3459, + serialized_start=3340, + serialized_end=3404, ) _sym_db.RegisterEnumDescriptor(_LOCATIONSHARING) @@ -242,6 +249,7 @@ NO_ROUTE = 1 GOT_NAK = 2 TIMEOUT = 3 Unused = 0 +DATA_PAYLOAD_LEN = 240 Unset = 0 US = 1 EU433 = 2 @@ -260,36 +268,6 @@ LocEnabled = 1 LocDisabled = 2 -_DATA_TYPE = _descriptor.EnumDescriptor( - name='Type', - full_name='Data.Type', - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name='OPAQUE', index=0, number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='CLEAR_TEXT', index=1, number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='CLEAR_READACK', index=2, number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - ], - containing_type=None, - serialized_options=None, - serialized_start=172, - serialized_end=225, -) -_sym_db.RegisterEnumDescriptor(_DATA_TYPE) - _CHANNELSETTINGS_MODEMCONFIG = _descriptor.EnumDescriptor( name='ModemConfig', full_name='ChannelSettings.ModemConfig', @@ -320,8 +298,8 @@ _CHANNELSETTINGS_MODEMCONFIG = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=1065, - serialized_end=1161, + serialized_start=1040, + serialized_end=1136, ) _sym_db.RegisterEnumDescriptor(_CHANNELSETTINGS_MODEMCONFIG) @@ -381,8 +359,8 @@ _POSITION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=14, - serialized_end=120, + serialized_start=30, + serialized_end=136, ) @@ -395,7 +373,7 @@ _DATA = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='typ', full_name='Data.typ', index=0, + name='portnum', full_name='Data.portnum', index=0, number=1, type=14, cpp_type=8, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, @@ -413,7 +391,6 @@ _DATA = _descriptor.Descriptor( ], nested_types=[], enum_types=[ - _DATA_TYPE, ], serialized_options=None, is_extendable=False, @@ -421,8 +398,8 @@ _DATA = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=122, - serialized_end=225, + serialized_start=138, + serialized_end=188, ) @@ -474,8 +451,8 @@ _USER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=227, - serialized_end=301, + serialized_start=190, + serialized_end=264, ) @@ -506,8 +483,8 @@ _ROUTEDISCOVERY = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=303, - serialized_end=334, + serialized_start=266, + serialized_end=297, ) @@ -525,7 +502,7 @@ _SUBPACKET = _descriptor.Descriptor( 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), + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='data', full_name='SubPacket.data', index=1, number=3, type=11, cpp_type=10, label=1, @@ -539,7 +516,7 @@ _SUBPACKET = _descriptor.Descriptor( 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), + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='route_request', full_name='SubPacket.route_request', index=3, number=6, type=11, cpp_type=10, label=1, @@ -625,8 +602,8 @@ _SUBPACKET = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=337, - serialized_end=676, + serialized_start=300, + serialized_end=647, ) @@ -718,8 +695,8 @@ _MESHPACKET = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=679, - serialized_end=862, + serialized_start=650, + serialized_end=833, ) @@ -744,7 +721,7 @@ _CHANNELSETTINGS = _descriptor.Descriptor( 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), + serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( name='bandwidth', full_name='ChannelSettings.bandwidth', index=2, number=6, type=13, cpp_type=3, label=1, @@ -800,8 +777,8 @@ _CHANNELSETTINGS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=865, - serialized_end=1161, + serialized_start=836, + serialized_end=1136, ) @@ -933,42 +910,56 @@ _RADIOCONFIG_USERPREFERENCES = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='factory_reset', full_name='RadioConfig.UserPreferences.factory_reset', index=17, + name='fixed_position', full_name='RadioConfig.UserPreferences.fixed_position', index=17, + number=39, 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), + _descriptor.FieldDescriptor( + name='factory_reset', full_name='RadioConfig.UserPreferences.factory_reset', index=18, number=100, 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), _descriptor.FieldDescriptor( - name='location_share', full_name='RadioConfig.UserPreferences.location_share', index=18, + name='debug_log_enabled', full_name='RadioConfig.UserPreferences.debug_log_enabled', index=19, + number=101, 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), + _descriptor.FieldDescriptor( + name='location_share', full_name='RadioConfig.UserPreferences.location_share', index=20, number=32, type=14, cpp_type=8, 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='gps_operation', full_name='RadioConfig.UserPreferences.gps_operation', index=19, + name='gps_operation', full_name='RadioConfig.UserPreferences.gps_operation', index=21, number=33, type=14, cpp_type=8, 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='gps_update_interval', full_name='RadioConfig.UserPreferences.gps_update_interval', index=20, + name='gps_update_interval', full_name='RadioConfig.UserPreferences.gps_update_interval', index=22, number=34, 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='gps_attempt_time', full_name='RadioConfig.UserPreferences.gps_attempt_time', index=21, + name='gps_attempt_time', full_name='RadioConfig.UserPreferences.gps_attempt_time', index=23, number=36, 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='ignore_incoming', full_name='RadioConfig.UserPreferences.ignore_incoming', index=22, + name='ignore_incoming', full_name='RadioConfig.UserPreferences.ignore_incoming', index=24, number=103, type=13, cpp_type=3, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, @@ -986,8 +977,8 @@ _RADIOCONFIG_USERPREFERENCES = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1275, - serialized_end=1900, + serialized_start=1250, + serialized_end=1926, ) _RADIOCONFIG = _descriptor.Descriptor( @@ -1024,8 +1015,8 @@ _RADIOCONFIG = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1164, - serialized_end=1900, + serialized_start=1139, + serialized_end=1926, ) @@ -1084,8 +1075,8 @@ _NODEINFO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1902, - serialized_end=2006, + serialized_start=1928, + serialized_end=2032, ) @@ -1207,8 +1198,8 @@ _MYNODEINFO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2009, - serialized_end=2334, + serialized_start=2035, + serialized_end=2360, ) @@ -1295,8 +1286,8 @@ _DEVICESTATE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2337, - serialized_end=2590, + serialized_start=2363, + serialized_end=2616, ) @@ -1327,8 +1318,8 @@ _DEBUGSTRING = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2592, - serialized_end=2622, + serialized_start=2618, + serialized_end=2648, ) @@ -1413,8 +1404,8 @@ _FROMRADIO = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=2625, - serialized_end=2874, + serialized_start=2651, + serialized_end=2900, ) @@ -1471,65 +1462,11 @@ _TORADIO = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=2877, - serialized_end=3017, + serialized_start=2903, + serialized_end=3043, ) - -_MANUFACTURINGDATA = _descriptor.Descriptor( - name='ManufacturingData', - full_name='ManufacturingData', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='fradioFreq', full_name='ManufacturingData.fradioFreq', 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='hw_model', full_name='ManufacturingData.hw_model', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - 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='hw_version', full_name='ManufacturingData.hw_version', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - 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='selftest_result', full_name='ManufacturingData.selftest_result', index=3, - number=4, type=17, cpp_type=1, 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), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3019, - serialized_end=3121, -) - -_DATA.fields_by_name['typ'].enum_type = _DATA_TYPE -_DATA_TYPE.containing_type = _DATA +_DATA.fields_by_name['portnum'].enum_type = portnums__pb2._PORTNUM _SUBPACKET.fields_by_name['position'].message_type = _POSITION _SUBPACKET.fields_by_name['data'].message_type = _DATA _SUBPACKET.fields_by_name['user'].message_type = _USER @@ -1638,7 +1575,6 @@ DESCRIPTOR.message_types_by_name['DeviceState'] = _DEVICESTATE DESCRIPTOR.message_types_by_name['DebugString'] = _DEBUGSTRING DESCRIPTOR.message_types_by_name['FromRadio'] = _FROMRADIO DESCRIPTOR.message_types_by_name['ToRadio'] = _TORADIO -DESCRIPTOR.message_types_by_name['ManufacturingData'] = _MANUFACTURINGDATA DESCRIPTOR.enum_types_by_name['RouteError'] = _ROUTEERROR DESCRIPTOR.enum_types_by_name['Constants'] = _CONSTANTS DESCRIPTOR.enum_types_by_name['RegionCode'] = _REGIONCODE @@ -1752,15 +1688,11 @@ ToRadio = _reflection.GeneratedProtocolMessageType('ToRadio', (_message. }) _sym_db.RegisterMessage(ToRadio) -ManufacturingData = _reflection.GeneratedProtocolMessageType('ManufacturingData', (_message.Message,), { - 'DESCRIPTOR' : _MANUFACTURINGDATA, - '__module__' : 'mesh_pb2' - # @@protoc_insertion_point(class_scope:ManufacturingData) - }) -_sym_db.RegisterMessage(ManufacturingData) - DESCRIPTOR._options = None +_SUBPACKET.fields_by_name['position']._options = None +_SUBPACKET.fields_by_name['user']._options = None +_CHANNELSETTINGS.fields_by_name['modem_config']._options = None # @@protoc_insertion_point(module_scope) @@ -2468,31 +2400,15 @@ shown below.

    Class variables

    -
    var CLEAR_READACK
    -
    -
    -
    -
    var CLEAR_TEXT
    -
    -
    -
    var DESCRIPTOR
    -
    var OPAQUE
    -
    -
    -
    var PAYLOAD_FIELD_NUMBER
    -
    var TYP_FIELD_NUMBER
    -
    -
    -
    -
    var Type
    +
    var PORTNUM_FIELD_NUMBER
    @@ -2547,9 +2463,9 @@ shown below.

    return self._fields.get(field, default_value) -
    var typ
    +
    var portnum
    -

    Getter for typ.

    +

    Getter for portnum.

    Expand source code @@ -5010,597 +4926,6 @@ and propagates this to our listener iff this was a state change.

    -
    -class ManufacturingData -(**kwargs) -
    -
    -

    Abstract base class for protocol messages.

    -

    Protocol message classes are almost always generated by the protocol -compiler. -These generated types subclass Message and implement the methods -shown below.

    -

    Ancestors

    - -

    Class variables

    -
    -
    var DESCRIPTOR
    -
    -
    -
    -
    var FRADIOFREQ_FIELD_NUMBER
    -
    -
    -
    -
    var HW_MODEL_FIELD_NUMBER
    -
    -
    -
    -
    var HW_VERSION_FIELD_NUMBER
    -
    -
    -
    -
    var SELFTEST_RESULT_FIELD_NUMBER
    -
    -
    -
    -
    -

    Static methods

    -
    -
    -def FromString(s) -
    -
    -
    -
    - -Expand source code - -
    def FromString(s):
    -  message = cls()
    -  message.MergeFromString(s)
    -  return message
    -
    -
    -
    -def RegisterExtension(extension_handle) -
    -
    -
    -
    - -Expand source code - -
    def RegisterExtension(extension_handle):
    -  extension_handle.containing_type = cls.DESCRIPTOR
    -  # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available.
    -  # pylint: disable=protected-access
    -  cls.DESCRIPTOR.file.pool._AddExtensionDescriptor(extension_handle)
    -  _AttachFieldHelpers(cls, extension_handle)
    -
    -
    -
    -

    Instance variables

    -
    -
    var fradioFreq
    -
    -

    Getter for fradioFreq.

    -
    - -Expand source code - -
    def getter(self):
    -  # TODO(protobuf-team): This may be broken since there may not be
    -  # default_value.  Combine with has_default_value somehow.
    -  return self._fields.get(field, default_value)
    -
    -
    -
    var hw_model
    -
    -

    Getter for hw_model.

    -
    - -Expand source code - -
    def getter(self):
    -  # TODO(protobuf-team): This may be broken since there may not be
    -  # default_value.  Combine with has_default_value somehow.
    -  return self._fields.get(field, default_value)
    -
    -
    -
    var hw_version
    -
    -

    Getter for hw_version.

    -
    - -Expand source code - -
    def getter(self):
    -  # TODO(protobuf-team): This may be broken since there may not be
    -  # default_value.  Combine with has_default_value somehow.
    -  return self._fields.get(field, default_value)
    -
    -
    -
    var selftest_result
    -
    -

    Getter for selftest_result.

    -
    - -Expand source code - -
    def getter(self):
    -  # TODO(protobuf-team): This may be broken since there may not be
    -  # default_value.  Combine with has_default_value somehow.
    -  return self._fields.get(field, default_value)
    -
    -
    -
    -

    Methods

    -
    -
    -def ByteSize(self) -
    -
    -
    -
    - -Expand source code - -
    def ByteSize(self):
    -  if not self._cached_byte_size_dirty:
    -    return self._cached_byte_size
    -
    -  size = 0
    -  descriptor = self.DESCRIPTOR
    -  if descriptor.GetOptions().map_entry:
    -    # Fields of map entry should always be serialized.
    -    size = descriptor.fields_by_name['key']._sizer(self.key)
    -    size += descriptor.fields_by_name['value']._sizer(self.value)
    -  else:
    -    for field_descriptor, field_value in self.ListFields():
    -      size += field_descriptor._sizer(field_value)
    -    for tag_bytes, value_bytes in self._unknown_fields:
    -      size += len(tag_bytes) + len(value_bytes)
    -
    -  self._cached_byte_size = size
    -  self._cached_byte_size_dirty = False
    -  self._listener_for_children.dirty = False
    -  return size
    -
    -
    -
    -def Clear(self) -
    -
    -
    -
    - -Expand source code - -
    def _Clear(self):
    -  # Clear fields.
    -  self._fields = {}
    -  self._unknown_fields = ()
    -  # pylint: disable=protected-access
    -  if self._unknown_field_set is not None:
    -    self._unknown_field_set._clear()
    -    self._unknown_field_set = None
    -
    -  self._oneofs = {}
    -  self._Modified()
    -
    -
    -
    -def ClearField(self, field_name) -
    -
    -
    -
    - -Expand source code - -
    def ClearField(self, field_name):
    -  try:
    -    field = message_descriptor.fields_by_name[field_name]
    -  except KeyError:
    -    try:
    -      field = message_descriptor.oneofs_by_name[field_name]
    -      if field in self._oneofs:
    -        field = self._oneofs[field]
    -      else:
    -        return
    -    except KeyError:
    -      raise ValueError('Protocol message %s has no "%s" field.' %
    -                       (message_descriptor.name, field_name))
    -
    -  if field in self._fields:
    -    # To match the C++ implementation, we need to invalidate iterators
    -    # for map fields when ClearField() happens.
    -    if hasattr(self._fields[field], 'InvalidateIterators'):
    -      self._fields[field].InvalidateIterators()
    -
    -    # Note:  If the field is a sub-message, its listener will still point
    -    #   at us.  That's fine, because the worst than can happen is that it
    -    #   will call _Modified() and invalidate our byte size.  Big deal.
    -    del self._fields[field]
    -
    -    if self._oneofs.get(field.containing_oneof, None) is field:
    -      del self._oneofs[field.containing_oneof]
    -
    -  # Always call _Modified() -- even if nothing was changed, this is
    -  # a mutating method, and thus calling it should cause the field to become
    -  # present in the parent message.
    -  self._Modified()
    -
    -
    -
    -def DiscardUnknownFields(self) -
    -
    -
    -
    - -Expand source code - -
    def _DiscardUnknownFields(self):
    -  self._unknown_fields = []
    -  self._unknown_field_set = None      # pylint: disable=protected-access
    -  for field, value in self.ListFields():
    -    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
    -      if _IsMapField(field):
    -        if _IsMessageMapField(field):
    -          for key in value:
    -            value[key].DiscardUnknownFields()
    -      elif field.label == _FieldDescriptor.LABEL_REPEATED:
    -        for sub_message in value:
    -          sub_message.DiscardUnknownFields()
    -      else:
    -        value.DiscardUnknownFields()
    -
    -
    -
    -def FindInitializationErrors(self) -
    -
    -

    Finds required fields which are not initialized.

    -

    Returns

    -

    A list of strings. -Each string is a path to an uninitialized field from -the top-level message, e.g. "foo.bar[5].baz".

    -
    - -Expand source code - -
    def FindInitializationErrors(self):
    -  """Finds required fields which are not initialized.
    -
    -  Returns:
    -    A list of strings.  Each string is a path to an uninitialized field from
    -    the top-level message, e.g. "foo.bar[5].baz".
    -  """
    -
    -  errors = []  # simplify things
    -
    -  for field in required_fields:
    -    if not self.HasField(field.name):
    -      errors.append(field.name)
    -
    -  for field, value in self.ListFields():
    -    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
    -      if field.is_extension:
    -        name = '(%s)' % field.full_name
    -      else:
    -        name = field.name
    -
    -      if _IsMapField(field):
    -        if _IsMessageMapField(field):
    -          for key in value:
    -            element = value[key]
    -            prefix = '%s[%s].' % (name, key)
    -            sub_errors = element.FindInitializationErrors()
    -            errors += [prefix + error for error in sub_errors]
    -        else:
    -          # ScalarMaps can't have any initialization errors.
    -          pass
    -      elif field.label == _FieldDescriptor.LABEL_REPEATED:
    -        for i in range(len(value)):
    -          element = value[i]
    -          prefix = '%s[%d].' % (name, i)
    -          sub_errors = element.FindInitializationErrors()
    -          errors += [prefix + error for error in sub_errors]
    -      else:
    -        prefix = name + '.'
    -        sub_errors = value.FindInitializationErrors()
    -        errors += [prefix + error for error in sub_errors]
    -
    -  return errors
    -
    -
    -
    -def HasField(self, field_name) -
    -
    -
    -
    - -Expand source code - -
    def HasField(self, field_name):
    -  try:
    -    field = hassable_fields[field_name]
    -  except KeyError:
    -    raise ValueError(error_msg % (message_descriptor.full_name, field_name))
    -
    -  if isinstance(field, descriptor_mod.OneofDescriptor):
    -    try:
    -      return HasField(self, self._oneofs[field].name)
    -    except KeyError:
    -      return False
    -  else:
    -    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
    -      value = self._fields.get(field)
    -      return value is not None and value._is_present_in_parent
    -    else:
    -      return field in self._fields
    -
    -
    -
    -def IsInitialized(self, errors=None) -
    -
    -

    Checks if all required fields of a message are set.

    -

    Args

    -
    -
    errors
    -
    A list which, if provided, will be populated with the field -paths of all missing required fields.
    -
    -

    Returns

    -

    True iff the specified message has all required fields set.

    -
    - -Expand source code - -
    def IsInitialized(self, errors=None):
    -  """Checks if all required fields of a message are set.
    -
    -  Args:
    -    errors:  A list which, if provided, will be populated with the field
    -             paths of all missing required fields.
    -
    -  Returns:
    -    True iff the specified message has all required fields set.
    -  """
    -
    -  # Performance is critical so we avoid HasField() and ListFields().
    -
    -  for field in required_fields:
    -    if (field not in self._fields or
    -        (field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and
    -         not self._fields[field]._is_present_in_parent)):
    -      if errors is not None:
    -        errors.extend(self.FindInitializationErrors())
    -      return False
    -
    -  for field, value in list(self._fields.items()):  # dict can change size!
    -    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
    -      if field.label == _FieldDescriptor.LABEL_REPEATED:
    -        if (field.message_type.has_options and
    -            field.message_type.GetOptions().map_entry):
    -          continue
    -        for element in value:
    -          if not element.IsInitialized():
    -            if errors is not None:
    -              errors.extend(self.FindInitializationErrors())
    -            return False
    -      elif value._is_present_in_parent and not value.IsInitialized():
    -        if errors is not None:
    -          errors.extend(self.FindInitializationErrors())
    -        return False
    -
    -  return True
    -
    -
    -
    -def ListFields(self) -
    -
    -
    -
    - -Expand source code - -
    def ListFields(self):
    -  all_fields = [item for item in self._fields.items() if _IsPresent(item)]
    -  all_fields.sort(key = lambda item: item[0].number)
    -  return all_fields
    -
    -
    -
    -def MergeFrom(self, msg) -
    -
    -
    -
    - -Expand source code - -
    def MergeFrom(self, msg):
    -  if not isinstance(msg, cls):
    -    raise TypeError(
    -        'Parameter to MergeFrom() must be instance of same class: '
    -        'expected %s got %s.' % (cls.__name__, msg.__class__.__name__))
    -
    -  assert msg is not self
    -  self._Modified()
    -
    -  fields = self._fields
    -
    -  for field, value in msg._fields.items():
    -    if field.label == LABEL_REPEATED:
    -      field_value = fields.get(field)
    -      if field_value is None:
    -        # Construct a new object to represent this field.
    -        field_value = field._default_constructor(self)
    -        fields[field] = field_value
    -      field_value.MergeFrom(value)
    -    elif field.cpp_type == CPPTYPE_MESSAGE:
    -      if value._is_present_in_parent:
    -        field_value = fields.get(field)
    -        if field_value is None:
    -          # Construct a new object to represent this field.
    -          field_value = field._default_constructor(self)
    -          fields[field] = field_value
    -        field_value.MergeFrom(value)
    -    else:
    -      self._fields[field] = value
    -      if field.containing_oneof:
    -        self._UpdateOneofState(field)
    -
    -  if msg._unknown_fields:
    -    if not self._unknown_fields:
    -      self._unknown_fields = []
    -    self._unknown_fields.extend(msg._unknown_fields)
    -    # pylint: disable=protected-access
    -    if self._unknown_field_set is None:
    -      self._unknown_field_set = containers.UnknownFieldSet()
    -    self._unknown_field_set._extend(msg._unknown_field_set)
    -
    -
    -
    -def MergeFromString(self, serialized) -
    -
    -
    -
    - -Expand source code - -
    def MergeFromString(self, serialized):
    -  if isinstance(serialized, memoryview) and six.PY2:
    -    raise TypeError(
    -        'memoryview not supported in Python 2 with the pure Python proto '
    -        'implementation: this is to maintain compatibility with the C++ '
    -        'implementation')
    -
    -  serialized = memoryview(serialized)
    -  length = len(serialized)
    -  try:
    -    if self._InternalParse(serialized, 0, length) != length:
    -      # The only reason _InternalParse would return early is if it
    -      # encountered an end-group tag.
    -      raise message_mod.DecodeError('Unexpected end-group tag.')
    -  except (IndexError, TypeError):
    -    # Now ord(buf[p:p+1]) == ord('') gets TypeError.
    -    raise message_mod.DecodeError('Truncated message.')
    -  except struct.error as e:
    -    raise message_mod.DecodeError(e)
    -  return length   # Return this for legacy reasons.
    -
    -
    -
    -def SerializePartialToString(self, **kwargs) -
    -
    -
    -
    - -Expand source code - -
    def SerializePartialToString(self, **kwargs):
    -  out = BytesIO()
    -  self._InternalSerialize(out.write, **kwargs)
    -  return out.getvalue()
    -
    -
    -
    -def SerializeToString(self, **kwargs) -
    -
    -
    -
    - -Expand source code - -
    def SerializeToString(self, **kwargs):
    -  # Check if the message has all of its required fields set.
    -  if not self.IsInitialized():
    -    raise message_mod.EncodeError(
    -        'Message %s is missing required fields: %s' % (
    -        self.DESCRIPTOR.full_name, ','.join(self.FindInitializationErrors())))
    -  return self.SerializePartialToString(**kwargs)
    -
    -
    -
    -def SetInParent(self) -
    -
    -

    Sets the _cached_byte_size_dirty bit to true, -and propagates this to our listener iff this was a state change.

    -
    - -Expand source code - -
    def Modified(self):
    -  """Sets the _cached_byte_size_dirty bit to true,
    -  and propagates this to our listener iff this was a state change.
    -  """
    -
    -  # Note:  Some callers check _cached_byte_size_dirty before calling
    -  #   _Modified() as an extra optimization.  So, if this method is ever
    -  #   changed such that it does stuff even when _cached_byte_size_dirty is
    -  #   already true, the callers need to be updated.
    -  if not self._cached_byte_size_dirty:
    -    self._cached_byte_size_dirty = True
    -    self._listener_for_children.dirty = True
    -    self._is_present_in_parent = True
    -    self._listener.Modified()
    -
    -
    -
    -def UnknownFields(self) -
    -
    -
    -
    - -Expand source code - -
    def _UnknownFields(self):
    -  if self._unknown_field_set is None:  # pylint: disable=protected-access
    -    # pylint: disable=protected-access
    -    self._unknown_field_set = containers.UnknownFieldSet()
    -  return self._unknown_field_set    # pylint: disable=protected-access
    -
    -
    -
    -def WhichOneof(self, oneof_name) -
    -
    -

    Returns the name of the currently set field inside a oneof, or None.

    -
    - -Expand source code - -
    def WhichOneof(self, oneof_name):
    -  """Returns the name of the currently set field inside a oneof, or None."""
    -  try:
    -    field = message_descriptor.oneofs_by_name[oneof_name]
    -  except KeyError:
    -    raise ValueError(
    -        'Protocol message has no oneof "%s" field.' % oneof_name)
    -
    -  nested_field = self._oneofs.get(field, None)
    -  if nested_field is not None and self.HasField(nested_field.name):
    -    return nested_field.name
    -  else:
    -    return None
    -
    -
    -
    -
    class MeshPacket (**kwargs) @@ -11472,8 +10797,6 @@ and propagates this to our listener iff this was a state change.

    Data

  • @@ -11605,37 +10926,6 @@ and propagates this to our listener iff this was a state change.

  • -

    ManufacturingData

    - -
  • -
  • MeshPacket

    • ByteSize
    • diff --git a/docs/meshtastic/portnums_pb2.html b/docs/meshtastic/portnums_pb2.html new file mode 100644 index 0000000..085d2d2 --- /dev/null +++ b/docs/meshtastic/portnums_pb2.html @@ -0,0 +1,150 @@ + + + + + + +meshtastic.portnums_pb2 API documentation + + + + + + + + + + + +
      +
      +
      +

      Module meshtastic.portnums_pb2

      +
      +
      +

      Generated protocol buffer code.

      +
      + +Expand source code + +
      # -*- coding: utf-8 -*-
      +# Generated by the protocol buffer compiler.  DO NOT EDIT!
      +# source: portnums.proto
      +"""Generated protocol buffer code."""
      +from google.protobuf.internal import enum_type_wrapper
      +from google.protobuf import descriptor as _descriptor
      +from google.protobuf import message as _message
      +from google.protobuf import reflection as _reflection
      +from google.protobuf import symbol_database as _symbol_database
      +# @@protoc_insertion_point(imports)
      +
      +_sym_db = _symbol_database.Default()
      +
      +
      +
      +
      +DESCRIPTOR = _descriptor.FileDescriptor(
      +  name='portnums.proto',
      +  package='',
      +  syntax='proto3',
      +  serialized_options=b'\n\023com.geeksville.meshB\010PortnumsH\003',
      +  create_key=_descriptor._internal_create_key,
      +  serialized_pb=b'\n\x0eportnums.proto*\x93\x01\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x12\n\rIP_TUNNEL_APP\x10\x80\x08\x42!\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03\x62\x06proto3'
      +)
      +
      +_PORTNUM = _descriptor.EnumDescriptor(
      +  name='PortNum',
      +  full_name='PortNum',
      +  filename=None,
      +  file=DESCRIPTOR,
      +  create_key=_descriptor._internal_create_key,
      +  values=[
      +    _descriptor.EnumValueDescriptor(
      +      name='UNKNOWN_APP', index=0, number=0,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='TEXT_MESSAGE_APP', index=1, number=1,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='REMOTE_HARDWARE_APP', index=2, number=2,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='POSITION_APP', index=3, number=3,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='NODEINFO_APP', index=4, number=4,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='PRIVATE_APP', index=5, number=256,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='IP_TUNNEL_APP', index=6, number=1024,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +  ],
      +  containing_type=None,
      +  serialized_options=None,
      +  serialized_start=19,
      +  serialized_end=166,
      +)
      +_sym_db.RegisterEnumDescriptor(_PORTNUM)
      +
      +PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
      +UNKNOWN_APP = 0
      +TEXT_MESSAGE_APP = 1
      +REMOTE_HARDWARE_APP = 2
      +POSITION_APP = 3
      +NODEINFO_APP = 4
      +PRIVATE_APP = 256
      +IP_TUNNEL_APP = 1024
      +
      +
      +DESCRIPTOR.enum_types_by_name['PortNum'] = _PORTNUM
      +_sym_db.RegisterFileDescriptor(DESCRIPTOR)
      +
      +
      +DESCRIPTOR._options = None
      +# @@protoc_insertion_point(module_scope)
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/meshtastic/remote_hardware.html b/docs/meshtastic/remote_hardware.html new file mode 100644 index 0000000..63aa6fc --- /dev/null +++ b/docs/meshtastic/remote_hardware.html @@ -0,0 +1,238 @@ + + + + + + +meshtastic.remote_hardware API documentation + + + + + + + + + + + +
      +
      +
      +

      Module meshtastic.remote_hardware

      +
      +
      +
      + +Expand source code + +
      from . import portnums_pb2, remote_hardware_pb2
      +from pubsub import pub
      +
      +def onGPIOreceive(packet, interface):
      +    """Callback for received GPIO responses
      +    
      +    FIXME figure out how to do closures with methods in python"""
      +    pb = remote_hardware_pb2.HardwareMessage()
      +    pb.ParseFromString(packet["decoded"]["data"]["payload"])
      +    print(f"Received RemoteHardware typ={pb.typ}, gpio_value={pb.gpio_value}")
      +
      +class RemoteHardwareClient:
      +    """
      +    This is the client code to control/monitor simple hardware built into the 
      +    meshtastic devices.  It is intended to be both a useful API/service and example
      +    code for how you can connect to your own custom meshtastic services
      +    """
      +
      +    def __init__(self, iface):
      +        """
      +        Constructor
      +
      +        iface is the already open MeshInterface instance
      +        """
      +        self.iface = iface
      +
      +        pub.subscribe(onGPIOreceive, "meshtastic.receive.data.REMOTE_HARDWARE_APP")
      +
      +    def writeGPIOs(self, nodeid, mask, vals):
      +        """
      +        Write the specified vals bits to the device GPIOs.  Only bits in mask that
      +        are 1 will be changed
      +        """
      +        r = remote_hardware_pb2.HardwareMessage()
      +        r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
      +        r.gpio_mask = mask
      +        r.gpio_value = vals
      +        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
      +
      +    def readGPIOs(self, nodeid, mask):
      +        """Read the specified bits from GPIO inputs on the device"""
      +        r = remote_hardware_pb2.HardwareMessage()
      +        r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
      +        r.gpio_mask = mask
      +        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
      +
      +
      +
      +
      +
      +
      +
      +

      Functions

      +
      +
      +def onGPIOreceive(packet, interface) +
      +
      +

      Callback for received GPIO responses

      +

      FIXME figure out how to do closures with methods in python

      +
      + +Expand source code + +
      def onGPIOreceive(packet, interface):
      +    """Callback for received GPIO responses
      +    
      +    FIXME figure out how to do closures with methods in python"""
      +    pb = remote_hardware_pb2.HardwareMessage()
      +    pb.ParseFromString(packet["decoded"]["data"]["payload"])
      +    print(f"Received RemoteHardware typ={pb.typ}, gpio_value={pb.gpio_value}")
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class RemoteHardwareClient +(iface) +
      +
      +

      This is the client code to control/monitor simple hardware built into the +meshtastic devices. +It is intended to be both a useful API/service and example +code for how you can connect to your own custom meshtastic services

      +

      Constructor

      +

      iface is the already open MeshInterface instance

      +
      + +Expand source code + +
      class RemoteHardwareClient:
      +    """
      +    This is the client code to control/monitor simple hardware built into the 
      +    meshtastic devices.  It is intended to be both a useful API/service and example
      +    code for how you can connect to your own custom meshtastic services
      +    """
      +
      +    def __init__(self, iface):
      +        """
      +        Constructor
      +
      +        iface is the already open MeshInterface instance
      +        """
      +        self.iface = iface
      +
      +        pub.subscribe(onGPIOreceive, "meshtastic.receive.data.REMOTE_HARDWARE_APP")
      +
      +    def writeGPIOs(self, nodeid, mask, vals):
      +        """
      +        Write the specified vals bits to the device GPIOs.  Only bits in mask that
      +        are 1 will be changed
      +        """
      +        r = remote_hardware_pb2.HardwareMessage()
      +        r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
      +        r.gpio_mask = mask
      +        r.gpio_value = vals
      +        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
      +
      +    def readGPIOs(self, nodeid, mask):
      +        """Read the specified bits from GPIO inputs on the device"""
      +        r = remote_hardware_pb2.HardwareMessage()
      +        r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
      +        r.gpio_mask = mask
      +        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
      +
      +

      Methods

      +
      +
      +def readGPIOs(self, nodeid, mask) +
      +
      +

      Read the specified bits from GPIO inputs on the device

      +
      + +Expand source code + +
      def readGPIOs(self, nodeid, mask):
      +    """Read the specified bits from GPIO inputs on the device"""
      +    r = remote_hardware_pb2.HardwareMessage()
      +    r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
      +    r.gpio_mask = mask
      +    return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
      +
      +
      +
      +def writeGPIOs(self, nodeid, mask, vals) +
      +
      +

      Write the specified vals bits to the device GPIOs. +Only bits in mask that +are 1 will be changed

      +
      + +Expand source code + +
      def writeGPIOs(self, nodeid, mask, vals):
      +    """
      +    Write the specified vals bits to the device GPIOs.  Only bits in mask that
      +    are 1 will be changed
      +    """
      +    r = remote_hardware_pb2.HardwareMessage()
      +    r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
      +    r.gpio_mask = mask
      +    r.gpio_value = vals
      +    return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/docs/meshtastic/remote_hardware_pb2.html b/docs/meshtastic/remote_hardware_pb2.html new file mode 100644 index 0000000..ad616d5 --- /dev/null +++ b/docs/meshtastic/remote_hardware_pb2.html @@ -0,0 +1,837 @@ + + + + + + +meshtastic.remote_hardware_pb2 API documentation + + + + + + + + + + + +
      +
      +
      +

      Module meshtastic.remote_hardware_pb2

      +
      +
      +

      Generated protocol buffer code.

      +
      + +Expand source code + +
      # -*- coding: utf-8 -*-
      +# Generated by the protocol buffer compiler.  DO NOT EDIT!
      +# source: remote_hardware.proto
      +"""Generated protocol buffer code."""
      +from google.protobuf import descriptor as _descriptor
      +from google.protobuf import message as _message
      +from google.protobuf import reflection as _reflection
      +from google.protobuf import symbol_database as _symbol_database
      +# @@protoc_insertion_point(imports)
      +
      +_sym_db = _symbol_database.Default()
      +
      +
      +
      +
      +DESCRIPTOR = _descriptor.FileDescriptor(
      +  name='remote_hardware.proto',
      +  package='',
      +  syntax='proto3',
      +  serialized_options=b'\n\023com.geeksville.meshB\016RemoteHardwareH\003',
      +  create_key=_descriptor._internal_create_key,
      +  serialized_pb=b'\n\x15remote_hardware.proto\"\xca\x01\n\x0fHardwareMessage\x12\"\n\x03typ\x18\x01 \x01(\x0e\x32\x15.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\'\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareH\x03\x62\x06proto3'
      +)
      +
      +
      +
      +_HARDWAREMESSAGE_TYPE = _descriptor.EnumDescriptor(
      +  name='Type',
      +  full_name='HardwareMessage.Type',
      +  filename=None,
      +  file=DESCRIPTOR,
      +  create_key=_descriptor._internal_create_key,
      +  values=[
      +    _descriptor.EnumValueDescriptor(
      +      name='UNSET', index=0, number=0,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='WRITE_GPIOS', index=1, number=1,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='WATCH_GPIOS', index=2, number=2,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='GPIOS_CHANGED', index=3, number=3,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='READ_GPIOS', index=4, number=4,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +    _descriptor.EnumValueDescriptor(
      +      name='READ_GPIOS_REPLY', index=5, number=5,
      +      serialized_options=None,
      +      type=None,
      +      create_key=_descriptor._internal_create_key),
      +  ],
      +  containing_type=None,
      +  serialized_options=None,
      +  serialized_start=120,
      +  serialized_end=228,
      +)
      +_sym_db.RegisterEnumDescriptor(_HARDWAREMESSAGE_TYPE)
      +
      +
      +_HARDWAREMESSAGE = _descriptor.Descriptor(
      +  name='HardwareMessage',
      +  full_name='HardwareMessage',
      +  filename=None,
      +  file=DESCRIPTOR,
      +  containing_type=None,
      +  create_key=_descriptor._internal_create_key,
      +  fields=[
      +    _descriptor.FieldDescriptor(
      +      name='typ', full_name='HardwareMessage.typ', index=0,
      +      number=1, type=14, cpp_type=8, 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='gpio_mask', full_name='HardwareMessage.gpio_mask', index=1,
      +      number=2, type=4, cpp_type=4, 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='gpio_value', full_name='HardwareMessage.gpio_value', index=2,
      +      number=3, type=4, cpp_type=4, 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),
      +  ],
      +  extensions=[
      +  ],
      +  nested_types=[],
      +  enum_types=[
      +    _HARDWAREMESSAGE_TYPE,
      +  ],
      +  serialized_options=None,
      +  is_extendable=False,
      +  syntax='proto3',
      +  extension_ranges=[],
      +  oneofs=[
      +  ],
      +  serialized_start=26,
      +  serialized_end=228,
      +)
      +
      +_HARDWAREMESSAGE.fields_by_name['typ'].enum_type = _HARDWAREMESSAGE_TYPE
      +_HARDWAREMESSAGE_TYPE.containing_type = _HARDWAREMESSAGE
      +DESCRIPTOR.message_types_by_name['HardwareMessage'] = _HARDWAREMESSAGE
      +_sym_db.RegisterFileDescriptor(DESCRIPTOR)
      +
      +HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), {
      +  'DESCRIPTOR' : _HARDWAREMESSAGE,
      +  '__module__' : 'remote_hardware_pb2'
      +  # @@protoc_insertion_point(class_scope:HardwareMessage)
      +  })
      +_sym_db.RegisterMessage(HardwareMessage)
      +
      +
      +DESCRIPTOR._options = None
      +# @@protoc_insertion_point(module_scope)
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      Classes

      +
      +
      +class HardwareMessage +(**kwargs) +
      +
      +

      Abstract base class for protocol messages.

      +

      Protocol message classes are almost always generated by the protocol +compiler. +These generated types subclass Message and implement the methods +shown below.

      +

      Ancestors

      +
        +
      • google.protobuf.message.Message
      • +
      +

      Class variables

      +
      +
      var DESCRIPTOR
      +
      +
      +
      +
      var GPIOS_CHANGED
      +
      +
      +
      +
      var GPIO_MASK_FIELD_NUMBER
      +
      +
      +
      +
      var GPIO_VALUE_FIELD_NUMBER
      +
      +
      +
      +
      var READ_GPIOS
      +
      +
      +
      +
      var READ_GPIOS_REPLY
      +
      +
      +
      +
      var TYP_FIELD_NUMBER
      +
      +
      +
      +
      var Type
      +
      +
      +
      +
      var UNSET
      +
      +
      +
      +
      var WATCH_GPIOS
      +
      +
      +
      +
      var WRITE_GPIOS
      +
      +
      +
      +
      +

      Static methods

      +
      +
      +def FromString(s) +
      +
      +
      +
      + +Expand source code + +
      def FromString(s):
      +  message = cls()
      +  message.MergeFromString(s)
      +  return message
      +
      +
      +
      +def RegisterExtension(extension_handle) +
      +
      +
      +
      + +Expand source code + +
      def RegisterExtension(extension_handle):
      +  extension_handle.containing_type = cls.DESCRIPTOR
      +  # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available.
      +  # pylint: disable=protected-access
      +  cls.DESCRIPTOR.file.pool._AddExtensionDescriptor(extension_handle)
      +  _AttachFieldHelpers(cls, extension_handle)
      +
      +
      +
      +

      Instance variables

      +
      +
      var gpio_mask
      +
      +

      Getter for gpio_mask.

      +
      + +Expand source code + +
      def getter(self):
      +  # TODO(protobuf-team): This may be broken since there may not be
      +  # default_value.  Combine with has_default_value somehow.
      +  return self._fields.get(field, default_value)
      +
      +
      +
      var gpio_value
      +
      +

      Getter for gpio_value.

      +
      + +Expand source code + +
      def getter(self):
      +  # TODO(protobuf-team): This may be broken since there may not be
      +  # default_value.  Combine with has_default_value somehow.
      +  return self._fields.get(field, default_value)
      +
      +
      +
      var typ
      +
      +

      Getter for typ.

      +
      + +Expand source code + +
      def getter(self):
      +  # TODO(protobuf-team): This may be broken since there may not be
      +  # default_value.  Combine with has_default_value somehow.
      +  return self._fields.get(field, default_value)
      +
      +
      +
      +

      Methods

      +
      +
      +def ByteSize(self) +
      +
      +
      +
      + +Expand source code + +
      def ByteSize(self):
      +  if not self._cached_byte_size_dirty:
      +    return self._cached_byte_size
      +
      +  size = 0
      +  descriptor = self.DESCRIPTOR
      +  if descriptor.GetOptions().map_entry:
      +    # Fields of map entry should always be serialized.
      +    size = descriptor.fields_by_name['key']._sizer(self.key)
      +    size += descriptor.fields_by_name['value']._sizer(self.value)
      +  else:
      +    for field_descriptor, field_value in self.ListFields():
      +      size += field_descriptor._sizer(field_value)
      +    for tag_bytes, value_bytes in self._unknown_fields:
      +      size += len(tag_bytes) + len(value_bytes)
      +
      +  self._cached_byte_size = size
      +  self._cached_byte_size_dirty = False
      +  self._listener_for_children.dirty = False
      +  return size
      +
      +
      +
      +def Clear(self) +
      +
      +
      +
      + +Expand source code + +
      def _Clear(self):
      +  # Clear fields.
      +  self._fields = {}
      +  self._unknown_fields = ()
      +  # pylint: disable=protected-access
      +  if self._unknown_field_set is not None:
      +    self._unknown_field_set._clear()
      +    self._unknown_field_set = None
      +
      +  self._oneofs = {}
      +  self._Modified()
      +
      +
      +
      +def ClearField(self, field_name) +
      +
      +
      +
      + +Expand source code + +
      def ClearField(self, field_name):
      +  try:
      +    field = message_descriptor.fields_by_name[field_name]
      +  except KeyError:
      +    try:
      +      field = message_descriptor.oneofs_by_name[field_name]
      +      if field in self._oneofs:
      +        field = self._oneofs[field]
      +      else:
      +        return
      +    except KeyError:
      +      raise ValueError('Protocol message %s has no "%s" field.' %
      +                       (message_descriptor.name, field_name))
      +
      +  if field in self._fields:
      +    # To match the C++ implementation, we need to invalidate iterators
      +    # for map fields when ClearField() happens.
      +    if hasattr(self._fields[field], 'InvalidateIterators'):
      +      self._fields[field].InvalidateIterators()
      +
      +    # Note:  If the field is a sub-message, its listener will still point
      +    #   at us.  That's fine, because the worst than can happen is that it
      +    #   will call _Modified() and invalidate our byte size.  Big deal.
      +    del self._fields[field]
      +
      +    if self._oneofs.get(field.containing_oneof, None) is field:
      +      del self._oneofs[field.containing_oneof]
      +
      +  # Always call _Modified() -- even if nothing was changed, this is
      +  # a mutating method, and thus calling it should cause the field to become
      +  # present in the parent message.
      +  self._Modified()
      +
      +
      +
      +def DiscardUnknownFields(self) +
      +
      +
      +
      + +Expand source code + +
      def _DiscardUnknownFields(self):
      +  self._unknown_fields = []
      +  self._unknown_field_set = None      # pylint: disable=protected-access
      +  for field, value in self.ListFields():
      +    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
      +      if _IsMapField(field):
      +        if _IsMessageMapField(field):
      +          for key in value:
      +            value[key].DiscardUnknownFields()
      +      elif field.label == _FieldDescriptor.LABEL_REPEATED:
      +        for sub_message in value:
      +          sub_message.DiscardUnknownFields()
      +      else:
      +        value.DiscardUnknownFields()
      +
      +
      +
      +def FindInitializationErrors(self) +
      +
      +

      Finds required fields which are not initialized.

      +

      Returns

      +

      A list of strings. +Each string is a path to an uninitialized field from +the top-level message, e.g. "foo.bar[5].baz".

      +
      + +Expand source code + +
      def FindInitializationErrors(self):
      +  """Finds required fields which are not initialized.
      +
      +  Returns:
      +    A list of strings.  Each string is a path to an uninitialized field from
      +    the top-level message, e.g. "foo.bar[5].baz".
      +  """
      +
      +  errors = []  # simplify things
      +
      +  for field in required_fields:
      +    if not self.HasField(field.name):
      +      errors.append(field.name)
      +
      +  for field, value in self.ListFields():
      +    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
      +      if field.is_extension:
      +        name = '(%s)' % field.full_name
      +      else:
      +        name = field.name
      +
      +      if _IsMapField(field):
      +        if _IsMessageMapField(field):
      +          for key in value:
      +            element = value[key]
      +            prefix = '%s[%s].' % (name, key)
      +            sub_errors = element.FindInitializationErrors()
      +            errors += [prefix + error for error in sub_errors]
      +        else:
      +          # ScalarMaps can't have any initialization errors.
      +          pass
      +      elif field.label == _FieldDescriptor.LABEL_REPEATED:
      +        for i in range(len(value)):
      +          element = value[i]
      +          prefix = '%s[%d].' % (name, i)
      +          sub_errors = element.FindInitializationErrors()
      +          errors += [prefix + error for error in sub_errors]
      +      else:
      +        prefix = name + '.'
      +        sub_errors = value.FindInitializationErrors()
      +        errors += [prefix + error for error in sub_errors]
      +
      +  return errors
      +
      +
      +
      +def HasField(self, field_name) +
      +
      +
      +
      + +Expand source code + +
      def HasField(self, field_name):
      +  try:
      +    field = hassable_fields[field_name]
      +  except KeyError:
      +    raise ValueError(error_msg % (message_descriptor.full_name, field_name))
      +
      +  if isinstance(field, descriptor_mod.OneofDescriptor):
      +    try:
      +      return HasField(self, self._oneofs[field].name)
      +    except KeyError:
      +      return False
      +  else:
      +    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
      +      value = self._fields.get(field)
      +      return value is not None and value._is_present_in_parent
      +    else:
      +      return field in self._fields
      +
      +
      +
      +def IsInitialized(self, errors=None) +
      +
      +

      Checks if all required fields of a message are set.

      +

      Args

      +
      +
      errors
      +
      A list which, if provided, will be populated with the field +paths of all missing required fields.
      +
      +

      Returns

      +

      True iff the specified message has all required fields set.

      +
      + +Expand source code + +
      def IsInitialized(self, errors=None):
      +  """Checks if all required fields of a message are set.
      +
      +  Args:
      +    errors:  A list which, if provided, will be populated with the field
      +             paths of all missing required fields.
      +
      +  Returns:
      +    True iff the specified message has all required fields set.
      +  """
      +
      +  # Performance is critical so we avoid HasField() and ListFields().
      +
      +  for field in required_fields:
      +    if (field not in self._fields or
      +        (field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and
      +         not self._fields[field]._is_present_in_parent)):
      +      if errors is not None:
      +        errors.extend(self.FindInitializationErrors())
      +      return False
      +
      +  for field, value in list(self._fields.items()):  # dict can change size!
      +    if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
      +      if field.label == _FieldDescriptor.LABEL_REPEATED:
      +        if (field.message_type.has_options and
      +            field.message_type.GetOptions().map_entry):
      +          continue
      +        for element in value:
      +          if not element.IsInitialized():
      +            if errors is not None:
      +              errors.extend(self.FindInitializationErrors())
      +            return False
      +      elif value._is_present_in_parent and not value.IsInitialized():
      +        if errors is not None:
      +          errors.extend(self.FindInitializationErrors())
      +        return False
      +
      +  return True
      +
      +
      +
      +def ListFields(self) +
      +
      +
      +
      + +Expand source code + +
      def ListFields(self):
      +  all_fields = [item for item in self._fields.items() if _IsPresent(item)]
      +  all_fields.sort(key = lambda item: item[0].number)
      +  return all_fields
      +
      +
      +
      +def MergeFrom(self, msg) +
      +
      +
      +
      + +Expand source code + +
      def MergeFrom(self, msg):
      +  if not isinstance(msg, cls):
      +    raise TypeError(
      +        'Parameter to MergeFrom() must be instance of same class: '
      +        'expected %s got %s.' % (cls.__name__, msg.__class__.__name__))
      +
      +  assert msg is not self
      +  self._Modified()
      +
      +  fields = self._fields
      +
      +  for field, value in msg._fields.items():
      +    if field.label == LABEL_REPEATED:
      +      field_value = fields.get(field)
      +      if field_value is None:
      +        # Construct a new object to represent this field.
      +        field_value = field._default_constructor(self)
      +        fields[field] = field_value
      +      field_value.MergeFrom(value)
      +    elif field.cpp_type == CPPTYPE_MESSAGE:
      +      if value._is_present_in_parent:
      +        field_value = fields.get(field)
      +        if field_value is None:
      +          # Construct a new object to represent this field.
      +          field_value = field._default_constructor(self)
      +          fields[field] = field_value
      +        field_value.MergeFrom(value)
      +    else:
      +      self._fields[field] = value
      +      if field.containing_oneof:
      +        self._UpdateOneofState(field)
      +
      +  if msg._unknown_fields:
      +    if not self._unknown_fields:
      +      self._unknown_fields = []
      +    self._unknown_fields.extend(msg._unknown_fields)
      +    # pylint: disable=protected-access
      +    if self._unknown_field_set is None:
      +      self._unknown_field_set = containers.UnknownFieldSet()
      +    self._unknown_field_set._extend(msg._unknown_field_set)
      +
      +
      +
      +def MergeFromString(self, serialized) +
      +
      +
      +
      + +Expand source code + +
      def MergeFromString(self, serialized):
      +  if isinstance(serialized, memoryview) and six.PY2:
      +    raise TypeError(
      +        'memoryview not supported in Python 2 with the pure Python proto '
      +        'implementation: this is to maintain compatibility with the C++ '
      +        'implementation')
      +
      +  serialized = memoryview(serialized)
      +  length = len(serialized)
      +  try:
      +    if self._InternalParse(serialized, 0, length) != length:
      +      # The only reason _InternalParse would return early is if it
      +      # encountered an end-group tag.
      +      raise message_mod.DecodeError('Unexpected end-group tag.')
      +  except (IndexError, TypeError):
      +    # Now ord(buf[p:p+1]) == ord('') gets TypeError.
      +    raise message_mod.DecodeError('Truncated message.')
      +  except struct.error as e:
      +    raise message_mod.DecodeError(e)
      +  return length   # Return this for legacy reasons.
      +
      +
      +
      +def SerializePartialToString(self, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def SerializePartialToString(self, **kwargs):
      +  out = BytesIO()
      +  self._InternalSerialize(out.write, **kwargs)
      +  return out.getvalue()
      +
      +
      +
      +def SerializeToString(self, **kwargs) +
      +
      +
      +
      + +Expand source code + +
      def SerializeToString(self, **kwargs):
      +  # Check if the message has all of its required fields set.
      +  if not self.IsInitialized():
      +    raise message_mod.EncodeError(
      +        'Message %s is missing required fields: %s' % (
      +        self.DESCRIPTOR.full_name, ','.join(self.FindInitializationErrors())))
      +  return self.SerializePartialToString(**kwargs)
      +
      +
      +
      +def SetInParent(self) +
      +
      +

      Sets the _cached_byte_size_dirty bit to true, +and propagates this to our listener iff this was a state change.

      +
      + +Expand source code + +
      def Modified(self):
      +  """Sets the _cached_byte_size_dirty bit to true,
      +  and propagates this to our listener iff this was a state change.
      +  """
      +
      +  # Note:  Some callers check _cached_byte_size_dirty before calling
      +  #   _Modified() as an extra optimization.  So, if this method is ever
      +  #   changed such that it does stuff even when _cached_byte_size_dirty is
      +  #   already true, the callers need to be updated.
      +  if not self._cached_byte_size_dirty:
      +    self._cached_byte_size_dirty = True
      +    self._listener_for_children.dirty = True
      +    self._is_present_in_parent = True
      +    self._listener.Modified()
      +
      +
      +
      +def UnknownFields(self) +
      +
      +
      +
      + +Expand source code + +
      def _UnknownFields(self):
      +  if self._unknown_field_set is None:  # pylint: disable=protected-access
      +    # pylint: disable=protected-access
      +    self._unknown_field_set = containers.UnknownFieldSet()
      +  return self._unknown_field_set    # pylint: disable=protected-access
      +
      +
      +
      +def WhichOneof(self, oneof_name) +
      +
      +

      Returns the name of the currently set field inside a oneof, or None.

      +
      + +Expand source code + +
      def WhichOneof(self, oneof_name):
      +  """Returns the name of the currently set field inside a oneof, or None."""
      +  try:
      +    field = message_descriptor.oneofs_by_name[oneof_name]
      +  except KeyError:
      +    raise ValueError(
      +        'Protocol message has no oneof "%s" field.' % oneof_name)
      +
      +  nested_field = self._oneofs.get(field, None)
      +  if nested_field is not None and self.HasField(nested_field.name):
      +    return nested_field.name
      +  else:
      +    return None
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + \ No newline at end of file diff --git a/setup.py b/setup.py index 9fa0407..6555d95 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.1.7", + version="1.1.20", description="Python API & client shell for talking to Meshtastic devices", long_description=long_description, long_description_content_type="text/markdown",