From 087b7563e7a8ff1a04c3d7271de7ef53e4ed9d67 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 6 Mar 2021 14:27:49 +0800 Subject: [PATCH] 1.2.5 add hopLimit support and fix automatic text decoding --- docs/meshtastic/index.html | 48 ++++++++++++++++++++++++++++---------- docs/meshtastic/test.html | 4 ++-- meshtastic/__init__.py | 17 ++++++++++---- meshtastic/test.py | 2 +- setup.py | 2 +- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/docs/meshtastic/index.html b/docs/meshtastic/index.html index 59188cf..7943728 100644 --- a/docs/meshtastic/index.html +++ b/docs/meshtastic/index.html @@ -164,6 +164,7 @@ START1 = 0x94 START2 = 0xc3 HEADER_LEN = 4 MAX_TO_FROM_RADIO_SIZE = 512 +defaultHopLimit = 3 BROADCAST_ADDR = "^all" # A special ID that means broadcast @@ -238,6 +239,7 @@ class MeshInterface: destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a utf8 string to some other node, if the node has a display it will also be shown on the device. @@ -256,11 +258,13 @@ class MeshInterface: portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse, + hopLimit=hopLimit, onResponse=onResponse) def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a data packet to some other node @@ -280,12 +284,16 @@ class MeshInterface: if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN: raise Exception("Data payload too big") + + if portNum == portnums_pb2.PortNum.UNKNOWN_APP: # we are now more strict wrt port numbers + raise Exception("A non-zero port number must be specified") + meshPacket = mesh_pb2.MeshPacket() meshPacket.decoded.payload = data meshPacket.decoded.portnum = portNum meshPacket.decoded.want_response = wantResponse - p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck) + p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit) if onResponse is not None: self._addResponseHandler(p.id, onResponse) return p @@ -325,7 +333,7 @@ class MeshInterface: def _sendPacket(self, meshPacket, destinationId=BROADCAST_ADDR, - wantAck=False): + wantAck=False, hopLimit=defaultHopLimit): """Send a MeshPacket to the specified node (or if unspecified, broadcast). You probably don't want this - use sendData instead. @@ -350,6 +358,7 @@ class MeshInterface: meshPacket.to = nodeNum 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. @@ -459,7 +468,7 @@ class MeshInterface: channelSet.settings.append(c.settings) bytes = channelSet.SerializeToString() s = base64.urlsafe_b64encode(bytes).decode('ascii') - return f"https://www.meshtastic.org/d/#{s}" + return f"https://www.meshtastic.org/d/#{s}".replace("=", "") def setURL(self, url): """Set mesh network URL""" @@ -1062,8 +1071,8 @@ def _onTextReceive(iface, asDict): # Usually btw this problem is caused by apps sending binary data but setting the payload type to # text. try: - asDict["decoded"]["text"] = meshPacket.decoded.payload.decode( - "utf-8") + asBytes = asDict["decoded"]["payload"] + asDict["decoded"]["text"] = asBytes.decode("utf-8") except Exception as ex: logging.error(f"Malformatted utf8 in text message: {ex}") @@ -1361,6 +1370,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a utf8 string to some other node, if the node has a display it will also be shown on the device. @@ -1379,11 +1389,13 @@ noProto – If True, don't try to run our protocol on the link - just be a d portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse, + hopLimit=hopLimit, onResponse=onResponse) def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a data packet to some other node @@ -1403,12 +1415,16 @@ noProto – If True, don't try to run our protocol on the link - just be a d if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN: raise Exception("Data payload too big") + + if portNum == portnums_pb2.PortNum.UNKNOWN_APP: # we are now more strict wrt port numbers + raise Exception("A non-zero port number must be specified") + meshPacket = mesh_pb2.MeshPacket() meshPacket.decoded.payload = data meshPacket.decoded.portnum = portNum meshPacket.decoded.want_response = wantResponse - p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck) + p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit) if onResponse is not None: self._addResponseHandler(p.id, onResponse) return p @@ -1448,7 +1464,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d def _sendPacket(self, meshPacket, destinationId=BROADCAST_ADDR, - wantAck=False): + wantAck=False, hopLimit=defaultHopLimit): """Send a MeshPacket to the specified node (or if unspecified, broadcast). You probably don't want this - use sendData instead. @@ -1473,6 +1489,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d meshPacket.to = nodeNum 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. @@ -1582,7 +1599,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d channelSet.settings.append(c.settings) bytes = channelSet.SerializeToString() s = base64.urlsafe_b64encode(bytes).decode('ascii') - return f"https://www.meshtastic.org/d/#{s}" + return f"https://www.meshtastic.org/d/#{s}".replace("=", "") def setURL(self, url): """Set mesh network URL""" @@ -1927,7 +1944,7 @@ def channelURL(self): channelSet.settings.append(c.settings) bytes = channelSet.SerializeToString() s = base64.urlsafe_b64encode(bytes).decode('ascii') - return f"https://www.meshtastic.org/d/#{s}" + return f"https://www.meshtastic.org/d/#{s}".replace("=", "") @@ -1997,7 +2014,7 @@ def channelURL(self):
-def sendData(self, data, destinationId='^all', portNum=256, wantAck=False, wantResponse=False, onResponse=None) +def sendData(self, data, destinationId='^all', portNum=256, wantAck=False, wantResponse=False, hopLimit=3, onResponse=None)

Send a data packet to some other node

@@ -2016,6 +2033,7 @@ onResponse – A closure of the form funct(packet), that will be called when
def sendData(self, data, destinationId=BROADCAST_ADDR,
              portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False,
              wantResponse=False,
+             hopLimit=defaultHopLimit,
              onResponse=None):
     """Send a data packet to some other node
 
@@ -2035,12 +2053,16 @@ onResponse – A closure of the form funct(packet), that will be called when
 
     if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN:
         raise Exception("Data payload too big")
+
+    if portNum == portnums_pb2.PortNum.UNKNOWN_APP: # we are now more strict wrt port numbers
+        raise Exception("A non-zero port number must be specified")
+
     meshPacket = mesh_pb2.MeshPacket()
     meshPacket.decoded.payload = data
     meshPacket.decoded.portnum = portNum
     meshPacket.decoded.want_response = wantResponse
 
-    p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck)
+    p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit)
     if onResponse is not None:
         self._addResponseHandler(p.id, onResponse)
     return p
@@ -2091,7 +2113,7 @@ the local position.

-def sendText(self, text: ~AnyStr, destinationId='^all', wantAck=False, wantResponse=False, onResponse=None) +def sendText(self, text: ~AnyStr, destinationId='^all', wantAck=False, wantResponse=False, hopLimit=3, onResponse=None)

Send a utf8 string to some other node, if the node has a display it will also be shown on the device.

@@ -2111,6 +2133,7 @@ wantResponse – True if you want the service on the other side to send an a destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a utf8 string to some other node, if the node has a display it will also be shown on the device. @@ -2129,6 +2152,7 @@ wantResponse – True if you want the service on the other side to send an a portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse, + hopLimit=hopLimit, onResponse=onResponse)
diff --git a/docs/meshtastic/test.html b/docs/meshtastic/test.html index 12327be..73bc6b4 100644 --- a/docs/meshtastic/test.html +++ b/docs/meshtastic/test.html @@ -53,7 +53,7 @@ def onReceive(packet, interface): if sendingInterface == interface: print("Ignoring sending interface") else: - print(f"From {interface.stream.port}: {packet}") + # print(f"From {interface.stream.port}: {packet}") p = DotMap(packet) if p.decoded.portnum == "TEXT_MESSAGE_APP": @@ -227,7 +227,7 @@ def testAll(): if sendingInterface == interface: print("Ignoring sending interface") else: - print(f"From {interface.stream.port}: {packet}") + # print(f"From {interface.stream.port}: {packet}") p = DotMap(packet) if p.decoded.portnum == "TEXT_MESSAGE_APP": diff --git a/meshtastic/__init__.py b/meshtastic/__init__.py index 141fcac..dd69d99 100644 --- a/meshtastic/__init__.py +++ b/meshtastic/__init__.py @@ -77,6 +77,7 @@ START1 = 0x94 START2 = 0xc3 HEADER_LEN = 4 MAX_TO_FROM_RADIO_SIZE = 512 +defaultHopLimit = 3 BROADCAST_ADDR = "^all" # A special ID that means broadcast @@ -151,6 +152,7 @@ class MeshInterface: destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a utf8 string to some other node, if the node has a display it will also be shown on the device. @@ -169,11 +171,13 @@ class MeshInterface: portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse, + hopLimit=hopLimit, onResponse=onResponse) def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False, + hopLimit=defaultHopLimit, onResponse=None): """Send a data packet to some other node @@ -193,12 +197,16 @@ class MeshInterface: if len(data) > mesh_pb2.Constants.DATA_PAYLOAD_LEN: raise Exception("Data payload too big") + + if portNum == portnums_pb2.PortNum.UNKNOWN_APP: # we are now more strict wrt port numbers + raise Exception("A non-zero port number must be specified") + meshPacket = mesh_pb2.MeshPacket() meshPacket.decoded.payload = data meshPacket.decoded.portnum = portNum meshPacket.decoded.want_response = wantResponse - p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck) + p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit) if onResponse is not None: self._addResponseHandler(p.id, onResponse) return p @@ -238,7 +246,7 @@ class MeshInterface: def _sendPacket(self, meshPacket, destinationId=BROADCAST_ADDR, - wantAck=False): + wantAck=False, hopLimit=defaultHopLimit): """Send a MeshPacket to the specified node (or if unspecified, broadcast). You probably don't want this - use sendData instead. @@ -263,6 +271,7 @@ class MeshInterface: meshPacket.to = nodeNum 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. @@ -975,8 +984,8 @@ def _onTextReceive(iface, asDict): # Usually btw this problem is caused by apps sending binary data but setting the payload type to # text. try: - asDict["decoded"]["text"] = meshPacket.decoded.payload.decode( - "utf-8") + asBytes = asDict["decoded"]["payload"] + asDict["decoded"]["text"] = asBytes.decode("utf-8") except Exception as ex: logging.error(f"Malformatted utf8 in text message: {ex}") diff --git a/meshtastic/test.py b/meshtastic/test.py index 0eb8747..4147ae4 100644 --- a/meshtastic/test.py +++ b/meshtastic/test.py @@ -25,7 +25,7 @@ def onReceive(packet, interface): if sendingInterface == interface: print("Ignoring sending interface") else: - print(f"From {interface.stream.port}: {packet}") + # print(f"From {interface.stream.port}: {packet}") p = DotMap(packet) if p.decoded.portnum == "TEXT_MESSAGE_APP": diff --git a/setup.py b/setup.py index 1c586fb..442525d 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ with open("README.md", "r") as fh: # This call to setup() does all the work setup( name="meshtastic", - version="1.2.4", + version="1.2.5", description="Python API & client shell for talking to Meshtastic devices", long_description=long_description, long_description_content_type="text/markdown",