diff --git a/meshtastic/__init__.py b/meshtastic/__init__.py index 629351d..112a88d 100644 --- a/meshtastic/__init__.py +++ b/meshtastic/__init__.py @@ -91,9 +91,10 @@ BROADCAST_NUM = 0xffffffff BROADCAST_ADDR = "^all" -"""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 + format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20 """ OUR_APP_VERSION = 20200 diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 5cd92fe..08a96bb 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -18,7 +18,7 @@ from . import portnums_pb2, channel_pb2, radioconfig_pb2 from .globals import Globals -"""We only import the tunnel code if we are on a platform that can run it""" +"""We only import the tunnel code if we are on a platform that can run it. """ have_tunnel = platform.system() == 'Linux' def onReceive(packet, interface): @@ -375,7 +375,9 @@ def onConnected(interface): print(f"Deleting channel {channelIndex}") ch = getNode().deleteChannel(channelIndex) - ch_changes = [args.ch_longslow, args.ch_longfast, args.ch_mediumslow, args.ch_mediumfast, args.ch_shortslow, args.ch_shortfast] + ch_changes = [args.ch_longslow, args.ch_longfast, + args.ch_mediumslow, args.ch_mediumfast, + args.ch_shortslow, args.ch_shortfast] any_primary_channel_changes = any(x for x in ch_changes) if args.ch_set or any_primary_channel_changes or args.ch_enable or args.ch_disable: closeNow = True @@ -593,7 +595,7 @@ def common(): def initParser(): - """ Initialize the command line argument parsing.""" + """Initialize the command line argument parsing.""" our_globals = Globals.getInstance() parser = our_globals.get_parser() args = our_globals.get_args() diff --git a/meshtastic/ble_interface.py b/meshtastic/ble_interface.py index 57404f1..8248d24 100644 --- a/meshtastic/ble_interface.py +++ b/meshtastic/ble_interface.py @@ -1,4 +1,4 @@ -""" Bluetooth interface +"""Bluetooth interface """ import logging import pygatt diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index 8f245cf..b5a3954 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -39,7 +39,8 @@ class MeshInterface: """Constructor Keyword Arguments: - noProto -- If True, don't try to run our protocol on the link - just be a dumb serial client. + 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 @@ -92,12 +93,15 @@ class MeshInterface: def showNodes(self, includeSelf=True, file=sys.stdout): """Show table summary of nodes in mesh""" def formatFloat(value, precision=2, unit=''): + """Format a float value with precsion.""" return f'{value:.{precision}f}{unit}' if value else None def getLH(ts): + """Format last heard""" return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None def getTimeAgo(ts): + """Format how long ago have we heard from this node (aka timeago).""" return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None rows = [] @@ -137,8 +141,7 @@ class MeshInterface: for i, row in enumerate(rows): row['N'] = i+1 - table = tabulate(rows, headers='keys', missingval='N/A', - tablefmt='fancy_grid') + table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid') print(table) return table @@ -161,18 +164,24 @@ class MeshInterface: hopLimit=defaultHopLimit, onResponse=None, channelIndex=0): - """Send a utf8 string to some other node, if the node has a display it will also be shown on the device. + """Send a utf8 string to some other node, if the node has a display it + will also be shown on the device. Arguments: 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) - wantResponse -- True if you want the service on the other side to send an application layer response + 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) + wantResponse -- True if you want the service on the other side to + send an application layer response - Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. + 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, portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, @@ -191,15 +200,23 @@ class MeshInterface: """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) - wantResponse -- True if you want the service on the other side to send an application layer response - onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives - (or the transaction is NAKed due to non receipt) + 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) + wantResponse -- True if you want the service on the other + side to send an application layer response + onResponse -- A closure of the form funct(packet), that will be + called when a response packet arrives (or the transaction + is NAKed due to non receipt) - Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. + 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: {stripnl(data)}") @@ -223,16 +240,18 @@ class MeshInterface: self._addResponseHandler(p.id, onResponse) return p - def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False): + def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, + destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False): """ Send a position packet to some other node (normally a broadcast) - Also, the device software will notice this packet and use it to automatically set its notion of - the local position. + Also, the device software will notice this packet and use it to automatically + set its notion of the local position. If timeSec is not specified (recommended), we will use the local machine time. - Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. + Returns the sent packet. The id field will be populated in this packet and + can be used to track future message acks/naks. """ p = mesh_pb2.Position() if latitude != 0.0: @@ -293,8 +312,8 @@ class MeshInterface: meshPacket.want_ack = wantAck meshPacket.hop_limit = hopLimit - # if the user hasn't set an ID for this packet (likely and recommended), we should pick a new unique ID - # so the message can be tracked. + # if the user hasn't set an ID for this packet (likely and recommended), + # we should pick a new unique ID so the message can be tracked. if meshPacket.id == 0: meshPacket.id = self._generatePacketId() @@ -305,8 +324,7 @@ class MeshInterface: def waitForConfig(self): """Block until radio config is received. Returns True if config has been received.""" - success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes') - ) and self.localNode.waitForConfig() + success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig() if not success: raise Exception("Timed out waiting for interface config") @@ -408,8 +426,7 @@ class MeshInterface: def _sendToRadio(self, toRadio): """Send a ToRadio protobuf to the device""" if self.noProto: - logging.warning( - f"Not sending packet because protocol use is disabled by noProto") + logging.warning(f"Not sending packet because protocol use is disabled by noProto") else: #logging.debug(f"Sending toRadio: {stripnl(toRadio)}") self._sendToRadioImpl(toRadio) @@ -420,7 +437,8 @@ class MeshInterface: def _handleConfigComplete(self): """ - Done with initial config messages, now send regular MeshPackets to ask for settings and channels + Done with initial config messages, now send regular MeshPackets + to ask for settings and channels """ self.localNode.requestConfig() @@ -441,7 +459,7 @@ class MeshInterface: failmsg = None # Check for app too old if self.myInfo.min_app_version > OUR_APP_VERSION: - failmsg = "This device needs a newer python client, please \"pip install --upgrade meshtastic\". "\ + failmsg = "This device needs a newer python client, run 'pip install --upgrade meshtastic'."\ "For more information see https://tinyurl.com/5bjsxu32" # check for firmware too old @@ -469,13 +487,15 @@ class MeshInterface: publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated", node=node, interface=self)) elif fromRadio.config_complete_id == self.configId: - # we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id + # we ignore the config_complete_id, it is unneeded for our + # stream API fromRadio.config_complete_id logging.debug(f"Config complete ID {self.configId}") self._handleConfigComplete() elif fromRadio.HasField("packet"): self._handlePacketFromRadio(fromRadio.packet) elif fromRadio.rebooted: - # Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port + # 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... @@ -569,8 +589,9 @@ class MeshInterface: # byte array. decoded["payload"] = meshPacket.decoded.payload - # UNKNOWN_APP is the default protobuf portnum value, and therefore if not set it will not be populated at all - # to make API usage easier, set it to prevent confusion + # UNKNOWN_APP is the default protobuf portnum 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 "portnum" in decoded: decoded["portnum"] = portnums_pb2.PortNum.Name( portnums_pb2.PortNum.UNKNOWN_APP) @@ -579,8 +600,9 @@ class MeshInterface: topic = f"meshtastic.receive.data.{portnum}" - # decode position protobufs and update nodedb, provide decoded version as "position" in the published msg - # move the following into a 'decoders' API that clients could register? + # decode position protobufs and update nodedb, provide decoded version + # as "position" in the published msg move the following into a 'decoders' + # API that clients could register? portNumInt = meshPacket.decoded.portnum # we want portnum as an int handler = protocols.get(portNumInt) # The decoded protobuf as a dictionary (if we understand this message) diff --git a/meshtastic/node.py b/meshtastic/node.py index 896c2a2..64b21fe 100644 --- a/meshtastic/node.py +++ b/meshtastic/node.py @@ -1,4 +1,4 @@ -""" Node class +"""Node class """ import logging @@ -24,7 +24,7 @@ class Node: self.partialChannels = None def showChannels(self): - """Show human readable description of our channels""" + """Show human readable description of our channels.""" print("Channels:") if self.channels: for c in self.channels: @@ -47,9 +47,7 @@ class Node: self.showChannels() def requestConfig(self): - """ - Send regular MeshPackets to ask for settings and channels - """ + """Send regular MeshPackets to ask for settings and channels.""" self.radioConfig = None self.channels = None self.partialChannels = [] # We keep our channels in a temp array until finished @@ -104,9 +102,11 @@ class Node: self.writeChannel(index, adminIndex=adminIndex) index += 1 - # if we are updating the local node, we might end up *moving* the admin channel index as we are writing + # if we are updating the local node, we might end up + # *moving* the admin channel index as we are writing if (self.iface.localNode == self) and index >= adminIndex: - # We've now passed the old location for admin index (and writen it), so we can start finding it by name again + # We've now passed the old location for admin index + # (and writen it), so we can start finding it by name again adminIndex = 0 def getChannelByName(self, name): @@ -165,8 +165,7 @@ class Node: return self._sendAdmin(p) def getURL(self, includeAll: bool = True): - """The sharable URL that describes the current channel - """ + """The sharable URL that describes the current channel""" # Only keep the primary/secondary channels, assume primary is first channelSet = apponly_pb2.ChannelSet() if self.channels: @@ -212,9 +211,8 @@ class Node: i = i + 1 def _requestSettings(self): - """ - Done with initial config messages, now send regular MeshPackets to ask for settings - """ + """Done with initial config messages, now send regular + MeshPackets to ask for settings.""" p = admin_pb2.AdminMessage() p.get_radio_request = True @@ -233,26 +231,20 @@ class Node: # Show progress message for super slow operations if self != self.iface.localNode: - logging.info( - "Requesting preferences from remote node (this could take a while)") + print("Requesting preferences from remote node (this could take a while)") - return self._sendAdmin(p, - wantResponse=True, - onResponse=onResponse) + return self._sendAdmin(p, wantResponse=True, onResponse=onResponse) def exitSimulator(self): - """ - Tell a simulator node to exit (this message is ignored for other nodes) - """ + """Tell a simulator node to exit (this message + is ignored for other nodes)""" p = admin_pb2.AdminMessage() p.exit_simulator = True return self._sendAdmin(p) def reboot(self, secs: int = 10): - """ - Tell the node to reboot - """ + """Tell the node to reboot.""" p = admin_pb2.AdminMessage() p.reboot_seconds = secs logging.info(f"Telling node to reboot in {secs} seconds") @@ -263,6 +255,7 @@ class Node: """Fixup indexes and add disabled channels as needed""" # Add extra disabled channels as needed + # TODO: These 2 lines seem to not do anything. for index, ch in enumerate(self.channels): ch.index = index # fixup indexes @@ -281,21 +274,19 @@ class Node: index += 1 def _requestChannel(self, channelNum: int): - """ - Done with initial config messages, now send regular MeshPackets to ask for settings - """ + """Done with initial config messages, now send regular + MeshPackets to ask for settings""" p = admin_pb2.AdminMessage() p.get_channel_request = channelNum + 1 # Show progress message for super slow operations if self != self.iface.localNode: - logging.info( - f"Requesting channel {channelNum} info from remote node (this could take a while)") + logging.info(f"Requesting channel {channelNum} info from remote node (this could take a while)") else: logging.debug(f"Requesting channel {channelNum}") def onResponse(p): - """A closure to handle the response packet""" + """A closure to handle the response packet for requesting a channel""" c = p["decoded"]["admin"]["raw"].get_channel_response self.partialChannels.append(c) self._timeout.reset() # We made foreward progress @@ -305,9 +296,9 @@ class Node: # for stress testing, we can always download all channels fastChannelDownload = True - # Once we see a response that has NO settings, assume we are at the end of channels and stop fetching - quitEarly = ( - c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload + # Once we see a response that has NO settings, assume + # we are at the end of channels and stop fetching + quitEarly = (c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload if quitEarly or index >= self.iface.myInfo.max_channels - 1: logging.debug("Finished downloading channels") @@ -320,13 +311,10 @@ class Node: else: self._requestChannel(index + 1) - return self._sendAdmin(p, - wantResponse=True, - onResponse=onResponse) + return self._sendAdmin(p, wantResponse=True, onResponse=onResponse) def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False, - onResponse=None, - adminIndex=0): + onResponse=None, adminIndex=0): """Send an admin message to the specified node (or the local node if destNodeNum is zero)""" if adminIndex == 0: # unless a special channel index was used, we want to use the admin index diff --git a/meshtastic/stream_interface.py b/meshtastic/stream_interface.py index eaad34c..3d4ee76 100644 --- a/meshtastic/stream_interface.py +++ b/meshtastic/stream_interface.py @@ -1,4 +1,4 @@ -""" Stream Interface base class +"""Stream Interface base class """ import logging import threading @@ -25,7 +25,8 @@ class StreamInterface(MeshInterface): 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}) + debugOut {stream} -- If a stream is provided, any debug serial output from the + device will be emitted to that stream. (default: {None}) Raises: Exception: [description] @@ -53,12 +54,14 @@ class StreamInterface(MeshInterface): def connect(self): """Connect to our radio - Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually - start the reading thread later. + Normally this is called automatically by the constructor, but if you + passed in connectNow=False you can manually start the reading thread later. """ - # Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure - # we write enought start bytes to force it to resync (we don't use START1 because we want to ensure it is looking for START1) + # Send some bogus UART characters to force a sleeping device to wake, and + # if the reading statemachine was parsing a bad packet make sure + # we write enought start bytes to force it to resync (we don't use START1 + # because we want to ensure it is looking for START1) p = bytearray([START2] * 32) self._writeBytes(p) time.sleep(0.1) # wait 100ms to give device time to start running @@ -105,7 +108,8 @@ class StreamInterface(MeshInterface): """Close a connection to the device""" logging.debug("Closing stream") MeshInterface.close(self) - # pyserial cancel_read doesn't seem to work, therefore we ask the reader thread to close things for us + # pyserial cancel_read doesn't seem to work, therefore we ask the + # reader thread to close things for us self._wantExit = True if self._rxThread != threading.current_thread(): self._rxThread.join() # wait for it to exit @@ -151,8 +155,7 @@ class StreamInterface(MeshInterface): try: self._handleFromRadio(self._rxBuf[HEADER_LEN:]) except Exception as ex: - logging.error( - f"Error while handling message from radio {ex}") + logging.error(f"Error while handling message from radio {ex}") traceback.print_exc() self._rxBuf = empty else: @@ -160,15 +163,12 @@ class StreamInterface(MeshInterface): pass except serial.SerialException as ex: if not self._wantExit: # We might intentionally get an exception during shutdown - logging.warning( - f"Meshtastic serial port disconnected, disconnecting... {ex}") + logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}") except OSError as ex: if not self._wantExit: # We might intentionally get an exception during shutdown - logging.error( - f"Unexpected OSError, terminating meshtastic reader... {ex}") + logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}") except Exception as ex: - logging.error( - f"Unexpected exception, terminating meshtastic reader... {ex}") + logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}") finally: logging.debug("reader is exiting") self._disconnected() diff --git a/meshtastic/tcp_interface.py b/meshtastic/tcp_interface.py index 9116999..a892448 100644 --- a/meshtastic/tcp_interface.py +++ b/meshtastic/tcp_interface.py @@ -1,4 +1,4 @@ -""" TCPInterface class for interfacing with http endpoint +"""TCPInterface class for interfacing with http endpoint """ import logging import socket @@ -9,7 +9,8 @@ from .stream_interface import StreamInterface class TCPInterface(StreamInterface): """Interface class for meshtastic devices over a TCP link""" - def __init__(self, hostname: AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403): + def __init__(self, hostname: AnyStr, debugOut=None, noProto=False, + connectNow=True, portNumber=4403): """Constructor, opens a connection to a specified IP address/hostname Keyword Arguments: @@ -33,8 +34,8 @@ class TCPInterface(StreamInterface): """Close a connection to the device""" logging.debug("Closing TCP stream") StreamInterface.close(self) - # Sometimes the socket read might be blocked in the reader thread. Therefore we force the shutdown by closing - # the socket here + # Sometimes the socket read might be blocked in the reader thread. + # Therefore we force the shutdown by closing the socket here self._wantExit = True if not self.socket is None: try: diff --git a/meshtastic/test.py b/meshtastic/test.py index 0c70f73..c35b37c 100644 --- a/meshtastic/test.py +++ b/meshtastic/test.py @@ -1,5 +1,5 @@ -""" With two radios connected serially, send and receive test - messages and report back if successful. +"""With two radios connected serially, send and receive test + messages and report back if successful. """ import logging import time diff --git a/meshtastic/tests/test_node.py b/meshtastic/tests/test_node.py new file mode 100644 index 0000000..3787e76 --- /dev/null +++ b/meshtastic/tests/test_node.py @@ -0,0 +1,34 @@ +"""Meshtastic unit tests for node.py""" + +import re + +from unittest.mock import patch, MagicMock +import pytest + +from ..node import Node +from ..serial_interface import SerialInterface +from ..admin_pb2 import AdminMessage + + +@pytest.mark.unit +def test_node(capsys): + """Test that we can instantiate a Node""" + anode = Node('foo', 'bar') + anode.showChannels() + anode.showInfo() + out, err = capsys.readouterr() + assert re.search(r'Preferences', out) + assert re.search(r'Channels', out) + assert re.search(r'Primary channel URL', out) + assert err == '' + + +@pytest.mark.unit +def test_node_reqquestConfig(): + """Test run requestConfig""" + iface = MagicMock(autospec=SerialInterface) + amesg = MagicMock(autospec=AdminMessage) + with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo: + with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg): + anode = Node(mo, 'bar') + anode.requestConfig() diff --git a/meshtastic/tunnel.py b/meshtastic/tunnel.py index 3dd0fa3..f6b7894 100644 --- a/meshtastic/tunnel.py +++ b/meshtastic/tunnel.py @@ -1,4 +1,4 @@ -""" Code for IP tunnel over a mesh +"""Code for IP tunnel over a mesh # Note python-pytuntap was too buggy # using pip3 install pytap2 @@ -88,8 +88,8 @@ class Tunnel: global tunnelInstance tunnelInstance = self - logging.info( - "Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work). Mesh members:") + logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\ + "feature to work). Mesh members:") pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP") myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num) @@ -117,8 +117,8 @@ class Tunnel: else: logging.debug( f"Received mesh tunnel message type={type(p)} len={len(p)}") - # we don't really need to check for filtering here (sender should have checked), but this provides - # useful debug printing on types of packets received + # we don't really need to check for filtering here (sender should have checked), + # but this provides useful debug printing on types of packets received if not self._shouldFilterPacket(p): self.tun.write(p) @@ -137,8 +137,8 @@ class Tunnel: icmpType = p[20] icmpCode = p[21] checksum = p[22:24] - logging.debug( - f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}") + # pylint: disable=line-too-long + logging.debug(f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}") # reply to pings (swap src and dest but keep rest of packet unchanged) #pingback = p[:12]+p[16:20]+p[12:16]+p[20:] # tap.write(pingback) @@ -157,14 +157,12 @@ class Tunnel: destport = readnet_u16(p, subheader + 2) if destport in tcpBlacklist: ignore = True - logging.log( - LOG_TRACE, f"ignoring blacklisted TCP port {destport}") + logging.log(LOG_TRACE, f"ignoring blacklisted TCP port {destport}") else: - logging.debug( - f"forwarding tcp srcport={srcport}, destport={destport}") + logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}") else: - logging.warning( - f"forwarding unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destAddr)}") + logging.warning(f"forwarding unexpected protocol 0x{protocol:02x}, "\ + "src={ipstr(srcaddr)}, dest={ipstr(destAddr)}") return ignore @@ -200,13 +198,11 @@ class Tunnel: """Forward the provided IP packet into the mesh""" nodeId = self._ipToNodeId(destAddr) if nodeId is not None: - logging.debug( - f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}") + logging.debug(f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}") self.iface.sendData( p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False) else: - logging.warning( - f"Dropping packet because no node found for destIP={ipstr(destAddr)}") + logging.warning(f"Dropping packet because no node found for destIP={ipstr(destAddr)}") def close(self): """Close""" diff --git a/meshtastic/util.py b/meshtastic/util.py index d129384..f9f95d7 100644 --- a/meshtastic/util.py +++ b/meshtastic/util.py @@ -1,4 +1,4 @@ -""" Utility functions. +"""Utility functions. """ import traceback from queue import Queue @@ -39,7 +39,8 @@ def fromPSK(valstr): def fromStr(valstr): - """try to parse as int, float or bool (and fallback to a string as last resort) + """Try to parse as int, float or bool (and fallback to a string as last resort) + Returns: an int, bool, float, str or byte array (for strings of hex digits) Args: @@ -82,13 +83,13 @@ def pskToString(psk: bytes): def stripnl(s): - """remove newlines from a string (and remove extra whitespace)""" + """Remove newlines from a string (and remove extra whitespace)""" s = str(s).replace("\n", " ") return ' '.join(s.split()) def fixme(message): - """raise an exception for things that needs to be fixed""" + """Raise an exception for things that needs to be fixed""" raise Exception(f"FIXME: {message}") @@ -174,7 +175,7 @@ def our_exit(message, return_value = 1): def support_info(): - """Print out info that is helping in support of the cli.""" + """Print out info that helps troubleshooting of the cli.""" print('') print('If having issues with meshtastic cli or python library') print('or wish to make feature requests, visit:')