This commit is contained in:
Kevin Hester
2020-12-09 13:44:31 +08:00
parent 27466fc8b4
commit 449c0a2dac
6 changed files with 1543 additions and 891 deletions

View File

@@ -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".</li>
<li>meshtastic.receive.position(packet)</li>
<li>meshtastic.receive.user(packet)</li>
<li>meshtastic.receive.data(packet)</li>
<li>meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)</li>
<li>meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc&hellip;)</li>
</ul>
<p>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 &#34;meshtastic.receive&#34;.
- 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
&#34;&#34;&#34;The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand&#34;&#34;&#34;
OUR_APP_VERSION = 172
&#34;&#34;&#34;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
&#34;&#34;&#34;
OUR_APP_VERSION = 20120
class MeshInterface:
&#34;&#34;&#34;Interface class for meshtastic devices
@@ -184,10 +185,15 @@ class MeshInterface:
&#34;&#34;&#34;
def __init__(self, debugOut=None, noProto=False):
&#34;&#34;&#34;Constructor&#34;&#34;&#34;
&#34;&#34;&#34;Constructor
Keyword Arguments:
noProto -- If True, don&#39;t try to run our protocol on the link - just be a dumb serial client.
&#34;&#34;&#34;
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.
&#34;&#34;&#34;
return self.sendData(text.encode(&#34;utf-8&#34;), 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):
&#34;&#34;&#34;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.
&#34;&#34;&#34;
if getattr(data, &#34;SerializeToString&#34;, None):
logging.debug(f&#34;Serializing protobuf as data: {data}&#34;)
data = data.SerializeToString()
if len(data) &gt; mesh_pb2.Constants.DATA_PAYLOAD_LEN:
raise Exception(&#34;Data payload too big&#34;)
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.
&#34;&#34;&#34;
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):
&#34;&#34;&#34;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.
&#34;&#34;&#34;
self._waitConnected()
toRadio = mesh_pb2.ToRadio()
# FIXME add support for non broadcast addresses
if isinstance(destinationId, int):
if destinationId is None:
raise Exception(&#34;destinationId must not be None&#34;)
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(&#34;Wrote config&#34;)
def getMyNode(self):
if self.myInfo is None:
@@ -374,6 +393,12 @@ class MeshInterface:
if write:
self.writeConfig()
def _waitConnected(self):
&#34;&#34;&#34;Block until the initial node db download is complete, or timeout
and raise an exception&#34;&#34;&#34;
if not self.isConnected.wait(5.0): # timeout after 5 seconds
raise Exception(&#34;Timed out waiting for connection completion&#34;)
def _generatePacketId(self):
&#34;&#34;&#34;Get a new unique packet ID&#34;&#34;&#34;
if self.currentPacketId is None:
@@ -384,13 +409,13 @@ class MeshInterface:
def _disconnected(self):
&#34;&#34;&#34;Called by subclasses to tell clients this interface has disconnected&#34;&#34;&#34;
self.isConnected = False
self.isConnected.clear()
pub.sendMessage(&#34;meshtastic.connection.lost&#34;, interface=self)
def _connected(self):
&#34;&#34;&#34;Called by this class to tell clients we are now fully connected to a node
&#34;&#34;&#34;
self.isConnected = True
self.isConnected.set()
pub.sendMessage(&#34;meshtastic.connection.established&#34;, 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 = &#34;meshtastic.receive&#34; # Generic unknown packet type
if meshPacket.decoded.HasField(&#34;position&#34;):
topic = &#34;meshtastic.receive.position&#34;
p = asDict[&#34;decoded&#34;][&#34;position&#34;]
self._fixupPosition(p)
# update node DB as needed
self._getOrCreateByNum(asDict[&#34;from&#34;])[&#34;position&#34;] = p
if meshPacket.decoded.HasField(&#34;user&#34;):
topic = &#34;meshtastic.receive.user&#34;
u = asDict[&#34;decoded&#34;][&#34;user&#34;]
# update node DB as needed
n = self._getOrCreateByNum(asDict[&#34;from&#34;])
n[&#34;user&#34;] = u
# We now have a node ID, make sure it is uptodate in that table
self.nodes[u[&#34;id&#34;]] = u
# Warn users if firmware doesn&#39;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(&#34;user&#34;) or meshPacket.decoded.HasField(&#34;position&#34;):
logging.error(&#34;The device firmware is too old to work with this version of the python library. Please update firmware to 1.20 or later&#34;)
if meshPacket.decoded.HasField(&#34;data&#34;):
topic = &#34;meshtastic.receive.data&#34;
# 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&#39;t want that - it messes up data payload. So slam in the correct
# byte array.
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;payload&#34;] = 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 &#34;typ&#34; in asDict[&#34;decoded&#34;][&#34;data&#34;]:
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;typ&#34;] = &#34;OPAQUE&#34;
if not &#34;portnum&#34; in asDict[&#34;decoded&#34;][&#34;data&#34;]:
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] = portnums_pb2.PortNum.UNKNOWN_APP
portnum = asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;]
topic = f&#34;meshtastic.receive.data.{portnum}&#34;
# For text messages, we go ahead and decode the text to ascii for our users
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;typ&#34;] == &#34;CLEAR_TEXT&#34;:
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] == portnums_pb2.PortNum.TEXT_MESSAGE_APP:
topic = &#34;meshtastic.receive.text&#34;
# We don&#39;t throw if the utf8 is invalid in the text message. Instead we just don&#39;t populate
@@ -549,6 +571,31 @@ class MeshInterface:
except Exception as ex:
logging.error(f&#34;Malformatted utf8 in text message: {ex}&#34;)
# decode position protobufs and update nodedb, provide decoded version as &#34;position&#34; in the published msg
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] == portnums_pb2.PortNum.POSITION_APP:
topic = &#34;meshtastic.receive.position&#34;
pb = mesh_pb2.Position()
pb.ParseFromString(meshPacket.decoded.data.payload)
p = google.protobuf.json_format.MessageToDict(pb)
self._fixupPosition(p)
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;position&#34;] = p
# update node DB as needed
self._getOrCreateByNum(asDict[&#34;from&#34;])[&#34;position&#34;] = p
# decode user protobufs and update nodedb, provide decoded version as &#34;position&#34; in the published msg
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] == portnums_pb2.PortNum.NODEINFO_APP:
topic = &#34;meshtastic.receive.user&#34;
pb = mesh_pb2.User()
pb.ParseFromString(meshPacket.decoded.data.payload)
u = google.protobuf.json_format.MessageToDict(pb)
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;user&#34;] = u
# update node DB as needed
n = self._getOrCreateByNum(asDict[&#34;from&#34;])
n[&#34;user&#34;] = u
# We now have a node ID, make sure it is uptodate in that table
self.nodes[u[&#34;id&#34;]] = u
logging.debug(f&#34;Publishing topic {topic}&#34;)
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):
&#34;&#34;&#34;We override the superclass implementation to close our port&#34;&#34;&#34;
@@ -827,6 +877,18 @@ class TCPInterface(StreamInterface):
<dd>
<div class="desc"><p>Generated protocol buffer code.</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.portnums_pb2" href="portnums_pb2.html">meshtastic.portnums_pb2</a></code></dt>
<dd>
<div class="desc"><p>Generated protocol buffer code.</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.remote_hardware" href="remote_hardware.html">meshtastic.remote_hardware</a></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt><code class="name"><a title="meshtastic.remote_hardware_pb2" href="remote_hardware_pb2.html">meshtastic.remote_hardware_pb2</a></code></dt>
<dd>
<div class="desc"><p>Generated protocol buffer code.</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.test" href="test.html">meshtastic.test</a></code></dt>
<dd>
<div class="desc"></div>
@@ -842,7 +904,8 @@ class TCPInterface(StreamInterface):
<dl>
<dt id="meshtastic.MY_CONFIG_ID"><code class="name">var <span class="ident">MY_CONFIG_ID</span></code></dt>
<dd>
<div class="desc"><p>The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand</p></div>
<div class="desc"><p>The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand</p>
<p>format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20</p></div>
</dd>
</dl>
</section>
@@ -857,7 +920,9 @@ class TCPInterface(StreamInterface):
</code></dt>
<dd>
<div class="desc"><p>A not quite ready - FIXME - BLE interface to devices</p>
<p>Constructor</p></div>
<p>Constructor</p>
<p>Keyword Arguments:
noProto &ndash; If True, don't try to run our protocol on the link - just be a dumb serial client.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
@@ -946,7 +1011,9 @@ class TCPInterface(StreamInterface):
<p>isConnected
nodes
debugOut</p>
<p>Constructor</p></div>
<p>Constructor</p>
<p>Keyword Arguments:
noProto &ndash; If True, don't try to run our protocol on the link - just be a dumb serial client.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
@@ -962,10 +1029,15 @@ debugOut</p>
&#34;&#34;&#34;
def __init__(self, debugOut=None, noProto=False):
&#34;&#34;&#34;Constructor&#34;&#34;&#34;
&#34;&#34;&#34;Constructor
Keyword Arguments:
noProto -- If True, don&#39;t try to run our protocol on the link - just be a dumb serial client.
&#34;&#34;&#34;
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</p>
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.
&#34;&#34;&#34;
return self.sendData(text.encode(&#34;utf-8&#34;), 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):
&#34;&#34;&#34;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.
&#34;&#34;&#34;
if getattr(data, &#34;SerializeToString&#34;, None):
logging.debug(f&#34;Serializing protobuf as data: {data}&#34;)
data = data.SerializeToString()
if len(data) &gt; mesh_pb2.Constants.DATA_PAYLOAD_LEN:
raise Exception(&#34;Data payload too big&#34;)
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</p>
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
&#34;&#34;&#34;
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):
&#34;&#34;&#34;Send a MeshPacket to the specified node (or if unspecified, broadcast).
@@ -1043,10 +1123,14 @@ debugOut</p>
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
&#34;&#34;&#34;
self._waitConnected()
toRadio = mesh_pb2.ToRadio()
# FIXME add support for non broadcast addresses
if isinstance(destinationId, int):
if destinationId is None:
raise Exception(&#34;destinationId must not be None&#34;)
elif isinstance(destinationId, int):
nodeNum = destinationId
elif destinationId == BROADCAST_ADDR:
nodeNum = BROADCAST_NUM
@@ -1081,6 +1165,7 @@ debugOut</p>
t = mesh_pb2.ToRadio()
t.set_radio.CopyFrom(self.radioConfig)
self._sendToRadio(t)
logging.debug(&#34;Wrote config&#34;)
def getMyNode(self):
if self.myInfo is None:
@@ -1152,6 +1237,12 @@ debugOut</p>
if write:
self.writeConfig()
def _waitConnected(self):
&#34;&#34;&#34;Block until the initial node db download is complete, or timeout
and raise an exception&#34;&#34;&#34;
if not self.isConnected.wait(5.0): # timeout after 5 seconds
raise Exception(&#34;Timed out waiting for connection completion&#34;)
def _generatePacketId(self):
&#34;&#34;&#34;Get a new unique packet ID&#34;&#34;&#34;
if self.currentPacketId is None:
@@ -1162,13 +1253,13 @@ debugOut</p>
def _disconnected(self):
&#34;&#34;&#34;Called by subclasses to tell clients this interface has disconnected&#34;&#34;&#34;
self.isConnected = False
self.isConnected.clear()
pub.sendMessage(&#34;meshtastic.connection.lost&#34;, interface=self)
def _connected(self):
&#34;&#34;&#34;Called by this class to tell clients we are now fully connected to a node
&#34;&#34;&#34;
self.isConnected = True
self.isConnected.set()
pub.sendMessage(&#34;meshtastic.connection.established&#34;, interface=self)
def _startConfig(self):
@@ -1288,32 +1379,29 @@ debugOut</p>
# We could provide our objects as DotMaps - which work with . notation or as dictionaries
# asObj = DotMap(asDict)
topic = &#34;meshtastic.receive&#34; # Generic unknown packet type
if meshPacket.decoded.HasField(&#34;position&#34;):
topic = &#34;meshtastic.receive.position&#34;
p = asDict[&#34;decoded&#34;][&#34;position&#34;]
self._fixupPosition(p)
# update node DB as needed
self._getOrCreateByNum(asDict[&#34;from&#34;])[&#34;position&#34;] = p
if meshPacket.decoded.HasField(&#34;user&#34;):
topic = &#34;meshtastic.receive.user&#34;
u = asDict[&#34;decoded&#34;][&#34;user&#34;]
# update node DB as needed
n = self._getOrCreateByNum(asDict[&#34;from&#34;])
n[&#34;user&#34;] = u
# We now have a node ID, make sure it is uptodate in that table
self.nodes[u[&#34;id&#34;]] = u
# Warn users if firmware doesn&#39;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(&#34;user&#34;) or meshPacket.decoded.HasField(&#34;position&#34;):
logging.error(&#34;The device firmware is too old to work with this version of the python library. Please update firmware to 1.20 or later&#34;)
if meshPacket.decoded.HasField(&#34;data&#34;):
topic = &#34;meshtastic.receive.data&#34;
# 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&#39;t want that - it messes up data payload. So slam in the correct
# byte array.
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;payload&#34;] = 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 &#34;typ&#34; in asDict[&#34;decoded&#34;][&#34;data&#34;]:
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;typ&#34;] = &#34;OPAQUE&#34;
if not &#34;portnum&#34; in asDict[&#34;decoded&#34;][&#34;data&#34;]:
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] = portnums_pb2.PortNum.UNKNOWN_APP
portnum = asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;]
topic = f&#34;meshtastic.receive.data.{portnum}&#34;
# For text messages, we go ahead and decode the text to ascii for our users
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;typ&#34;] == &#34;CLEAR_TEXT&#34;:
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] == portnums_pb2.PortNum.TEXT_MESSAGE_APP:
topic = &#34;meshtastic.receive.text&#34;
# We don&#39;t throw if the utf8 is invalid in the text message. Instead we just don&#39;t populate
@@ -1327,6 +1415,31 @@ debugOut</p>
except Exception as ex:
logging.error(f&#34;Malformatted utf8 in text message: {ex}&#34;)
# decode position protobufs and update nodedb, provide decoded version as &#34;position&#34; in the published msg
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] == portnums_pb2.PortNum.POSITION_APP:
topic = &#34;meshtastic.receive.position&#34;
pb = mesh_pb2.Position()
pb.ParseFromString(meshPacket.decoded.data.payload)
p = google.protobuf.json_format.MessageToDict(pb)
self._fixupPosition(p)
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;position&#34;] = p
# update node DB as needed
self._getOrCreateByNum(asDict[&#34;from&#34;])[&#34;position&#34;] = p
# decode user protobufs and update nodedb, provide decoded version as &#34;position&#34; in the published msg
if asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;portnum&#34;] == portnums_pb2.PortNum.NODEINFO_APP:
topic = &#34;meshtastic.receive.user&#34;
pb = mesh_pb2.User()
pb.ParseFromString(meshPacket.decoded.data.payload)
u = google.protobuf.json_format.MessageToDict(pb)
asDict[&#34;decoded&#34;][&#34;data&#34;][&#34;user&#34;] = u
# update node DB as needed
n = self._getOrCreateByNum(asDict[&#34;from&#34;])
n[&#34;user&#34;] = u
# We now have a node ID, make sure it is uptodate in that table
self.nodes[u[&#34;id&#34;]] = u
logging.debug(f&#34;Publishing topic {topic}&#34;)
pub.sendMessage(topic, packet=asDict, interface=self)</code></pre>
</details>
<h3>Subclasses</h3>
@@ -1408,30 +1521,40 @@ def channelURL(self):
</details>
</dd>
<dt id="meshtastic.MeshInterface.sendData"><code class="name flex">
<span>def <span class="ident">sendData</span></span>(<span>self, byteData, destinationId='^all', dataType=0, wantAck=False, wantResponse=False)</span>
<span>def <span class="ident">sendData</span></span>(<span>self, data, destinationId='^all', portNum=256, wantAck=False, wantResponse=False)</span>
</code></dt>
<dd>
<div class="desc"><p>Send a data packet to some other node</p>
<p>Keyword Arguments:
data &ndash; 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} &ndash; where to send this message (default: {BROADCAST_ADDR})
portNum &ndash; the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
wantAck &ndash; True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)</p>
<p>Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def sendData(self, byteData, destinationId=BROADCAST_ADDR, dataType=mesh_pb2.Data.OPAQUE, wantAck=False, wantResponse=False):
<pre><code class="python">def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False):
&#34;&#34;&#34;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.
&#34;&#34;&#34;
if getattr(data, &#34;SerializeToString&#34;, None):
logging.debug(f&#34;Serializing protobuf as data: {data}&#34;)
data = data.SerializeToString()
if len(data) &gt; mesh_pb2.Constants.DATA_PAYLOAD_LEN:
raise Exception(&#34;Data payload too big&#34;)
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)</code></pre>
</details>
@@ -1453,10 +1576,14 @@ You probably don't want this - use sendData instead.</p>
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
&#34;&#34;&#34;
self._waitConnected()
toRadio = mesh_pb2.ToRadio()
# FIXME add support for non broadcast addresses
if isinstance(destinationId, int):
if destinationId is None:
raise Exception(&#34;destinationId must not be None&#34;)
elif isinstance(destinationId, int):
nodeNum = destinationId
elif destinationId == BROADCAST_ADDR:
nodeNum = BROADCAST_NUM
@@ -1500,22 +1627,21 @@ the local position.</p>
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
&#34;&#34;&#34;
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)</code></pre>
return self.sendData(p, destinationId, portNum=portnums_pb2.PortNum.POSITION_APP, wantAck=wantAck, wantResponse=wantResponse)</code></pre>
</details>
</dd>
<dt id="meshtastic.MeshInterface.sendText"><code class="name flex">
@@ -1527,6 +1653,7 @@ the local position.</p>
<p>text {string} &ndash; The text to send</p>
<p>Keyword Arguments:
destinationId {nodeId or nodeNum} &ndash; where to send this message (default: {BROADCAST_ADDR})
portNum &ndash; the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
wantAck &ndash; True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)</p>
<p>Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.</p></div>
<details class="source">
@@ -1541,12 +1668,13 @@ wantAck &ndash; 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.
&#34;&#34;&#34;
return self.sendData(text.encode(&#34;utf-8&#34;), destinationId,
dataType=mesh_pb2.Data.CLEAR_TEXT, wantAck=wantAck, wantResponse=wantResponse)</code></pre>
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse)</code></pre>
</details>
</dd>
<dt id="meshtastic.MeshInterface.setOwner"><code class="name flex">
@@ -1643,7 +1771,8 @@ wantAck &ndash; 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)</code></pre>
self._sendToRadio(t)
logging.debug(&#34;Wrote config&#34;)</code></pre>
</details>
</dd>
</dl>
@@ -1778,6 +1907,7 @@ debugOut {stream} &ndash; 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} &ndash; 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):
&#34;&#34;&#34;We override the superclass implementation to close our port&#34;&#34;&#34;
@@ -1937,7 +2069,9 @@ start the reading thread later.</p></div>
self._writeBytes(bytes([START1, START1, START1, START1]))
time.sleep(0.1) # wait 100ms to give device time to start running
self._rxThread.start()</code></pre>
self._rxThread.start()
if not self.noProto: # Wait for the db download if using the protocol
self._waitConnected()</code></pre>
</details>
</dd>
</dl>
@@ -2051,6 +2185,9 @@ hostname {string} &ndash; Hostname/IP address of the device to connect to</p></d
<ul>
<li><code><a title="meshtastic.ble" href="ble.html">meshtastic.ble</a></code></li>
<li><code><a title="meshtastic.mesh_pb2" href="mesh_pb2.html">meshtastic.mesh_pb2</a></code></li>
<li><code><a title="meshtastic.portnums_pb2" href="portnums_pb2.html">meshtastic.portnums_pb2</a></code></li>
<li><code><a title="meshtastic.remote_hardware" href="remote_hardware.html">meshtastic.remote_hardware</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2" href="remote_hardware_pb2.html">meshtastic.remote_hardware_pb2</a></code></li>
<li><code><a title="meshtastic.test" href="test.html">meshtastic.test</a></code></li>
<li><code><a title="meshtastic.util" href="util.html">meshtastic.util</a></code></li>
</ul>

View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,150 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.9.1" />
<title>meshtastic.portnums_pb2 API documentation</title>
<meta name="description" content="Generated protocol buffer code." />
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>meshtastic.portnums_pb2</code></h1>
</header>
<section id="section-intro">
<p>Generated protocol buffer code.</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python"># -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: portnums.proto
&#34;&#34;&#34;Generated protocol buffer code.&#34;&#34;&#34;
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=&#39;portnums.proto&#39;,
package=&#39;&#39;,
syntax=&#39;proto3&#39;,
serialized_options=b&#39;\n\023com.geeksville.meshB\010PortnumsH\003&#39;,
create_key=_descriptor._internal_create_key,
serialized_pb=b&#39;\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&#39;
)
_PORTNUM = _descriptor.EnumDescriptor(
name=&#39;PortNum&#39;,
full_name=&#39;PortNum&#39;,
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name=&#39;UNKNOWN_APP&#39;, index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;TEXT_MESSAGE_APP&#39;, index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;REMOTE_HARDWARE_APP&#39;, index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;POSITION_APP&#39;, index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;NODEINFO_APP&#39;, index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;PRIVATE_APP&#39;, index=5, number=256,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;IP_TUNNEL_APP&#39;, 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[&#39;PortNum&#39;] = _PORTNUM
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope)</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
</article>
<nav id="sidebar">
<h1>Index</h1>
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.1</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,238 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.9.1" />
<title>meshtastic.remote_hardware API documentation</title>
<meta name="description" content="" />
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>meshtastic.remote_hardware</code></h1>
</header>
<section id="section-intro">
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">from . import portnums_pb2, remote_hardware_pb2
from pubsub import pub
def onGPIOreceive(packet, interface):
&#34;&#34;&#34;Callback for received GPIO responses
FIXME figure out how to do closures with methods in python&#34;&#34;&#34;
pb = remote_hardware_pb2.HardwareMessage()
pb.ParseFromString(packet[&#34;decoded&#34;][&#34;data&#34;][&#34;payload&#34;])
print(f&#34;Received RemoteHardware typ={pb.typ}, gpio_value={pb.gpio_value}&#34;)
class RemoteHardwareClient:
&#34;&#34;&#34;
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
&#34;&#34;&#34;
def __init__(self, iface):
&#34;&#34;&#34;
Constructor
iface is the already open MeshInterface instance
&#34;&#34;&#34;
self.iface = iface
pub.subscribe(onGPIOreceive, &#34;meshtastic.receive.data.REMOTE_HARDWARE_APP&#34;)
def writeGPIOs(self, nodeid, mask, vals):
&#34;&#34;&#34;
Write the specified vals bits to the device GPIOs. Only bits in mask that
are 1 will be changed
&#34;&#34;&#34;
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):
&#34;&#34;&#34;Read the specified bits from GPIO inputs on the device&#34;&#34;&#34;
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)</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.remote_hardware.onGPIOreceive"><code class="name flex">
<span>def <span class="ident">onGPIOreceive</span></span>(<span>packet, interface)</span>
</code></dt>
<dd>
<div class="desc"><p>Callback for received GPIO responses</p>
<p>FIXME figure out how to do closures with methods in python</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def onGPIOreceive(packet, interface):
&#34;&#34;&#34;Callback for received GPIO responses
FIXME figure out how to do closures with methods in python&#34;&#34;&#34;
pb = remote_hardware_pb2.HardwareMessage()
pb.ParseFromString(packet[&#34;decoded&#34;][&#34;data&#34;][&#34;payload&#34;])
print(f&#34;Received RemoteHardware typ={pb.typ}, gpio_value={pb.gpio_value}&#34;)</code></pre>
</details>
</dd>
</dl>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="meshtastic.remote_hardware.RemoteHardwareClient"><code class="flex name class">
<span>class <span class="ident">RemoteHardwareClient</span></span>
<span>(</span><span>iface)</span>
</code></dt>
<dd>
<div class="desc"><p>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</p>
<p>Constructor</p>
<p>iface is the already open MeshInterface instance</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class RemoteHardwareClient:
&#34;&#34;&#34;
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
&#34;&#34;&#34;
def __init__(self, iface):
&#34;&#34;&#34;
Constructor
iface is the already open MeshInterface instance
&#34;&#34;&#34;
self.iface = iface
pub.subscribe(onGPIOreceive, &#34;meshtastic.receive.data.REMOTE_HARDWARE_APP&#34;)
def writeGPIOs(self, nodeid, mask, vals):
&#34;&#34;&#34;
Write the specified vals bits to the device GPIOs. Only bits in mask that
are 1 will be changed
&#34;&#34;&#34;
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):
&#34;&#34;&#34;Read the specified bits from GPIO inputs on the device&#34;&#34;&#34;
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)</code></pre>
</details>
<h3>Methods</h3>
<dl>
<dt id="meshtastic.remote_hardware.RemoteHardwareClient.readGPIOs"><code class="name flex">
<span>def <span class="ident">readGPIOs</span></span>(<span>self, nodeid, mask)</span>
</code></dt>
<dd>
<div class="desc"><p>Read the specified bits from GPIO inputs on the device</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def readGPIOs(self, nodeid, mask):
&#34;&#34;&#34;Read the specified bits from GPIO inputs on the device&#34;&#34;&#34;
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)</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware.RemoteHardwareClient.writeGPIOs"><code class="name flex">
<span>def <span class="ident">writeGPIOs</span></span>(<span>self, nodeid, mask, vals)</span>
</code></dt>
<dd>
<div class="desc"><p>Write the specified vals bits to the device GPIOs.
Only bits in mask that
are 1 will be changed</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def writeGPIOs(self, nodeid, mask, vals):
&#34;&#34;&#34;
Write the specified vals bits to the device GPIOs. Only bits in mask that
are 1 will be changed
&#34;&#34;&#34;
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)</code></pre>
</details>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
<nav id="sidebar">
<h1>Index</h1>
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.remote_hardware.onGPIOreceive" href="#meshtastic.remote_hardware.onGPIOreceive">onGPIOreceive</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="meshtastic.remote_hardware.RemoteHardwareClient" href="#meshtastic.remote_hardware.RemoteHardwareClient">RemoteHardwareClient</a></code></h4>
<ul class="">
<li><code><a title="meshtastic.remote_hardware.RemoteHardwareClient.readGPIOs" href="#meshtastic.remote_hardware.RemoteHardwareClient.readGPIOs">readGPIOs</a></code></li>
<li><code><a title="meshtastic.remote_hardware.RemoteHardwareClient.writeGPIOs" href="#meshtastic.remote_hardware.RemoteHardwareClient.writeGPIOs">writeGPIOs</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.1</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,837 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.9.1" />
<title>meshtastic.remote_hardware_pb2 API documentation</title>
<meta name="description" content="Generated protocol buffer code." />
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>meshtastic.remote_hardware_pb2</code></h1>
</header>
<section id="section-intro">
<p>Generated protocol buffer code.</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python"># -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: remote_hardware.proto
&#34;&#34;&#34;Generated protocol buffer code.&#34;&#34;&#34;
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=&#39;remote_hardware.proto&#39;,
package=&#39;&#39;,
syntax=&#39;proto3&#39;,
serialized_options=b&#39;\n\023com.geeksville.meshB\016RemoteHardwareH\003&#39;,
create_key=_descriptor._internal_create_key,
serialized_pb=b&#39;\n\x15remote_hardware.proto\&#34;\xca\x01\n\x0fHardwareMessage\x12\&#34;\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\&#34;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\&#39;\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareH\x03\x62\x06proto3&#39;
)
_HARDWAREMESSAGE_TYPE = _descriptor.EnumDescriptor(
name=&#39;Type&#39;,
full_name=&#39;HardwareMessage.Type&#39;,
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name=&#39;UNSET&#39;, index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;WRITE_GPIOS&#39;, index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;WATCH_GPIOS&#39;, index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;GPIOS_CHANGED&#39;, index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;READ_GPIOS&#39;, index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name=&#39;READ_GPIOS_REPLY&#39;, 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=&#39;HardwareMessage&#39;,
full_name=&#39;HardwareMessage&#39;,
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name=&#39;typ&#39;, full_name=&#39;HardwareMessage.typ&#39;, 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=&#39;gpio_mask&#39;, full_name=&#39;HardwareMessage.gpio_mask&#39;, 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=&#39;gpio_value&#39;, full_name=&#39;HardwareMessage.gpio_value&#39;, 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=&#39;proto3&#39;,
extension_ranges=[],
oneofs=[
],
serialized_start=26,
serialized_end=228,
)
_HARDWAREMESSAGE.fields_by_name[&#39;typ&#39;].enum_type = _HARDWAREMESSAGE_TYPE
_HARDWAREMESSAGE_TYPE.containing_type = _HARDWAREMESSAGE
DESCRIPTOR.message_types_by_name[&#39;HardwareMessage&#39;] = _HARDWAREMESSAGE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
HardwareMessage = _reflection.GeneratedProtocolMessageType(&#39;HardwareMessage&#39;, (_message.Message,), {
&#39;DESCRIPTOR&#39; : _HARDWAREMESSAGE,
&#39;__module__&#39; : &#39;remote_hardware_pb2&#39;
# @@protoc_insertion_point(class_scope:HardwareMessage)
})
_sym_db.RegisterMessage(HardwareMessage)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope)</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage"><code class="flex name class">
<span>class <span class="ident">HardwareMessage</span></span>
<span>(</span><span>**kwargs)</span>
</code></dt>
<dd>
<div class="desc"><p>Abstract base class for protocol messages.</p>
<p>Protocol message classes are almost always generated by the protocol
compiler.
These generated types subclass Message and implement the methods
shown below.</p></div>
<h3>Ancestors</h3>
<ul class="hlist">
<li>google.protobuf.message.Message</li>
</ul>
<h3>Class variables</h3>
<dl>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.GPIOS_CHANGED"><code class="name">var <span class="ident">GPIOS_CHANGED</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_MASK_FIELD_NUMBER"><code class="name">var <span class="ident">GPIO_MASK_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_VALUE_FIELD_NUMBER"><code class="name">var <span class="ident">GPIO_VALUE_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS"><code class="name">var <span class="ident">READ_GPIOS</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS_REPLY"><code class="name">var <span class="ident">READ_GPIOS_REPLY</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.TYP_FIELD_NUMBER"><code class="name">var <span class="ident">TYP_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.Type"><code class="name">var <span class="ident">Type</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.UNSET"><code class="name">var <span class="ident">UNSET</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.WATCH_GPIOS"><code class="name">var <span class="ident">WATCH_GPIOS</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.WRITE_GPIOS"><code class="name">var <span class="ident">WRITE_GPIOS</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
</dl>
<h3>Static methods</h3>
<dl>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.FromString"><code class="name flex">
<span>def <span class="ident">FromString</span></span>(<span>s)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def FromString(s):
message = cls()
message.MergeFromString(s)
return message</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.RegisterExtension"><code class="name flex">
<span>def <span class="ident">RegisterExtension</span></span>(<span>extension_handle)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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)</code></pre>
</details>
</dd>
</dl>
<h3>Instance variables</h3>
<dl>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_mask"><code class="name">var <span class="ident">gpio_mask</span></code></dt>
<dd>
<div class="desc"><p>Getter for gpio_mask.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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)</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_value"><code class="name">var <span class="ident">gpio_value</span></code></dt>
<dd>
<div class="desc"><p>Getter for gpio_value.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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)</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.typ"><code class="name">var <span class="ident">typ</span></code></dt>
<dd>
<div class="desc"><p>Getter for typ.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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)</code></pre>
</details>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.ByteSize"><code class="name flex">
<span>def <span class="ident">ByteSize</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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[&#39;key&#39;]._sizer(self.key)
size += descriptor.fields_by_name[&#39;value&#39;]._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</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.Clear"><code class="name flex">
<span>def <span class="ident">Clear</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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()</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.ClearField"><code class="name flex">
<span>def <span class="ident">ClearField</span></span>(<span>self, field_name)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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(&#39;Protocol message %s has no &#34;%s&#34; field.&#39; %
(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], &#39;InvalidateIterators&#39;):
self._fields[field].InvalidateIterators()
# Note: If the field is a sub-message, its listener will still point
# at us. That&#39;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()</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.DiscardUnknownFields"><code class="name flex">
<span>def <span class="ident">DiscardUnknownFields</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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()</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.FindInitializationErrors"><code class="name flex">
<span>def <span class="ident">FindInitializationErrors</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Finds required fields which are not initialized.</p>
<h2 id="returns">Returns</h2>
<p>A list of strings.
Each string is a path to an uninitialized field from
the top-level message, e.g. "foo.bar[5].baz".</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def FindInitializationErrors(self):
&#34;&#34;&#34;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. &#34;foo.bar[5].baz&#34;.
&#34;&#34;&#34;
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 = &#39;(%s)&#39; % field.full_name
else:
name = field.name
if _IsMapField(field):
if _IsMessageMapField(field):
for key in value:
element = value[key]
prefix = &#39;%s[%s].&#39; % (name, key)
sub_errors = element.FindInitializationErrors()
errors += [prefix + error for error in sub_errors]
else:
# ScalarMaps can&#39;t have any initialization errors.
pass
elif field.label == _FieldDescriptor.LABEL_REPEATED:
for i in range(len(value)):
element = value[i]
prefix = &#39;%s[%d].&#39; % (name, i)
sub_errors = element.FindInitializationErrors()
errors += [prefix + error for error in sub_errors]
else:
prefix = name + &#39;.&#39;
sub_errors = value.FindInitializationErrors()
errors += [prefix + error for error in sub_errors]
return errors</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.HasField"><code class="name flex">
<span>def <span class="ident">HasField</span></span>(<span>self, field_name)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.IsInitialized"><code class="name flex">
<span>def <span class="ident">IsInitialized</span></span>(<span>self, errors=None)</span>
</code></dt>
<dd>
<div class="desc"><p>Checks if all required fields of a message are set.</p>
<h2 id="args">Args</h2>
<dl>
<dt><strong><code>errors</code></strong></dt>
<dd>A list which, if provided, will be populated with the field
paths of all missing required fields.</dd>
</dl>
<h2 id="returns">Returns</h2>
<p>True iff the specified message has all required fields set.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def IsInitialized(self, errors=None):
&#34;&#34;&#34;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.
&#34;&#34;&#34;
# 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</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.ListFields"><code class="name flex">
<span>def <span class="ident">ListFields</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFrom"><code class="name flex">
<span>def <span class="ident">MergeFrom</span></span>(<span>self, msg)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def MergeFrom(self, msg):
if not isinstance(msg, cls):
raise TypeError(
&#39;Parameter to MergeFrom() must be instance of same class: &#39;
&#39;expected %s got %s.&#39; % (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)</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFromString"><code class="name flex">
<span>def <span class="ident">MergeFromString</span></span>(<span>self, serialized)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def MergeFromString(self, serialized):
if isinstance(serialized, memoryview) and six.PY2:
raise TypeError(
&#39;memoryview not supported in Python 2 with the pure Python proto &#39;
&#39;implementation: this is to maintain compatibility with the C++ &#39;
&#39;implementation&#39;)
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(&#39;Unexpected end-group tag.&#39;)
except (IndexError, TypeError):
# Now ord(buf[p:p+1]) == ord(&#39;&#39;) gets TypeError.
raise message_mod.DecodeError(&#39;Truncated message.&#39;)
except struct.error as e:
raise message_mod.DecodeError(e)
return length # Return this for legacy reasons.</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.SerializePartialToString"><code class="name flex">
<span>def <span class="ident">SerializePartialToString</span></span>(<span>self, **kwargs)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def SerializePartialToString(self, **kwargs):
out = BytesIO()
self._InternalSerialize(out.write, **kwargs)
return out.getvalue()</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.SerializeToString"><code class="name flex">
<span>def <span class="ident">SerializeToString</span></span>(<span>self, **kwargs)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def SerializeToString(self, **kwargs):
# Check if the message has all of its required fields set.
if not self.IsInitialized():
raise message_mod.EncodeError(
&#39;Message %s is missing required fields: %s&#39; % (
self.DESCRIPTOR.full_name, &#39;,&#39;.join(self.FindInitializationErrors())))
return self.SerializePartialToString(**kwargs)</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.SetInParent"><code class="name flex">
<span>def <span class="ident">SetInParent</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Sets the _cached_byte_size_dirty bit to true,
and propagates this to our listener iff this was a state change.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def Modified(self):
&#34;&#34;&#34;Sets the _cached_byte_size_dirty bit to true,
and propagates this to our listener iff this was a state change.
&#34;&#34;&#34;
# 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()</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.UnknownFields"><code class="name flex">
<span>def <span class="ident">UnknownFields</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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</code></pre>
</details>
</dd>
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.WhichOneof"><code class="name flex">
<span>def <span class="ident">WhichOneof</span></span>(<span>self, oneof_name)</span>
</code></dt>
<dd>
<div class="desc"><p>Returns the name of the currently set field inside a oneof, or None.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def WhichOneof(self, oneof_name):
&#34;&#34;&#34;Returns the name of the currently set field inside a oneof, or None.&#34;&#34;&#34;
try:
field = message_descriptor.oneofs_by_name[oneof_name]
except KeyError:
raise ValueError(
&#39;Protocol message has no oneof &#34;%s&#34; field.&#39; % 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</code></pre>
</details>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
<nav id="sidebar">
<h1>Index</h1>
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage" href="#meshtastic.remote_hardware_pb2.HardwareMessage">HardwareMessage</a></code></h4>
<ul class="">
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.ByteSize" href="#meshtastic.remote_hardware_pb2.HardwareMessage.ByteSize">ByteSize</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.Clear" href="#meshtastic.remote_hardware_pb2.HardwareMessage.Clear">Clear</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.ClearField" href="#meshtastic.remote_hardware_pb2.HardwareMessage.ClearField">ClearField</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.DESCRIPTOR" href="#meshtastic.remote_hardware_pb2.HardwareMessage.DESCRIPTOR">DESCRIPTOR</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.DiscardUnknownFields" href="#meshtastic.remote_hardware_pb2.HardwareMessage.DiscardUnknownFields">DiscardUnknownFields</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.FindInitializationErrors" href="#meshtastic.remote_hardware_pb2.HardwareMessage.FindInitializationErrors">FindInitializationErrors</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.FromString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.FromString">FromString</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.GPIOS_CHANGED" href="#meshtastic.remote_hardware_pb2.HardwareMessage.GPIOS_CHANGED">GPIOS_CHANGED</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_MASK_FIELD_NUMBER" href="#meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_MASK_FIELD_NUMBER">GPIO_MASK_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_VALUE_FIELD_NUMBER" href="#meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_VALUE_FIELD_NUMBER">GPIO_VALUE_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.HasField" href="#meshtastic.remote_hardware_pb2.HardwareMessage.HasField">HasField</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.IsInitialized" href="#meshtastic.remote_hardware_pb2.HardwareMessage.IsInitialized">IsInitialized</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.ListFields" href="#meshtastic.remote_hardware_pb2.HardwareMessage.ListFields">ListFields</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFrom" href="#meshtastic.remote_hardware_pb2.HardwareMessage.MergeFrom">MergeFrom</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFromString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.MergeFromString">MergeFromString</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS" href="#meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS">READ_GPIOS</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS_REPLY" href="#meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS_REPLY">READ_GPIOS_REPLY</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.RegisterExtension" href="#meshtastic.remote_hardware_pb2.HardwareMessage.RegisterExtension">RegisterExtension</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.SerializePartialToString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.SerializePartialToString">SerializePartialToString</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.SerializeToString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.SerializeToString">SerializeToString</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.SetInParent" href="#meshtastic.remote_hardware_pb2.HardwareMessage.SetInParent">SetInParent</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.TYP_FIELD_NUMBER" href="#meshtastic.remote_hardware_pb2.HardwareMessage.TYP_FIELD_NUMBER">TYP_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.Type" href="#meshtastic.remote_hardware_pb2.HardwareMessage.Type">Type</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.UNSET" href="#meshtastic.remote_hardware_pb2.HardwareMessage.UNSET">UNSET</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.UnknownFields" href="#meshtastic.remote_hardware_pb2.HardwareMessage.UnknownFields">UnknownFields</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.WATCH_GPIOS" href="#meshtastic.remote_hardware_pb2.HardwareMessage.WATCH_GPIOS">WATCH_GPIOS</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.WRITE_GPIOS" href="#meshtastic.remote_hardware_pb2.HardwareMessage.WRITE_GPIOS">WRITE_GPIOS</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.WhichOneof" href="#meshtastic.remote_hardware_pb2.HardwareMessage.WhichOneof">WhichOneof</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_mask" href="#meshtastic.remote_hardware_pb2.HardwareMessage.gpio_mask">gpio_mask</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_value" href="#meshtastic.remote_hardware_pb2.HardwareMessage.gpio_value">gpio_value</a></code></li>
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.typ" href="#meshtastic.remote_hardware_pb2.HardwareMessage.typ">typ</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.1</a>.</p>
</footer>
</body>
</html>

View File

@@ -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",