From cca38a04ee753bf797eeda7b7e580c0954bf32a7 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 25 Sep 2020 16:50:24 -0700 Subject: [PATCH] 1.0.13 --- docs/meshtastic/index.html | 418 ++++++++++++++++++++++++++-------- docs/meshtastic/mesh_pb2.html | 60 +++-- docs/meshtastic/test.html | 6 +- meshtastic/__init__.py | 4 +- setup.py | 2 +- 5 files changed, 372 insertions(+), 118 deletions(-) diff --git a/docs/meshtastic/index.html b/docs/meshtastic/index.html index 4b22122..88ff96f 100644 --- a/docs/meshtastic/index.html +++ b/docs/meshtastic/index.html @@ -74,7 +74,7 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect pub.subscribe(onReceive, "meshtastic.receive") pub.subscribe(onConnection, "meshtastic.connection.established") # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0 -interface = meshtastic.StreamInterface() +interface = meshtastic.SerialInterface()
@@ -130,12 +130,13 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect pub.subscribe(onReceive, "meshtastic.receive") pub.subscribe(onConnection, "meshtastic.connection.established") # By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0 -interface = meshtastic.StreamInterface() +interface = meshtastic.SerialInterface() ``` """ +import socket import pygatt import google.protobuf.json_format import serial @@ -358,10 +359,12 @@ class MeshInterface: elif fromRadio.HasField("packet"): self._handlePacketFromRadio(fromRadio.packet) elif fromRadio.rebooted: - self._disconnected() + # Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port + MeshInterface._disconnected(self) + self._startConfig() # redownload the node db etc... else: - logging.warn("Unexpected FromRadio payload") + logging.debug("Unexpected FromRadio payload") def _fixupPosition(self, position): """Convert integer lat/lon into floats @@ -500,9 +503,8 @@ class BLEInterface(MeshInterface): class StreamInterface(MeshInterface): """Interface class for meshtastic devices over a stream link (serial, TCP, etc)""" - def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True): - """Constructor, opens a connection to a specified serial port, or if unspecified try to - find one Meshtastic device by probing + def __init__(self, debugOut=None, noProto=False, connectNow=True): + """Constructor, opens a connection to self.stream Keyword Arguments: devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) @@ -513,35 +515,12 @@ class StreamInterface(MeshInterface): Exception: [description] """ - if devPath is None: - ports = util.findPorts() - if len(ports) == 0: - raise Exception("No Meshtastic devices detected") - elif len(ports) > 1: - raise Exception( - f"Multiple ports detected, you must specify a device, such as {ports[0].device}") - else: - devPath = ports[0] - - logging.debug(f"Connecting to {devPath}") - self.devPath = devPath + if not hasattr(self, 'stream'): + raise Exception( + "StreamInterface is now abstract (to update existing code create SerialInterface instead)") self._rxBuf = bytes() # empty self._wantExit = False - # Note: we provide None for port here, because we will be opening it later - self.stream = serial.Serial( - None, 921600, exclusive=True, timeout=0.5) - - # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset - self.stream.port = devPath - # OS-X seems to have a bug in its serial driver. It ignores that we asked for no RTSCTS - # control and will always drive RTS either high or low (rather than letting the CP102 leave - # it as an open-collector floating pin). Since it is going to drive it anyways we want to make - # sure it is driven low, so that the TBEAM won't reset - if platform.system() == 'Darwin': - self.stream.rts = False - self.stream.open() - self._rxThread = threading.Thread(target=self.__reader, args=()) MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto) @@ -558,21 +537,36 @@ class StreamInterface(MeshInterface): """ # Send some bogus UART characters to force a sleeping device to wake - self.stream.write(bytes([START1, START1, START1, START1])) - self.stream.flush() + self._writeBytes(bytes([START1, START1, START1, START1])) time.sleep(0.1) # wait 100ms to give device time to start running self._rxThread.start() + def _disconnected(self): + """We override the superclass implementation to close our port""" + MeshInterface._disconnected(self) + + logging.debug("Closing our port") + if not self.stream is None: + self.stream.close() + + def _writeBytes(self, b): + """Write an array of bytes to our stream and flush""" + self.stream.write(b) + self.stream.flush() + + def _readBytes(self, len): + """Read an array of bytes from our stream""" + return self.stream.read(len) + def _sendToRadio(self, toRadio): """Send a ToRadio protobuf to the device""" logging.debug(f"Sending: {toRadio}") b = toRadio.SerializeToString() bufLen = len(b) + # We convert into a string, because the TCP code doesn't work with byte arrays header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff]) - self.stream.write(header) - self.stream.write(b) - self.stream.flush() + self._writeBytes(header + b) def close(self): """Close a connection to the device""" @@ -588,7 +582,7 @@ class StreamInterface(MeshInterface): try: while not self._wantExit: - b = self.stream.read(1) + b = self._readBytes(1) if len(b) > 0: # logging.debug(f"read returned {b}") c = b[0] @@ -626,17 +620,104 @@ class StreamInterface(MeshInterface): traceback.print_exc() self._rxBuf = empty else: - # logging.debug(f"timeout on {self.devPath}") + # logging.debug(f"timeout") pass except serial.SerialException as ex: logging.warn( f"Meshtastic serial port disconnected, disconnecting... {ex}") finally: logging.debug("reader is exiting") - if platform.system() == 'Darwin': - self.stream.rts = True # Return RTS high, so that the reset button still works - self.stream.close() - self._disconnected() + self._disconnected() + + +class SerialInterface(StreamInterface): + """Interface class for meshtastic devices over a serial link""" + + def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True): + """Constructor, opens a connection to a specified serial port, or if unspecified try to + find one Meshtastic device by probing + + Keyword Arguments: + devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) + debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None}) + """ + + if devPath is None: + ports = util.findPorts() + if len(ports) == 0: + raise Exception("No Meshtastic devices detected") + elif len(ports) > 1: + raise Exception( + f"Multiple ports detected, you must specify a device, such as {ports[0].device}") + else: + devPath = ports[0] + + logging.debug(f"Connecting to {devPath}") + + # Note: we provide None for port here, because we will be opening it later + self.stream = serial.Serial( + None, 921600, exclusive=True, timeout=0.5) + + # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset + self.stream.port = devPath + # OS-X seems to have a bug in its serial driver. It ignores that we asked for no RTSCTS + # control and will always drive RTS either high or low (rather than letting the CP102 leave + # it as an open-collector floating pin). Since it is going to drive it anyways we want to make + # sure it is driven low, so that the TBEAM won't reset + if platform.system() == 'Darwin': + self.stream.rts = False + self.stream.open() + + StreamInterface.__init__( + self, debugOut=debugOut, noProto=noProto, connectNow=connectNow) + + def _disconnected(self): + """We override the superclass implementation to close our port""" + + if platform.system() == 'Darwin': + self.stream.rts = True # Return RTS high, so that the reset button still works + + StreamInterface._disconnected(self) + + +class TCPInterface(StreamInterface): + """Interface class for meshtastic devices over a TCP link""" + + def __init__(self, hostname, debugOut=None, noProto=False, connectNow=True, portNumber=4403): + """Constructor, opens a connection to a specified IP address/hostname + + Keyword Arguments: + hostname {string} -- Hostname/IP address of the device to connect to + """ + + logging.debug(f"Connecting to {hostname}") + + server_address = (hostname, portNumber) + sock = socket.create_connection(server_address) + + # Instead of wrapping as a stream, we use the native socket API + # self.stream = sock.makefile('rw') + self.stream = None + self.socket = sock + + StreamInterface.__init__( + self, debugOut=debugOut, noProto=noProto, connectNow=connectNow) + + def _disconnected(self): + """We override the superclass implementation to close our port""" + StreamInterface._disconnected(self) + + logging.debug("Closing our socket") + if not self.socket is None: + self.socket.close() + + def _writeBytes(self, b): + """Write an array of bytes to our stream and flush""" + self.socket.send(b) + + def _readBytes(self, len): + """Read an array of bytes from our stream""" + return self.socket.recv(len)
@@ -961,10 +1042,12 @@ debugOut

elif fromRadio.HasField("packet"): self._handlePacketFromRadio(fromRadio.packet) elif fromRadio.rebooted: - self._disconnected() + # Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port + MeshInterface._disconnected(self) + self._startConfig() # redownload the node db etc... else: - logging.warn("Unexpected FromRadio payload") + logging.debug("Unexpected FromRadio payload") def _fixupPosition(self, position): """Convert integer lat/lon into floats @@ -1244,16 +1327,100 @@ wantAck – True if you want the message sent in a reliable manner (with ret -
-class StreamInterface +
+class SerialInterface (devPath=None, debugOut=None, noProto=False, connectNow=True)
-

Interface class for meshtastic devices over a stream link (serial, TCP, etc)

+

Interface class for meshtastic devices over a serial link

Constructor, opens a connection to a specified serial port, or if unspecified try to find one Meshtastic device by probing

Keyword Arguments: devPath {string} – A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) +debugOut {stream} – If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})

+
+ +Expand source code + +
class SerialInterface(StreamInterface):
+    """Interface class for meshtastic devices over a serial link"""
+
+    def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
+        """Constructor, opens a connection to a specified serial port, or if unspecified try to
+        find one Meshtastic device by probing
+
+        Keyword Arguments:
+            devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
+            debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
+        """
+
+        if devPath is None:
+            ports = util.findPorts()
+            if len(ports) == 0:
+                raise Exception("No Meshtastic devices detected")
+            elif len(ports) > 1:
+                raise Exception(
+                    f"Multiple ports detected, you must specify a device, such as {ports[0].device}")
+            else:
+                devPath = ports[0]
+
+        logging.debug(f"Connecting to {devPath}")
+
+        # Note: we provide None for port here, because we will be opening it later
+        self.stream = serial.Serial(
+            None, 921600, exclusive=True, timeout=0.5)
+
+        # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
+        self.stream.port = devPath
+        # OS-X seems to have a bug in its serial driver.  It ignores that we asked for no RTSCTS
+        # control and will always drive RTS either high or low (rather than letting the CP102 leave
+        # it as an open-collector floating pin).  Since it is going to drive it anyways we want to make
+        # sure it is driven low, so that the TBEAM won't reset
+        if platform.system() == 'Darwin':
+            self.stream.rts = False
+        self.stream.open()
+
+        StreamInterface.__init__(
+            self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
+
+    def _disconnected(self):
+        """We override the superclass implementation to close our port"""
+
+        if platform.system() == 'Darwin':
+            self.stream.rts = True  # Return RTS high, so that the reset button still works
+
+        StreamInterface._disconnected(self)
+
+

Ancestors

+ +

Inherited members

+ +
+
+class StreamInterface +(debugOut=None, noProto=False, connectNow=True) +
+
+

Interface class for meshtastic devices over a stream link (serial, TCP, etc)

+

Constructor, opens a connection to self.stream

+

Keyword Arguments: +devPath {string} – A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) debugOut {stream} – If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})

Raises

@@ -1269,9 +1436,8 @@ debugOut {stream} – If a stream is provided, any debug serial output from
class StreamInterface(MeshInterface):
     """Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
 
-    def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
-        """Constructor, opens a connection to a specified serial port, or if unspecified try to
-        find one Meshtastic device by probing
+    def __init__(self, debugOut=None, noProto=False, connectNow=True):
+        """Constructor, opens a connection to self.stream 
 
         Keyword Arguments:
             devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
@@ -1282,35 +1448,12 @@ debugOut {stream} – If a stream is provided, any debug serial output from
             Exception: [description]
         """
 
-        if devPath is None:
-            ports = util.findPorts()
-            if len(ports) == 0:
-                raise Exception("No Meshtastic devices detected")
-            elif len(ports) > 1:
-                raise Exception(
-                    f"Multiple ports detected, you must specify a device, such as {ports[0].device}")
-            else:
-                devPath = ports[0]
-
-        logging.debug(f"Connecting to {devPath}")
-        self.devPath = devPath
+        if not hasattr(self, 'stream'):
+            raise Exception(
+                "StreamInterface is now abstract (to update existing code create SerialInterface instead)")
         self._rxBuf = bytes()  # empty
         self._wantExit = False
 
-        # Note: we provide None for port here, because we will be opening it later
-        self.stream = serial.Serial(
-            None, 921600, exclusive=True, timeout=0.5)
-
-        # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
-        self.stream.port = devPath
-        # OS-X seems to have a bug in its serial driver.  It ignores that we asked for no RTSCTS
-        # control and will always drive RTS either high or low (rather than letting the CP102 leave
-        # it as an open-collector floating pin).  Since it is going to drive it anyways we want to make
-        # sure it is driven low, so that the TBEAM won't reset
-        if platform.system() == 'Darwin':
-            self.stream.rts = False
-        self.stream.open()
-
         self._rxThread = threading.Thread(target=self.__reader, args=())
 
         MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
@@ -1327,21 +1470,36 @@ debugOut {stream} – If a stream is provided, any debug serial output from
         """
 
         # Send some bogus UART characters to force a sleeping device to wake
-        self.stream.write(bytes([START1, START1, START1, START1]))
-        self.stream.flush()
+        self._writeBytes(bytes([START1, START1, START1, START1]))
         time.sleep(0.1)  # wait 100ms to give device time to start running
 
         self._rxThread.start()
 
+    def _disconnected(self):
+        """We override the superclass implementation to close our port"""
+        MeshInterface._disconnected(self)
+
+        logging.debug("Closing our port")
+        if not self.stream is None:
+            self.stream.close()
+
+    def _writeBytes(self, b):
+        """Write an array of bytes to our stream and flush"""
+        self.stream.write(b)
+        self.stream.flush()
+
+    def _readBytes(self, len):
+        """Read an array of bytes from our stream"""
+        return self.stream.read(len)
+
     def _sendToRadio(self, toRadio):
         """Send a ToRadio protobuf to the device"""
         logging.debug(f"Sending: {toRadio}")
         b = toRadio.SerializeToString()
         bufLen = len(b)
+        # We convert into a string, because the TCP code doesn't work with byte arrays
         header = bytes([START1, START2, (bufLen >> 8) & 0xff,  bufLen & 0xff])
-        self.stream.write(header)
-        self.stream.write(b)
-        self.stream.flush()
+        self._writeBytes(header + b)
 
     def close(self):
         """Close a connection to the device"""
@@ -1357,7 +1515,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from
 
         try:
             while not self._wantExit:
-                b = self.stream.read(1)
+                b = self._readBytes(1)
                 if len(b) > 0:
                     # logging.debug(f"read returned {b}")
                     c = b[0]
@@ -1395,22 +1553,24 @@ debugOut {stream} – If a stream is provided, any debug serial output from
                                 traceback.print_exc()
                             self._rxBuf = empty
                 else:
-                    # logging.debug(f"timeout on {self.devPath}")
+                    # logging.debug(f"timeout")
                     pass
         except serial.SerialException as ex:
             logging.warn(
                 f"Meshtastic serial port disconnected, disconnecting... {ex}")
         finally:
             logging.debug("reader is exiting")
-            if platform.system() == 'Darwin':
-                self.stream.rts = True  # Return RTS high, so that the reset button still works
-            self.stream.close()
             self._disconnected()

Ancestors

+

Subclasses

+

Methods

@@ -1450,8 +1610,7 @@ start the reading thread later.

""" # Send some bogus UART characters to force a sleeping device to wake - self.stream.write(bytes([START1, START1, START1, START1])) - self.stream.flush() + self._writeBytes(bytes([START1, START1, START1, START1])) time.sleep(0.1) # wait 100ms to give device time to start running self._rxThread.start() @@ -1472,6 +1631,79 @@ start the reading thread later.

+
+class TCPInterface +(hostname, debugOut=None, noProto=False, connectNow=True, portNumber=4403) +
+
+

Interface class for meshtastic devices over a TCP link

+

Constructor, opens a connection to a specified IP address/hostname

+

Keyword Arguments: +hostname {string} – Hostname/IP address of the device to connect to

+
+ +Expand source code + +
class TCPInterface(StreamInterface):
+    """Interface class for meshtastic devices over a TCP link"""
+
+    def __init__(self, hostname, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
+        """Constructor, opens a connection to a specified IP address/hostname
+
+        Keyword Arguments:
+            hostname {string} -- Hostname/IP address of the device to connect to
+        """
+
+        logging.debug(f"Connecting to {hostname}")
+
+        server_address = (hostname, portNumber)
+        sock = socket.create_connection(server_address)
+
+        # Instead of wrapping as a stream, we use the native socket API
+        # self.stream = sock.makefile('rw')
+        self.stream = None
+        self.socket = sock
+
+        StreamInterface.__init__(
+            self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
+
+    def _disconnected(self):
+        """We override the superclass implementation to close our port"""
+        StreamInterface._disconnected(self)
+
+        logging.debug("Closing our socket")
+        if not self.socket is None:
+            self.socket.close()
+
+    def _writeBytes(self, b):
+        """Write an array of bytes to our stream and flush"""
+        self.socket.send(b)
+
+    def _readBytes(self, len):
+        """Read an array of bytes from our stream"""
+        return self.socket.recv(len)
+
+

Ancestors

+ +

Inherited members

+ +
@@ -1518,12 +1750,18 @@ start the reading thread later.

  • +

    SerialInterface

    +
  • +
  • StreamInterface

  • +
  • +

    TCPInterface

    +
  • diff --git a/docs/meshtastic/mesh_pb2.html b/docs/meshtastic/mesh_pb2.html index 5f990e9..dec38f9 100644 --- a/docs/meshtastic/mesh_pb2.html +++ b/docs/meshtastic/mesh_pb2.html @@ -46,7 +46,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( package='', syntax='proto3', serialized_options=_b('\n\023com.geeksville.meshB\nMeshProtos'), - 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\"\xfc\x03\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\x8d\x03\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\x17\n\x0fignore_incoming\x18\x66 \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\x42!\n\x13\x63om.geeksville.meshB\nMeshProtosb\x06proto3') + 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\"\xa3\x04\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\xb4\x03\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\x0e\n\x06region\x18\x0f \x01(\t\x12\x15\n\rfactory_reset\x18\x64 \x01(\x08\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\x42!\n\x13\x63om.geeksville.meshB\nMeshProtosb\x06proto3') ) _ROUTEERROR = _descriptor.EnumDescriptor( @@ -74,8 +74,8 @@ _ROUTEERROR = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=2895, - serialized_end=2957, + serialized_start=2934, + serialized_end=2996, ) _sym_db.RegisterEnumDescriptor(_ROUTEERROR) @@ -93,8 +93,8 @@ _CONSTANTS = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=2959, - serialized_end=2982, + serialized_start=2998, + serialized_end=3021, ) _sym_db.RegisterEnumDescriptor(_CONSTANTS) @@ -735,8 +735,22 @@ _RADIOCONFIG_USERPREFERENCES = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( - name='ignore_incoming', full_name='RadioConfig.UserPreferences.ignore_incoming', index=14, - number=102, type=13, cpp_type=3, label=3, + name='region', full_name='RadioConfig.UserPreferences.region', index=14, + number=15, 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), + _descriptor.FieldDescriptor( + name='factory_reset', full_name='RadioConfig.UserPreferences.factory_reset', index=15, + 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), + _descriptor.FieldDescriptor( + name='ignore_incoming', full_name='RadioConfig.UserPreferences.ignore_incoming', index=16, + number=103, type=13, cpp_type=3, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, @@ -754,7 +768,7 @@ _RADIOCONFIG_USERPREFERENCES = _descriptor.Descriptor( oneofs=[ ], serialized_start=1275, - serialized_end=1672, + serialized_end=1711, ) _RADIOCONFIG = _descriptor.Descriptor( @@ -791,7 +805,7 @@ _RADIOCONFIG = _descriptor.Descriptor( oneofs=[ ], serialized_start=1164, - serialized_end=1672, + serialized_end=1711, ) @@ -849,8 +863,8 @@ _NODEINFO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1674, - serialized_end=1778, + serialized_start=1713, + serialized_end=1817, ) @@ -971,8 +985,8 @@ _MYNODEINFO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1781, - serialized_end=2106, + serialized_start=1820, + serialized_end=2145, ) @@ -1058,8 +1072,8 @@ _DEVICESTATE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2109, - serialized_end=2362, + serialized_start=2148, + serialized_end=2401, ) @@ -1089,8 +1103,8 @@ _DEBUGSTRING = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2364, - serialized_end=2394, + serialized_start=2403, + serialized_end=2433, ) @@ -1172,8 +1186,8 @@ _FROMRADIO = _descriptor.Descriptor( name='variant', full_name='FromRadio.variant', index=0, containing_type=None, fields=[]), ], - serialized_start=2397, - serialized_end=2646, + serialized_start=2436, + serialized_end=2685, ) @@ -1227,8 +1241,8 @@ _TORADIO = _descriptor.Descriptor( name='variant', full_name='ToRadio.variant', index=0, containing_type=None, fields=[]), ], - serialized_start=2649, - serialized_end=2789, + serialized_start=2688, + serialized_end=2828, ) @@ -1279,8 +1293,8 @@ _MANUFACTURINGDATA = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2791, - serialized_end=2893, + serialized_start=2830, + serialized_end=2932, ) _DATA.fields_by_name['typ'].enum_type = _DATA_TYPE diff --git a/docs/meshtastic/test.html b/docs/meshtastic/test.html index b1cd793..e921b65 100644 --- a/docs/meshtastic/test.html +++ b/docs/meshtastic/test.html @@ -26,7 +26,7 @@
    import logging
     from . import util
    -from . import StreamInterface, BROADCAST_NUM
    +from . import SerialInterface, BROADCAST_NUM
     from pubsub import pub
     import time
     import sys
    @@ -154,7 +154,7 @@ def testAll():
         pub.subscribe(onConnection, "meshtastic.connection")
         pub.subscribe(onReceive, "meshtastic.receive")
         global interfaces
    -    interfaces = list(map(lambda port: StreamInterface(
    +    interfaces = list(map(lambda port: SerialInterface(
             port, debugOut=openDebugLog(port), connectNow=False), ports))
         for i in interfaces:
             i.connect()
    @@ -288,7 +288,7 @@ def testAll():
         pub.subscribe(onConnection, "meshtastic.connection")
         pub.subscribe(onReceive, "meshtastic.receive")
         global interfaces
    -    interfaces = list(map(lambda port: StreamInterface(
    +    interfaces = list(map(lambda port: SerialInterface(
             port, debugOut=openDebugLog(port), connectNow=False), ports))
         for i in interfaces:
             i.connect()
    diff --git a/meshtastic/__init__.py b/meshtastic/__init__.py
    index 437b76a..43aa496 100644
    --- a/meshtastic/__init__.py
    +++ b/meshtastic/__init__.py
    @@ -276,7 +276,9 @@ class MeshInterface:
             elif fromRadio.HasField("packet"):
                 self._handlePacketFromRadio(fromRadio.packet)
             elif fromRadio.rebooted:
    -            self._disconnected()
    +            # Tell clients the device went away.  Careful not to call the overridden subclass version that closes the serial port
    +            MeshInterface._disconnected(self)
    +
                 self._startConfig()  # redownload the node db etc...
             else:
                 logging.debug("Unexpected FromRadio payload")
    diff --git a/setup.py b/setup.py
    index ddf920e..54e889d 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.0.11",
    +    version="1.0.13",
         description="Python API & client shell for talking to Meshtastic devices",
         long_description=long_description,
         long_description_content_type="text/markdown",