Bump version to 1.2.45

This commit is contained in:
Jm Casler
2021-12-16 13:57:42 -05:00
parent 876a0a13dd
commit 56dec2c52a
28 changed files with 9394 additions and 509 deletions

View File

@@ -27,7 +27,7 @@
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; Bluetooth interface
<pre><code class="python">&#34;&#34;&#34;Bluetooth interface
&#34;&#34;&#34;
import logging
import pygatt
@@ -44,22 +44,27 @@ FROMNUM_UUID = &#34;ed9da18c-a800-4f66-a670-aa7547e34453&#34;
class BLEInterface(MeshInterface):
&#34;&#34;&#34;A not quite ready - FIXME - BLE interface to devices&#34;&#34;&#34;
def __init__(self, address, debugOut=None):
def __init__(self, address, noProto=False, debugOut=None):
self.address = address
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
self.adapter.start()
logging.debug(f&#34;Connecting to {self.address}&#34;)
self.device = self.adapter.connect(address)
if not noProto:
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
self.adapter.start()
logging.debug(f&#34;Connecting to {self.address}&#34;)
self.device = self.adapter.connect(address)
else:
self.adapter = None
self.device = None
logging.debug(&#34;Connected to device&#34;)
# fromradio = self.device.char_read(FROMRADIO_UUID)
MeshInterface.__init__(self, debugOut=debugOut)
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
self._readFromRadio() # read the initial responses
def handle_data(handle, data):
self._handleFromRadio(data)
self.device.subscribe(FROMNUM_UUID, callback=handle_data)
if self.device:
self.device.subscribe(FROMNUM_UUID, callback=handle_data)
def _sendToRadioImpl(self, toRadio):
&#34;&#34;&#34;Send a ToRadio protobuf to the device&#34;&#34;&#34;
@@ -69,15 +74,18 @@ class BLEInterface(MeshInterface):
def close(self):
MeshInterface.close(self)
self.adapter.stop()
if self.adapter:
self.adapter.stop()
def _readFromRadio(self):
wasEmpty = False
while not wasEmpty:
b = self.device.char_read(FROMRADIO_UUID)
wasEmpty = len(b) == 0
if not wasEmpty:
self._handleFromRadio(b)</code></pre>
if not self.noProto:
wasEmpty = False
while not wasEmpty:
if self.device:
b = self.device.char_read(FROMRADIO_UUID)
wasEmpty = len(b) == 0
if not wasEmpty:
self._handleFromRadio(b)</code></pre>
</details>
</section>
<section>
@@ -91,13 +99,14 @@ class BLEInterface(MeshInterface):
<dl>
<dt id="meshtastic.ble_interface.BLEInterface"><code class="flex name class">
<span>class <span class="ident">BLEInterface</span></span>
<span>(</span><span>address, debugOut=None)</span>
<span>(</span><span>address, noProto=False, debugOut=None)</span>
</code></dt>
<dd>
<div class="desc"><p>A not quite ready - FIXME - BLE interface to devices</p>
<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>
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>
@@ -105,22 +114,27 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
<pre><code class="python">class BLEInterface(MeshInterface):
&#34;&#34;&#34;A not quite ready - FIXME - BLE interface to devices&#34;&#34;&#34;
def __init__(self, address, debugOut=None):
def __init__(self, address, noProto=False, debugOut=None):
self.address = address
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
self.adapter.start()
logging.debug(f&#34;Connecting to {self.address}&#34;)
self.device = self.adapter.connect(address)
if not noProto:
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
self.adapter.start()
logging.debug(f&#34;Connecting to {self.address}&#34;)
self.device = self.adapter.connect(address)
else:
self.adapter = None
self.device = None
logging.debug(&#34;Connected to device&#34;)
# fromradio = self.device.char_read(FROMRADIO_UUID)
MeshInterface.__init__(self, debugOut=debugOut)
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
self._readFromRadio() # read the initial responses
def handle_data(handle, data):
self._handleFromRadio(data)
self.device.subscribe(FROMNUM_UUID, callback=handle_data)
if self.device:
self.device.subscribe(FROMNUM_UUID, callback=handle_data)
def _sendToRadioImpl(self, toRadio):
&#34;&#34;&#34;Send a ToRadio protobuf to the device&#34;&#34;&#34;
@@ -130,15 +144,18 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
def close(self):
MeshInterface.close(self)
self.adapter.stop()
if self.adapter:
self.adapter.stop()
def _readFromRadio(self):
wasEmpty = False
while not wasEmpty:
b = self.device.char_read(FROMRADIO_UUID)
wasEmpty = len(b) == 0
if not wasEmpty:
self._handleFromRadio(b)</code></pre>
if not self.noProto:
wasEmpty = False
while not wasEmpty:
if self.device:
b = self.device.char_read(FROMRADIO_UUID)
wasEmpty = len(b) == 0
if not wasEmpty:
self._handleFromRadio(b)</code></pre>
</details>
<h3>Ancestors</h3>
<ul class="hlist">

View File

@@ -0,0 +1,380 @@
<!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.10.0" />
<title>meshtastic.globals API documentation</title>
<meta name="description" content="Globals singleton class …" />
<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.globals</code></h1>
</header>
<section id="section-intro">
<p>Globals singleton class.</p>
<p>Instead of using a global, stuff your variables in this "trash can".
This is not much better than using python's globals, but it allows
us to better test meshtastic. Plus, there are some weird python
global issues/gotcha that we can hopefully avoid by using this
class instead.</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Globals singleton class.
Instead of using a global, stuff your variables in this &#34;trash can&#34;.
This is not much better than using python&#39;s globals, but it allows
us to better test meshtastic. Plus, there are some weird python
global issues/gotcha that we can hopefully avoid by using this
class instead.
&#34;&#34;&#34;
class Globals:
&#34;&#34;&#34;Globals class is a Singleton.&#34;&#34;&#34;
__instance = None
@staticmethod
def getInstance():
&#34;&#34;&#34;Get an instance of the Globals class.&#34;&#34;&#34;
if Globals.__instance is None:
Globals()
return Globals.__instance
def __init__(self):
&#34;&#34;&#34;Constructor for the Globals CLass&#34;&#34;&#34;
if Globals.__instance is not None:
raise Exception(&#34;This class is a singleton&#34;)
else:
Globals.__instance = self
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None
def reset(self):
&#34;&#34;&#34;Reset all of our globals. If you add a member, add it to this method, too.&#34;&#34;&#34;
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None
def set_args(self, args):
&#34;&#34;&#34;Set the args&#34;&#34;&#34;
self.args = args
def set_parser(self, parser):
&#34;&#34;&#34;Set the parser&#34;&#34;&#34;
self.parser = parser
def set_target_node(self, target_node):
&#34;&#34;&#34;Set the target_node&#34;&#34;&#34;
self.target_node = target_node
def set_channel_index(self, channel_index):
&#34;&#34;&#34;Set the channel_index&#34;&#34;&#34;
self.channel_index = channel_index
def get_args(self):
&#34;&#34;&#34;Get args&#34;&#34;&#34;
return self.args
def get_parser(self):
&#34;&#34;&#34;Get parser&#34;&#34;&#34;
return self.parser
def get_target_node(self):
&#34;&#34;&#34;Get target_node&#34;&#34;&#34;
return self.target_node
def get_channel_index(self):
&#34;&#34;&#34;Get channel_index&#34;&#34;&#34;
return self.channel_index</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="meshtastic.globals.Globals"><code class="flex name class">
<span>class <span class="ident">Globals</span></span>
</code></dt>
<dd>
<div class="desc"><p>Globals class is a Singleton.</p>
<p>Constructor for the Globals CLass</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class Globals:
&#34;&#34;&#34;Globals class is a Singleton.&#34;&#34;&#34;
__instance = None
@staticmethod
def getInstance():
&#34;&#34;&#34;Get an instance of the Globals class.&#34;&#34;&#34;
if Globals.__instance is None:
Globals()
return Globals.__instance
def __init__(self):
&#34;&#34;&#34;Constructor for the Globals CLass&#34;&#34;&#34;
if Globals.__instance is not None:
raise Exception(&#34;This class is a singleton&#34;)
else:
Globals.__instance = self
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None
def reset(self):
&#34;&#34;&#34;Reset all of our globals. If you add a member, add it to this method, too.&#34;&#34;&#34;
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None
def set_args(self, args):
&#34;&#34;&#34;Set the args&#34;&#34;&#34;
self.args = args
def set_parser(self, parser):
&#34;&#34;&#34;Set the parser&#34;&#34;&#34;
self.parser = parser
def set_target_node(self, target_node):
&#34;&#34;&#34;Set the target_node&#34;&#34;&#34;
self.target_node = target_node
def set_channel_index(self, channel_index):
&#34;&#34;&#34;Set the channel_index&#34;&#34;&#34;
self.channel_index = channel_index
def get_args(self):
&#34;&#34;&#34;Get args&#34;&#34;&#34;
return self.args
def get_parser(self):
&#34;&#34;&#34;Get parser&#34;&#34;&#34;
return self.parser
def get_target_node(self):
&#34;&#34;&#34;Get target_node&#34;&#34;&#34;
return self.target_node
def get_channel_index(self):
&#34;&#34;&#34;Get channel_index&#34;&#34;&#34;
return self.channel_index</code></pre>
</details>
<h3>Static methods</h3>
<dl>
<dt id="meshtastic.globals.Globals.getInstance"><code class="name flex">
<span>def <span class="ident">getInstance</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Get an instance of the Globals class.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@staticmethod
def getInstance():
&#34;&#34;&#34;Get an instance of the Globals class.&#34;&#34;&#34;
if Globals.__instance is None:
Globals()
return Globals.__instance</code></pre>
</details>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="meshtastic.globals.Globals.get_args"><code class="name flex">
<span>def <span class="ident">get_args</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Get args</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_args(self):
&#34;&#34;&#34;Get args&#34;&#34;&#34;
return self.args</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.get_channel_index"><code class="name flex">
<span>def <span class="ident">get_channel_index</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Get channel_index</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_channel_index(self):
&#34;&#34;&#34;Get channel_index&#34;&#34;&#34;
return self.channel_index</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.get_parser"><code class="name flex">
<span>def <span class="ident">get_parser</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Get parser</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_parser(self):
&#34;&#34;&#34;Get parser&#34;&#34;&#34;
return self.parser</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.get_target_node"><code class="name flex">
<span>def <span class="ident">get_target_node</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Get target_node</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_target_node(self):
&#34;&#34;&#34;Get target_node&#34;&#34;&#34;
return self.target_node</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.reset"><code class="name flex">
<span>def <span class="ident">reset</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Reset all of our globals. If you add a member, add it to this method, too.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def reset(self):
&#34;&#34;&#34;Reset all of our globals. If you add a member, add it to this method, too.&#34;&#34;&#34;
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.set_args"><code class="name flex">
<span>def <span class="ident">set_args</span></span>(<span>self, args)</span>
</code></dt>
<dd>
<div class="desc"><p>Set the args</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def set_args(self, args):
&#34;&#34;&#34;Set the args&#34;&#34;&#34;
self.args = args</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.set_channel_index"><code class="name flex">
<span>def <span class="ident">set_channel_index</span></span>(<span>self, channel_index)</span>
</code></dt>
<dd>
<div class="desc"><p>Set the channel_index</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def set_channel_index(self, channel_index):
&#34;&#34;&#34;Set the channel_index&#34;&#34;&#34;
self.channel_index = channel_index</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.set_parser"><code class="name flex">
<span>def <span class="ident">set_parser</span></span>(<span>self, parser)</span>
</code></dt>
<dd>
<div class="desc"><p>Set the parser</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def set_parser(self, parser):
&#34;&#34;&#34;Set the parser&#34;&#34;&#34;
self.parser = parser</code></pre>
</details>
</dd>
<dt id="meshtastic.globals.Globals.set_target_node"><code class="name flex">
<span>def <span class="ident">set_target_node</span></span>(<span>self, target_node)</span>
</code></dt>
<dd>
<div class="desc"><p>Set the target_node</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def set_target_node(self, target_node):
&#34;&#34;&#34;Set the target_node&#34;&#34;&#34;
self.target_node = target_node</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.globals.Globals" href="#meshtastic.globals.Globals">Globals</a></code></h4>
<ul class="two-column">
<li><code><a title="meshtastic.globals.Globals.getInstance" href="#meshtastic.globals.Globals.getInstance">getInstance</a></code></li>
<li><code><a title="meshtastic.globals.Globals.get_args" href="#meshtastic.globals.Globals.get_args">get_args</a></code></li>
<li><code><a title="meshtastic.globals.Globals.get_channel_index" href="#meshtastic.globals.Globals.get_channel_index">get_channel_index</a></code></li>
<li><code><a title="meshtastic.globals.Globals.get_parser" href="#meshtastic.globals.Globals.get_parser">get_parser</a></code></li>
<li><code><a title="meshtastic.globals.Globals.get_target_node" href="#meshtastic.globals.Globals.get_target_node">get_target_node</a></code></li>
<li><code><a title="meshtastic.globals.Globals.reset" href="#meshtastic.globals.Globals.reset">reset</a></code></li>
<li><code><a title="meshtastic.globals.Globals.set_args" href="#meshtastic.globals.Globals.set_args">set_args</a></code></li>
<li><code><a title="meshtastic.globals.Globals.set_channel_index" href="#meshtastic.globals.Globals.set_channel_index">set_channel_index</a></code></li>
<li><code><a title="meshtastic.globals.Globals.set_parser" href="#meshtastic.globals.Globals.set_parser">set_parser</a></code></li>
<li><code><a title="meshtastic.globals.Globals.set_target_node" href="#meshtastic.globals.Globals.set_target_node">set_target_node</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -178,9 +178,10 @@ BROADCAST_NUM = 0xffffffff
BROADCAST_ADDR = &#34;^all&#34;
&#34;&#34;&#34;The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand
&#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
format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
&#34;&#34;&#34;
OUR_APP_VERSION = 20200
@@ -291,6 +292,10 @@ protocols = {
<dd>
<div class="desc"></div>
</dd>
<dt><code class="name"><a title="meshtastic.globals" href="globals.html">meshtastic.globals</a></code></dt>
<dd>
<div class="desc"><p>Globals singleton class …</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.mesh_interface" href="mesh_interface.html">meshtastic.mesh_interface</a></code></dt>
<dd>
<div class="desc"><p>Mesh Interface class</p></div>
@@ -339,7 +344,12 @@ protocols = {
<dd>
<div class="desc"><p>TCPInterface class for interfacing with http endpoint</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.test" href="test/index.html">meshtastic.test</a></code></dt>
<dt><code class="name"><a title="meshtastic.test" href="test.html">meshtastic.test</a></code></dt>
<dd>
<div class="desc"><p>With two radios connected serially, send and receive test
messages and report back if successful.</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests" href="tests/index.html">meshtastic.tests</a></code></dt>
<dd>
<div class="desc"></div>
</dd>
@@ -358,7 +368,8 @@ protocols = {
<dl>
<dt id="meshtastic.BROADCAST_ADDR"><code class="name">var <span class="ident">BROADCAST_ADDR</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 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>
<dt id="meshtastic.BROADCAST_NUM"><code class="name">var <span class="ident">BROADCAST_NUM</span></code></dt>
@@ -461,6 +472,7 @@ protocols = {
<li><code><a title="meshtastic.channel_pb2" href="channel_pb2.html">meshtastic.channel_pb2</a></code></li>
<li><code><a title="meshtastic.deviceonly_pb2" href="deviceonly_pb2.html">meshtastic.deviceonly_pb2</a></code></li>
<li><code><a title="meshtastic.environmental_measurement_pb2" href="environmental_measurement_pb2.html">meshtastic.environmental_measurement_pb2</a></code></li>
<li><code><a title="meshtastic.globals" href="globals.html">meshtastic.globals</a></code></li>
<li><code><a title="meshtastic.mesh_interface" href="mesh_interface.html">meshtastic.mesh_interface</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.mqtt_pb2" href="mqtt_pb2.html">meshtastic.mqtt_pb2</a></code></li>
@@ -473,7 +485,8 @@ protocols = {
<li><code><a title="meshtastic.storeforward_pb2" href="storeforward_pb2.html">meshtastic.storeforward_pb2</a></code></li>
<li><code><a title="meshtastic.stream_interface" href="stream_interface.html">meshtastic.stream_interface</a></code></li>
<li><code><a title="meshtastic.tcp_interface" href="tcp_interface.html">meshtastic.tcp_interface</a></code></li>
<li><code><a title="meshtastic.test" href="test/index.html">meshtastic.test</a></code></li>
<li><code><a title="meshtastic.test" href="test.html">meshtastic.test</a></code></li>
<li><code><a title="meshtastic.tests" href="tests/index.html">meshtastic.tests</a></code></li>
<li><code><a title="meshtastic.tunnel" href="tunnel.html">meshtastic.tunnel</a></code></li>
<li><code><a title="meshtastic.util" href="util.html">meshtastic.util</a></code></li>
</ul>

View File

@@ -68,7 +68,8 @@ class MeshInterface:
&#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.
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
@@ -82,6 +83,8 @@ class MeshInterface:
self.heartbeatTimer = None
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
self.currentPacketId = random.randint(0, 0xffffffff)
self.nodesByNum = None
self.configId = None
def close(self):
&#34;&#34;&#34;Shutdown this interface&#34;&#34;&#34;
@@ -119,54 +122,55 @@ class MeshInterface:
def showNodes(self, includeSelf=True, file=sys.stdout):
&#34;&#34;&#34;Show table summary of nodes in mesh&#34;&#34;&#34;
def formatFloat(value, precision=2, unit=&#39;&#39;):
&#34;&#34;&#34;Format a float value with precsion.&#34;&#34;&#34;
return f&#39;{value:.{precision}f}{unit}&#39; if value else None
def getLH(ts):
&#34;&#34;&#34;Format last heard&#34;&#34;&#34;
return datetime.fromtimestamp(ts).strftime(&#39;%Y-%m-%d %H:%M:%S&#39;) if ts else None
def getTimeAgo(ts):
&#34;&#34;&#34;Format how long ago have we heard from this node (aka timeago).&#34;&#34;&#34;
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
rows = []
for node in self.nodes.values():
if not includeSelf and node[&#39;num&#39;] == self.localNode.nodeNum:
continue
if self.nodes:
for node in self.nodes.values():
if not includeSelf and node[&#39;num&#39;] == self.localNode.nodeNum:
continue
row = {&#34;N&#34;: 0}
row = {&#34;N&#34;: 0}
user = node.get(&#39;user&#39;)
if user:
row.update({
&#34;User&#34;: user[&#39;longName&#39;],
&#34;AKA&#34;: user[&#39;shortName&#39;],
&#34;ID&#34;: user[&#39;id&#39;],
})
pos = node.get(&#39;position&#39;)
if pos:
row.update({
&#34;Latitude&#34;: formatFloat(pos.get(&#34;latitude&#34;), 4, &#34;°&#34;),
&#34;Longitude&#34;: formatFloat(pos.get(&#34;longitude&#34;), 4, &#34;°&#34;),
&#34;Altitude&#34;: formatFloat(pos.get(&#34;altitude&#34;), 0, &#34; m&#34;),
&#34;Battery&#34;: formatFloat(pos.get(&#34;batteryLevel&#34;), 2, &#34;%&#34;),
})
user = node.get(&#39;user&#39;)
if user:
row.update({
&#34;User&#34;: user[&#39;longName&#39;],
&#34;AKA&#34;: user[&#39;shortName&#39;],
&#34;ID&#34;: user[&#39;id&#39;],
&#34;SNR&#34;: formatFloat(node.get(&#34;snr&#34;), 2, &#34; dB&#34;),
&#34;LastHeard&#34;: getLH(node.get(&#34;lastHeard&#34;)),
&#34;Since&#34;: getTimeAgo(node.get(&#34;lastHeard&#34;)),
})
pos = node.get(&#39;position&#39;)
if pos:
row.update({
&#34;Latitude&#34;: formatFloat(pos.get(&#34;latitude&#34;), 4, &#34;°&#34;),
&#34;Longitude&#34;: formatFloat(pos.get(&#34;longitude&#34;), 4, &#34;°&#34;),
&#34;Altitude&#34;: formatFloat(pos.get(&#34;altitude&#34;), 0, &#34; m&#34;),
&#34;Battery&#34;: formatFloat(pos.get(&#34;batteryLevel&#34;), 2, &#34;%&#34;),
})
rows.append(row)
row.update({
&#34;SNR&#34;: formatFloat(node.get(&#34;snr&#34;), 2, &#34; dB&#34;),
&#34;LastHeard&#34;: getLH(node.get(&#34;lastHeard&#34;)),
&#34;Since&#34;: getTimeAgo(node.get(&#34;lastHeard&#34;)),
})
rows.append(row)
# Why doesn&#39;t this way work?
#rows.sort(key=lambda r: r.get(&#39;LastHeard&#39;, &#39;0000&#39;), reverse=True)
rows.sort(key=lambda r: r.get(&#39;LastHeard&#39;) or &#39;0000&#39;, reverse=True)
for i, row in enumerate(rows):
row[&#39;N&#39;] = i+1
table = tabulate(rows, headers=&#39;keys&#39;, missingval=&#39;N/A&#39;,
tablefmt=&#39;fancy_grid&#39;)
table = tabulate(rows, headers=&#39;keys&#39;, missingval=&#39;N/A&#39;, tablefmt=&#39;fancy_grid&#39;)
print(table)
return table
@@ -189,18 +193,24 @@ class MeshInterface:
hopLimit=defaultHopLimit,
onResponse=None,
channelIndex=0):
&#34;&#34;&#34;Send a utf8 string to some other node, if the node has a display it will also be shown on the device.
&#34;&#34;&#34;Send a utf8 string to some other node, if the node has a display it
will also be shown on the device.
Arguments:
text {string} -- The text to send
Keyword Arguments:
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other side to send an application layer response
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers)
of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable manner
(with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other side to
send an application layer response
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
&#34;&#34;&#34;
return self.sendData(text.encode(&#34;utf-8&#34;), destinationId,
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
@@ -219,15 +229,23 @@ class MeshInterface:
&#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)
wantResponse -- True if you want the service on the other side to send an application layer response
onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives
(or the transaction is NAKed due to non receipt)
data -- the data to send, either as an array of bytes or
as a protobuf (which will be automatically
serialized to bytes)
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers)
of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable
manner (with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other
side to send an application layer response
onResponse -- A closure of the form funct(packet), that will be
called when a response packet arrives (or the transaction
is NAKed due to non receipt)
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
&#34;&#34;&#34;
if getattr(data, &#34;SerializeToString&#34;, None):
logging.debug(f&#34;Serializing protobuf as data: {stripnl(data)}&#34;)
@@ -251,16 +269,18 @@ class MeshInterface:
self._addResponseHandler(p.id, onResponse)
return p
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
&#34;&#34;&#34;
Send a position packet to some other node (normally a broadcast)
Also, the device software will notice this packet and use it to automatically set its notion of
the local position.
Also, the device software will notice this packet and use it to automatically
set its notion of the local position.
If timeSec is not specified (recommended), we will use the local machine time.
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet and
can be used to track future message acks/naks.
&#34;&#34;&#34;
p = mesh_pb2.Position()
if latitude != 0.0:
@@ -321,8 +341,8 @@ class MeshInterface:
meshPacket.want_ack = wantAck
meshPacket.hop_limit = hopLimit
# if the user hasn&#39;t set an ID for this packet (likely and recommended), we should pick a new unique ID
# so the message can be tracked.
# if the user hasn&#39;t set an ID for this packet (likely and recommended),
# we should pick a new unique ID so the message can be tracked.
if meshPacket.id == 0:
meshPacket.id = self._generatePacketId()
@@ -333,8 +353,7 @@ class MeshInterface:
def waitForConfig(self):
&#34;&#34;&#34;Block until radio config is received. Returns True if config has been received.&#34;&#34;&#34;
success = self._timeout.waitForSet(self, attrs=(&#39;myInfo&#39;, &#39;nodes&#39;)
) and self.localNode.waitForConfig()
success = self._timeout.waitForSet(self, attrs=(&#39;myInfo&#39;, &#39;nodes&#39;)) and self.localNode.waitForConfig()
if not success:
raise Exception(&#34;Timed out waiting for interface config&#34;)
@@ -436,8 +455,7 @@ class MeshInterface:
def _sendToRadio(self, toRadio):
&#34;&#34;&#34;Send a ToRadio protobuf to the device&#34;&#34;&#34;
if self.noProto:
logging.warning(
f&#34;Not sending packet because protocol use is disabled by noProto&#34;)
logging.warning(f&#34;Not sending packet because protocol use is disabled by noProto&#34;)
else:
#logging.debug(f&#34;Sending toRadio: {stripnl(toRadio)}&#34;)
self._sendToRadioImpl(toRadio)
@@ -448,7 +466,8 @@ class MeshInterface:
def _handleConfigComplete(self):
&#34;&#34;&#34;
Done with initial config messages, now send regular MeshPackets to ask for settings and channels
Done with initial config messages, now send regular MeshPackets
to ask for settings and channels
&#34;&#34;&#34;
self.localNode.requestConfig()
@@ -469,7 +488,7 @@ class MeshInterface:
failmsg = None
# Check for app too old
if self.myInfo.min_app_version &gt; OUR_APP_VERSION:
failmsg = &#34;This device needs a newer python client, please \&#34;pip install --upgrade meshtastic\&#34;. &#34;\
failmsg = &#34;This device needs a newer python client, run &#39;pip install --upgrade meshtastic&#39;.&#34;\
&#34;For more information see https://tinyurl.com/5bjsxu32&#34;
# check for firmware too old
@@ -497,13 +516,15 @@ class MeshInterface:
publishingThread.queueWork(lambda: pub.sendMessage(&#34;meshtastic.node.updated&#34;,
node=node, interface=self))
elif fromRadio.config_complete_id == self.configId:
# we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id
# we ignore the config_complete_id, it is unneeded for our
# stream API fromRadio.config_complete_id
logging.debug(f&#34;Config complete ID {self.configId}&#34;)
self._handleConfigComplete()
elif fromRadio.HasField(&#34;packet&#34;):
self._handlePacketFromRadio(fromRadio.packet)
elif fromRadio.rebooted:
# Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port
# Tell clients the device went away. Careful not to call the overridden
# subclass version that closes the serial port
MeshInterface._disconnected(self)
self._startConfig() # redownload the node db etc...
@@ -569,12 +590,12 @@ class MeshInterface:
asDict[&#34;raw&#34;] = meshPacket
# from might be missing if the nodenum was zero.
if not &#34;from&#34; in asDict:
if &#34;from&#34; not in asDict:
asDict[&#34;from&#34;] = 0
logging.error(
f&#34;Device returned a packet we sent, ignoring: {stripnl(asDict)}&#34;)
return
if not &#34;to&#34; in asDict:
if &#34;to&#34; not in asDict:
asDict[&#34;to&#34;] = 0
# /add fromId and toId fields based on the node ID
@@ -597,8 +618,9 @@ class MeshInterface:
# byte array.
decoded[&#34;payload&#34;] = meshPacket.decoded.payload
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not set it will not be populated at all
# to make API usage easier, set it to prevent confusion
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not
# set it will not be populated at all to make API usage easier, set
# it to prevent confusion
if not &#34;portnum&#34; in decoded:
decoded[&#34;portnum&#34;] = portnums_pb2.PortNum.Name(
portnums_pb2.PortNum.UNKNOWN_APP)
@@ -607,8 +629,9 @@ class MeshInterface:
topic = f&#34;meshtastic.receive.data.{portnum}&#34;
# decode position protobufs and update nodedb, provide decoded version as &#34;position&#34; in the published msg
# move the following into a &#39;decoders&#39; API that clients could register?
# decode position protobufs and update nodedb, provide decoded version
# as &#34;position&#34; in the published msg move the following into a &#39;decoders&#39;
# API that clients could register?
portNumInt = meshPacket.decoded.portnum # we want portnum as an int
handler = protocols.get(portNumInt)
# The decoded protobuf as a dictionary (if we understand this message)
@@ -667,7 +690,8 @@ nodes
debugOut</p>
<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>
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>
@@ -686,7 +710,8 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
&#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.
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
@@ -700,6 +725,8 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
self.heartbeatTimer = None
random.seed() # FIXME, we should not clobber the random seedval here, instead tell user they must call it
self.currentPacketId = random.randint(0, 0xffffffff)
self.nodesByNum = None
self.configId = None
def close(self):
&#34;&#34;&#34;Shutdown this interface&#34;&#34;&#34;
@@ -737,54 +764,55 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
def showNodes(self, includeSelf=True, file=sys.stdout):
&#34;&#34;&#34;Show table summary of nodes in mesh&#34;&#34;&#34;
def formatFloat(value, precision=2, unit=&#39;&#39;):
&#34;&#34;&#34;Format a float value with precsion.&#34;&#34;&#34;
return f&#39;{value:.{precision}f}{unit}&#39; if value else None
def getLH(ts):
&#34;&#34;&#34;Format last heard&#34;&#34;&#34;
return datetime.fromtimestamp(ts).strftime(&#39;%Y-%m-%d %H:%M:%S&#39;) if ts else None
def getTimeAgo(ts):
&#34;&#34;&#34;Format how long ago have we heard from this node (aka timeago).&#34;&#34;&#34;
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
rows = []
for node in self.nodes.values():
if not includeSelf and node[&#39;num&#39;] == self.localNode.nodeNum:
continue
if self.nodes:
for node in self.nodes.values():
if not includeSelf and node[&#39;num&#39;] == self.localNode.nodeNum:
continue
row = {&#34;N&#34;: 0}
row = {&#34;N&#34;: 0}
user = node.get(&#39;user&#39;)
if user:
row.update({
&#34;User&#34;: user[&#39;longName&#39;],
&#34;AKA&#34;: user[&#39;shortName&#39;],
&#34;ID&#34;: user[&#39;id&#39;],
})
pos = node.get(&#39;position&#39;)
if pos:
row.update({
&#34;Latitude&#34;: formatFloat(pos.get(&#34;latitude&#34;), 4, &#34;°&#34;),
&#34;Longitude&#34;: formatFloat(pos.get(&#34;longitude&#34;), 4, &#34;°&#34;),
&#34;Altitude&#34;: formatFloat(pos.get(&#34;altitude&#34;), 0, &#34; m&#34;),
&#34;Battery&#34;: formatFloat(pos.get(&#34;batteryLevel&#34;), 2, &#34;%&#34;),
})
user = node.get(&#39;user&#39;)
if user:
row.update({
&#34;User&#34;: user[&#39;longName&#39;],
&#34;AKA&#34;: user[&#39;shortName&#39;],
&#34;ID&#34;: user[&#39;id&#39;],
&#34;SNR&#34;: formatFloat(node.get(&#34;snr&#34;), 2, &#34; dB&#34;),
&#34;LastHeard&#34;: getLH(node.get(&#34;lastHeard&#34;)),
&#34;Since&#34;: getTimeAgo(node.get(&#34;lastHeard&#34;)),
})
pos = node.get(&#39;position&#39;)
if pos:
row.update({
&#34;Latitude&#34;: formatFloat(pos.get(&#34;latitude&#34;), 4, &#34;°&#34;),
&#34;Longitude&#34;: formatFloat(pos.get(&#34;longitude&#34;), 4, &#34;°&#34;),
&#34;Altitude&#34;: formatFloat(pos.get(&#34;altitude&#34;), 0, &#34; m&#34;),
&#34;Battery&#34;: formatFloat(pos.get(&#34;batteryLevel&#34;), 2, &#34;%&#34;),
})
rows.append(row)
row.update({
&#34;SNR&#34;: formatFloat(node.get(&#34;snr&#34;), 2, &#34; dB&#34;),
&#34;LastHeard&#34;: getLH(node.get(&#34;lastHeard&#34;)),
&#34;Since&#34;: getTimeAgo(node.get(&#34;lastHeard&#34;)),
})
rows.append(row)
# Why doesn&#39;t this way work?
#rows.sort(key=lambda r: r.get(&#39;LastHeard&#39;, &#39;0000&#39;), reverse=True)
rows.sort(key=lambda r: r.get(&#39;LastHeard&#39;) or &#39;0000&#39;, reverse=True)
for i, row in enumerate(rows):
row[&#39;N&#39;] = i+1
table = tabulate(rows, headers=&#39;keys&#39;, missingval=&#39;N/A&#39;,
tablefmt=&#39;fancy_grid&#39;)
table = tabulate(rows, headers=&#39;keys&#39;, missingval=&#39;N/A&#39;, tablefmt=&#39;fancy_grid&#39;)
print(table)
return table
@@ -807,18 +835,24 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
hopLimit=defaultHopLimit,
onResponse=None,
channelIndex=0):
&#34;&#34;&#34;Send a utf8 string to some other node, if the node has a display it will also be shown on the device.
&#34;&#34;&#34;Send a utf8 string to some other node, if the node has a display it
will also be shown on the device.
Arguments:
text {string} -- The text to send
Keyword Arguments:
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other side to send an application layer response
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers)
of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable manner
(with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other side to
send an application layer response
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
&#34;&#34;&#34;
return self.sendData(text.encode(&#34;utf-8&#34;), destinationId,
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
@@ -837,15 +871,23 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
&#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)
wantResponse -- True if you want the service on the other side to send an application layer response
onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives
(or the transaction is NAKed due to non receipt)
data -- the data to send, either as an array of bytes or
as a protobuf (which will be automatically
serialized to bytes)
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers)
of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable
manner (with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other
side to send an application layer response
onResponse -- A closure of the form funct(packet), that will be
called when a response packet arrives (or the transaction
is NAKed due to non receipt)
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
&#34;&#34;&#34;
if getattr(data, &#34;SerializeToString&#34;, None):
logging.debug(f&#34;Serializing protobuf as data: {stripnl(data)}&#34;)
@@ -869,16 +911,18 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
self._addResponseHandler(p.id, onResponse)
return p
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
&#34;&#34;&#34;
Send a position packet to some other node (normally a broadcast)
Also, the device software will notice this packet and use it to automatically set its notion of
the local position.
Also, the device software will notice this packet and use it to automatically
set its notion of the local position.
If timeSec is not specified (recommended), we will use the local machine time.
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet and
can be used to track future message acks/naks.
&#34;&#34;&#34;
p = mesh_pb2.Position()
if latitude != 0.0:
@@ -939,8 +983,8 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
meshPacket.want_ack = wantAck
meshPacket.hop_limit = hopLimit
# if the user hasn&#39;t set an ID for this packet (likely and recommended), we should pick a new unique ID
# so the message can be tracked.
# if the user hasn&#39;t set an ID for this packet (likely and recommended),
# we should pick a new unique ID so the message can be tracked.
if meshPacket.id == 0:
meshPacket.id = self._generatePacketId()
@@ -951,8 +995,7 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
def waitForConfig(self):
&#34;&#34;&#34;Block until radio config is received. Returns True if config has been received.&#34;&#34;&#34;
success = self._timeout.waitForSet(self, attrs=(&#39;myInfo&#39;, &#39;nodes&#39;)
) and self.localNode.waitForConfig()
success = self._timeout.waitForSet(self, attrs=(&#39;myInfo&#39;, &#39;nodes&#39;)) and self.localNode.waitForConfig()
if not success:
raise Exception(&#34;Timed out waiting for interface config&#34;)
@@ -1054,8 +1097,7 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
def _sendToRadio(self, toRadio):
&#34;&#34;&#34;Send a ToRadio protobuf to the device&#34;&#34;&#34;
if self.noProto:
logging.warning(
f&#34;Not sending packet because protocol use is disabled by noProto&#34;)
logging.warning(f&#34;Not sending packet because protocol use is disabled by noProto&#34;)
else:
#logging.debug(f&#34;Sending toRadio: {stripnl(toRadio)}&#34;)
self._sendToRadioImpl(toRadio)
@@ -1066,7 +1108,8 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
def _handleConfigComplete(self):
&#34;&#34;&#34;
Done with initial config messages, now send regular MeshPackets to ask for settings and channels
Done with initial config messages, now send regular MeshPackets
to ask for settings and channels
&#34;&#34;&#34;
self.localNode.requestConfig()
@@ -1087,7 +1130,7 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
failmsg = None
# Check for app too old
if self.myInfo.min_app_version &gt; OUR_APP_VERSION:
failmsg = &#34;This device needs a newer python client, please \&#34;pip install --upgrade meshtastic\&#34;. &#34;\
failmsg = &#34;This device needs a newer python client, run &#39;pip install --upgrade meshtastic&#39;.&#34;\
&#34;For more information see https://tinyurl.com/5bjsxu32&#34;
# check for firmware too old
@@ -1115,13 +1158,15 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
publishingThread.queueWork(lambda: pub.sendMessage(&#34;meshtastic.node.updated&#34;,
node=node, interface=self))
elif fromRadio.config_complete_id == self.configId:
# we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id
# we ignore the config_complete_id, it is unneeded for our
# stream API fromRadio.config_complete_id
logging.debug(f&#34;Config complete ID {self.configId}&#34;)
self._handleConfigComplete()
elif fromRadio.HasField(&#34;packet&#34;):
self._handlePacketFromRadio(fromRadio.packet)
elif fromRadio.rebooted:
# Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port
# Tell clients the device went away. Careful not to call the overridden
# subclass version that closes the serial port
MeshInterface._disconnected(self)
self._startConfig() # redownload the node db etc...
@@ -1187,12 +1232,12 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
asDict[&#34;raw&#34;] = meshPacket
# from might be missing if the nodenum was zero.
if not &#34;from&#34; in asDict:
if &#34;from&#34; not in asDict:
asDict[&#34;from&#34;] = 0
logging.error(
f&#34;Device returned a packet we sent, ignoring: {stripnl(asDict)}&#34;)
return
if not &#34;to&#34; in asDict:
if &#34;to&#34; not in asDict:
asDict[&#34;to&#34;] = 0
# /add fromId and toId fields based on the node ID
@@ -1215,8 +1260,9 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
# byte array.
decoded[&#34;payload&#34;] = meshPacket.decoded.payload
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not set it will not be populated at all
# to make API usage easier, set it to prevent confusion
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not
# set it will not be populated at all to make API usage easier, set
# it to prevent confusion
if not &#34;portnum&#34; in decoded:
decoded[&#34;portnum&#34;] = portnums_pb2.PortNum.Name(
portnums_pb2.PortNum.UNKNOWN_APP)
@@ -1225,8 +1271,9 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
topic = f&#34;meshtastic.receive.data.{portnum}&#34;
# decode position protobufs and update nodedb, provide decoded version as &#34;position&#34; in the published msg
# move the following into a &#39;decoders&#39; API that clients could register?
# decode position protobufs and update nodedb, provide decoded version
# as &#34;position&#34; in the published msg move the following into a &#39;decoders&#39;
# API that clients could register?
portNumInt = meshPacket.decoded.portnum # we want portnum as an int
handler = protocols.get(portNumInt)
# The decoded protobuf as a dictionary (if we understand this message)
@@ -1381,14 +1428,22 @@ noProto &ndash; If True, don't try to run our protocol on the link - just be a d
<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)
wantResponse &ndash; True if you want the service on the other side to send an application layer response
onResponse &ndash; A closure of the form funct(packet), that will be called when a response packet arrives
(or the transaction is NAKed due to non receipt)</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>
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)
wantResponse &ndash; True if you want the service on the other
side to send an application layer response
onResponse &ndash; A closure of the form funct(packet), that will be
called when a response packet arrives (or the transaction
is NAKed due to non receipt)</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>
@@ -1402,15 +1457,23 @@ onResponse &ndash; A closure of the form funct(packet), that will be called when
&#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)
wantResponse -- True if you want the service on the other side to send an application layer response
onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives
(or the transaction is NAKed due to non receipt)
data -- the data to send, either as an array of bytes or
as a protobuf (which will be automatically
serialized to bytes)
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers)
of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable
manner (with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other
side to send an application layer response
onResponse -- A closure of the form funct(packet), that will be
called when a response packet arrives (or the transaction
is NAKed due to non receipt)
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
&#34;&#34;&#34;
if getattr(data, &#34;SerializeToString&#34;, None):
logging.debug(f&#34;Serializing protobuf as data: {stripnl(data)}&#34;)
@@ -1440,24 +1503,27 @@ onResponse &ndash; A closure of the form funct(packet), that will be called when
</code></dt>
<dd>
<div class="desc"><p>Send a position packet to some other node (normally a broadcast)</p>
<p>Also, the device software will notice this packet and use it to automatically set its notion of
the local position.</p>
<p>Also, the device software will notice this packet and use it to automatically
set its notion of the local position.</p>
<p>If timeSec is not specified (recommended), we will use the local machine time.</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>
<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 sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
<pre><code class="python">def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False):
&#34;&#34;&#34;
Send a position packet to some other node (normally a broadcast)
Also, the device software will notice this packet and use it to automatically set its notion of
the local position.
Also, the device software will notice this packet and use it to automatically
set its notion of the local position.
If timeSec is not specified (recommended), we will use the local machine time.
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet and
can be used to track future message acks/naks.
&#34;&#34;&#34;
p = mesh_pb2.Position()
if latitude != 0.0:
@@ -1483,15 +1549,21 @@ the local position.</p>
<span>def <span class="ident">sendText</span></span>(<span>self, text: ~AnyStr, destinationId='^all', wantAck=False, wantResponse=False, hopLimit=3, onResponse=None, channelIndex=0)</span>
</code></dt>
<dd>
<div class="desc"><p>Send a utf8 string to some other node, if the node has a display it will also be shown on the device.</p>
<div class="desc"><p>Send a utf8 string to some other node, if the node has a display it
will also be shown on the device.</p>
<h2 id="arguments">Arguments</h2>
<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)
wantResponse &ndash; True if you want the service on the other side to send an application layer response</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>
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)
wantResponse &ndash; True if you want the service on the other side to
send an application layer response</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>
@@ -1503,18 +1575,24 @@ wantResponse &ndash; True if you want the service on the other side to send an a
hopLimit=defaultHopLimit,
onResponse=None,
channelIndex=0):
&#34;&#34;&#34;Send a utf8 string to some other node, if the node has a display it will also be shown on the device.
&#34;&#34;&#34;Send a utf8 string to some other node, if the node has a display it
will also be shown on the device.
Arguments:
text {string} -- The text to send
Keyword Arguments:
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other side to send an application layer response
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
portNum -- the application portnum (similar to IP port numbers)
of the destination, see portnums.proto for a list
wantAck -- True if you want the message sent in a reliable manner
(with retries and ack/nak provided for delivery)
wantResponse -- True if you want the service on the other side to
send an application layer response
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
&#34;&#34;&#34;
return self.sendData(text.encode(&#34;utf-8&#34;), destinationId,
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
@@ -1562,54 +1640,55 @@ wantResponse &ndash; True if you want the service on the other side to send an a
<pre><code class="python">def showNodes(self, includeSelf=True, file=sys.stdout):
&#34;&#34;&#34;Show table summary of nodes in mesh&#34;&#34;&#34;
def formatFloat(value, precision=2, unit=&#39;&#39;):
&#34;&#34;&#34;Format a float value with precsion.&#34;&#34;&#34;
return f&#39;{value:.{precision}f}{unit}&#39; if value else None
def getLH(ts):
&#34;&#34;&#34;Format last heard&#34;&#34;&#34;
return datetime.fromtimestamp(ts).strftime(&#39;%Y-%m-%d %H:%M:%S&#39;) if ts else None
def getTimeAgo(ts):
&#34;&#34;&#34;Format how long ago have we heard from this node (aka timeago).&#34;&#34;&#34;
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
rows = []
for node in self.nodes.values():
if not includeSelf and node[&#39;num&#39;] == self.localNode.nodeNum:
continue
if self.nodes:
for node in self.nodes.values():
if not includeSelf and node[&#39;num&#39;] == self.localNode.nodeNum:
continue
row = {&#34;N&#34;: 0}
row = {&#34;N&#34;: 0}
user = node.get(&#39;user&#39;)
if user:
row.update({
&#34;User&#34;: user[&#39;longName&#39;],
&#34;AKA&#34;: user[&#39;shortName&#39;],
&#34;ID&#34;: user[&#39;id&#39;],
})
pos = node.get(&#39;position&#39;)
if pos:
row.update({
&#34;Latitude&#34;: formatFloat(pos.get(&#34;latitude&#34;), 4, &#34;°&#34;),
&#34;Longitude&#34;: formatFloat(pos.get(&#34;longitude&#34;), 4, &#34;°&#34;),
&#34;Altitude&#34;: formatFloat(pos.get(&#34;altitude&#34;), 0, &#34; m&#34;),
&#34;Battery&#34;: formatFloat(pos.get(&#34;batteryLevel&#34;), 2, &#34;%&#34;),
})
user = node.get(&#39;user&#39;)
if user:
row.update({
&#34;User&#34;: user[&#39;longName&#39;],
&#34;AKA&#34;: user[&#39;shortName&#39;],
&#34;ID&#34;: user[&#39;id&#39;],
&#34;SNR&#34;: formatFloat(node.get(&#34;snr&#34;), 2, &#34; dB&#34;),
&#34;LastHeard&#34;: getLH(node.get(&#34;lastHeard&#34;)),
&#34;Since&#34;: getTimeAgo(node.get(&#34;lastHeard&#34;)),
})
pos = node.get(&#39;position&#39;)
if pos:
row.update({
&#34;Latitude&#34;: formatFloat(pos.get(&#34;latitude&#34;), 4, &#34;°&#34;),
&#34;Longitude&#34;: formatFloat(pos.get(&#34;longitude&#34;), 4, &#34;°&#34;),
&#34;Altitude&#34;: formatFloat(pos.get(&#34;altitude&#34;), 0, &#34; m&#34;),
&#34;Battery&#34;: formatFloat(pos.get(&#34;batteryLevel&#34;), 2, &#34;%&#34;),
})
rows.append(row)
row.update({
&#34;SNR&#34;: formatFloat(node.get(&#34;snr&#34;), 2, &#34; dB&#34;),
&#34;LastHeard&#34;: getLH(node.get(&#34;lastHeard&#34;)),
&#34;Since&#34;: getTimeAgo(node.get(&#34;lastHeard&#34;)),
})
rows.append(row)
# Why doesn&#39;t this way work?
#rows.sort(key=lambda r: r.get(&#39;LastHeard&#39;, &#39;0000&#39;), reverse=True)
rows.sort(key=lambda r: r.get(&#39;LastHeard&#39;) or &#39;0000&#39;, reverse=True)
for i, row in enumerate(rows):
row[&#39;N&#39;] = i+1
table = tabulate(rows, headers=&#39;keys&#39;, missingval=&#39;N/A&#39;,
tablefmt=&#39;fancy_grid&#39;)
table = tabulate(rows, headers=&#39;keys&#39;, missingval=&#39;N/A&#39;, tablefmt=&#39;fancy_grid&#39;)
print(table)
return table</code></pre>
</details>
@@ -1625,8 +1704,7 @@ wantResponse &ndash; True if you want the service on the other side to send an a
</summary>
<pre><code class="python">def waitForConfig(self):
&#34;&#34;&#34;Block until radio config is received. Returns True if config has been received.&#34;&#34;&#34;
success = self._timeout.waitForSet(self, attrs=(&#39;myInfo&#39;, &#39;nodes&#39;)
) and self.localNode.waitForConfig()
success = self._timeout.waitForSet(self, attrs=(&#39;myInfo&#39;, &#39;nodes&#39;)) and self.localNode.waitForConfig()
if not success:
raise Exception(&#34;Timed out waiting for interface config&#34;)</code></pre>
</details>

View File

@@ -27,14 +27,14 @@
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; Node class
<pre><code class="python">&#34;&#34;&#34;Node class
&#34;&#34;&#34;
import logging
import base64
from google.protobuf.json_format import MessageToJson
from . import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
from .util import pskToString, stripnl, Timeout, our_exit
from .util import pskToString, stripnl, Timeout, our_exit, fromPSK
class Node:
@@ -50,14 +50,16 @@ class Node:
self.radioConfig = None
self.channels = None
self._timeout = Timeout(maxSecs=60)
self.partialChannels = None
def showChannels(self):
&#34;&#34;&#34;Show human readable description of our channels&#34;&#34;&#34;
&#34;&#34;&#34;Show human readable description of our channels.&#34;&#34;&#34;
print(&#34;Channels:&#34;)
if self.channels:
for c in self.channels:
if c.role != channel_pb2.Channel.Role.DISABLED:
cStr = stripnl(MessageToJson(c.settings))
cStr = stripnl(MessageToJson(c.settings))
# only show if there is no psk (meaning disabled channel)
if c.settings.psk:
print(f&#34; {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}&#34;)
publicURL = self.getURL(includeAll=False)
adminURL = self.getURL(includeAll=True)
@@ -74,15 +76,19 @@ class Node:
self.showChannels()
def requestConfig(self):
&#34;&#34;&#34;
Send regular MeshPackets to ask for settings and channels
&#34;&#34;&#34;
&#34;&#34;&#34;Send regular MeshPackets to ask for settings and channels.&#34;&#34;&#34;
self.radioConfig = None
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
self._requestSettings()
def turnOffEncryptionOnPrimaryChannel(self):
&#34;&#34;&#34;Turn off encryption on primary channel.&#34;&#34;&#34;
self.channels[0].settings.psk = fromPSK(&#34;none&#34;)
print(&#34;Writing modified channels to device&#34;)
self.writeChannel(0)
def waitForConfig(self):
&#34;&#34;&#34;Block until radio config is received. Returns True if config has been received.&#34;&#34;&#34;
return self._timeout.waitForSet(self, attrs=(&#39;radioConfig&#39;, &#39;channels&#39;))
@@ -110,7 +116,7 @@ class Node:
def deleteChannel(self, channelIndex):
&#34;&#34;&#34;Delete the specifed channelIndex and shift other channels up&#34;&#34;&#34;
ch = self.channels[channelIndex]
if ch.role != channel_pb2.Channel.Role.SECONDARY:
if ch.role not in (channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED):
our_exit(&#34;Warning: Only SECONDARY channels can be deleted&#34;)
# we are careful here because if we move the &#34;admin&#34; channel the channelIndex we need to use
@@ -125,9 +131,11 @@ class Node:
self.writeChannel(index, adminIndex=adminIndex)
index += 1
# if we are updating the local node, we might end up *moving* the admin channel index as we are writing
# if we are updating the local node, we might end up
# *moving* the admin channel index as we are writing
if (self.iface.localNode == self) and index &gt;= adminIndex:
# We&#39;ve now passed the old location for admin index (and writen it), so we can start finding it by name again
# We&#39;ve now passed the old location for admin index
# (and writen it), so we can start finding it by name again
adminIndex = 0
def getChannelByName(self, name):
@@ -186,16 +194,15 @@ class Node:
return self._sendAdmin(p)
def getURL(self, includeAll: bool = True):
&#34;&#34;&#34;The sharable URL that describes the current channel
&#34;&#34;&#34;
&#34;&#34;&#34;The sharable URL that describes the current channel&#34;&#34;&#34;
# Only keep the primary/secondary channels, assume primary is first
channelSet = apponly_pb2.ChannelSet()
if self.channels:
for c in self.channels:
if c.role == channel_pb2.Channel.Role.PRIMARY or (includeAll and c.role == channel_pb2.Channel.Role.SECONDARY):
channelSet.settings.append(c.settings)
bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(bytes).decode(&#39;ascii&#39;)
some_bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(some_bytes).decode(&#39;ascii&#39;)
return f&#34;https://www.meshtastic.org/d/#{s}&#34;.replace(&#34;=&#34;, &#34;&#34;)
def setURL(self, url):
@@ -233,41 +240,40 @@ class Node:
i = i + 1
def _requestSettings(self):
&#34;&#34;&#34;
Done with initial config messages, now send regular MeshPackets to ask for settings
&#34;&#34;&#34;
&#34;&#34;&#34;Done with initial config messages, now send regular
MeshPackets to ask for settings.&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.get_radio_request = True
def onResponse(p):
&#34;&#34;&#34;A closure to handle the response packet&#34;&#34;&#34;
self.radioConfig = p[&#34;decoded&#34;][&#34;admin&#34;][&#34;raw&#34;].get_radio_response
logging.debug(&#34;Received radio config, now fetching channels...&#34;)
self._timeout.reset() # We made foreward progress
self._requestChannel(0) # now start fetching channels
errorFound = False
if &#39;routing&#39; in p[&#34;decoded&#34;]:
if p[&#34;decoded&#34;][&#34;routing&#34;][&#34;errorReason&#34;] != &#34;NONE&#34;:
errorFound = True
print(f&#39;Error on response: {p[&#34;decoded&#34;][&#34;routing&#34;][&#34;errorReason&#34;]}&#39;)
if errorFound is False:
self.radioConfig = p[&#34;decoded&#34;][&#34;admin&#34;][&#34;raw&#34;].get_radio_response
logging.debug(&#34;Received radio config, now fetching channels...&#34;)
self._timeout.reset() # We made foreward progress
self._requestChannel(0) # now start fetching channels
# Show progress message for super slow operations
if self != self.iface.localNode:
logging.info(
&#34;Requesting preferences from remote node (this could take a while)&#34;)
print(&#34;Requesting preferences from remote node (this could take a while)&#34;)
return self._sendAdmin(p,
wantResponse=True,
onResponse=onResponse)
return self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
def exitSimulator(self):
&#34;&#34;&#34;
Tell a simulator node to exit (this message is ignored for other nodes)
&#34;&#34;&#34;
&#34;&#34;&#34;Tell a simulator node to exit (this message
is ignored for other nodes)&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.exit_simulator = True
return self._sendAdmin(p)
def reboot(self, secs: int = 10):
&#34;&#34;&#34;
Tell the node to reboot
&#34;&#34;&#34;
&#34;&#34;&#34;Tell the node to reboot.&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.reboot_seconds = secs
logging.info(f&#34;Telling node to reboot in {secs} seconds&#34;)
@@ -278,6 +284,7 @@ class Node:
&#34;&#34;&#34;Fixup indexes and add disabled channels as needed&#34;&#34;&#34;
# Add extra disabled channels as needed
# TODO: These 2 lines seem to not do anything.
for index, ch in enumerate(self.channels):
ch.index = index # fixup indexes
@@ -296,21 +303,19 @@ class Node:
index += 1
def _requestChannel(self, channelNum: int):
&#34;&#34;&#34;
Done with initial config messages, now send regular MeshPackets to ask for settings
&#34;&#34;&#34;
&#34;&#34;&#34;Done with initial config messages, now send regular
MeshPackets to ask for settings&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.get_channel_request = channelNum + 1
# Show progress message for super slow operations
if self != self.iface.localNode:
logging.info(
f&#34;Requesting channel {channelNum} info from remote node (this could take a while)&#34;)
logging.info(f&#34;Requesting channel {channelNum} info from remote node (this could take a while)&#34;)
else:
logging.debug(f&#34;Requesting channel {channelNum}&#34;)
def onResponse(p):
&#34;&#34;&#34;A closure to handle the response packet&#34;&#34;&#34;
&#34;&#34;&#34;A closure to handle the response packet for requesting a channel&#34;&#34;&#34;
c = p[&#34;decoded&#34;][&#34;admin&#34;][&#34;raw&#34;].get_channel_response
self.partialChannels.append(c)
self._timeout.reset() # We made foreward progress
@@ -320,9 +325,9 @@ class Node:
# for stress testing, we can always download all channels
fastChannelDownload = True
# Once we see a response that has NO settings, assume we are at the end of channels and stop fetching
quitEarly = (
c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload
# Once we see a response that has NO settings, assume
# we are at the end of channels and stop fetching
quitEarly = (c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload
if quitEarly or index &gt;= self.iface.myInfo.max_channels - 1:
logging.debug(&#34;Finished downloading channels&#34;)
@@ -335,13 +340,10 @@ class Node:
else:
self._requestChannel(index + 1)
return self._sendAdmin(p,
wantResponse=True,
onResponse=onResponse)
return self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False,
onResponse=None,
adminIndex=0):
onResponse=None, adminIndex=0):
&#34;&#34;&#34;Send an admin message to the specified node (or the local node if destNodeNum is zero)&#34;&#34;&#34;
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
@@ -389,14 +391,16 @@ class Node:
self.radioConfig = None
self.channels = None
self._timeout = Timeout(maxSecs=60)
self.partialChannels = None
def showChannels(self):
&#34;&#34;&#34;Show human readable description of our channels&#34;&#34;&#34;
&#34;&#34;&#34;Show human readable description of our channels.&#34;&#34;&#34;
print(&#34;Channels:&#34;)
if self.channels:
for c in self.channels:
if c.role != channel_pb2.Channel.Role.DISABLED:
cStr = stripnl(MessageToJson(c.settings))
cStr = stripnl(MessageToJson(c.settings))
# only show if there is no psk (meaning disabled channel)
if c.settings.psk:
print(f&#34; {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}&#34;)
publicURL = self.getURL(includeAll=False)
adminURL = self.getURL(includeAll=True)
@@ -413,15 +417,19 @@ class Node:
self.showChannels()
def requestConfig(self):
&#34;&#34;&#34;
Send regular MeshPackets to ask for settings and channels
&#34;&#34;&#34;
&#34;&#34;&#34;Send regular MeshPackets to ask for settings and channels.&#34;&#34;&#34;
self.radioConfig = None
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
self._requestSettings()
def turnOffEncryptionOnPrimaryChannel(self):
&#34;&#34;&#34;Turn off encryption on primary channel.&#34;&#34;&#34;
self.channels[0].settings.psk = fromPSK(&#34;none&#34;)
print(&#34;Writing modified channels to device&#34;)
self.writeChannel(0)
def waitForConfig(self):
&#34;&#34;&#34;Block until radio config is received. Returns True if config has been received.&#34;&#34;&#34;
return self._timeout.waitForSet(self, attrs=(&#39;radioConfig&#39;, &#39;channels&#39;))
@@ -449,7 +457,7 @@ class Node:
def deleteChannel(self, channelIndex):
&#34;&#34;&#34;Delete the specifed channelIndex and shift other channels up&#34;&#34;&#34;
ch = self.channels[channelIndex]
if ch.role != channel_pb2.Channel.Role.SECONDARY:
if ch.role not in (channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED):
our_exit(&#34;Warning: Only SECONDARY channels can be deleted&#34;)
# we are careful here because if we move the &#34;admin&#34; channel the channelIndex we need to use
@@ -464,9 +472,11 @@ class Node:
self.writeChannel(index, adminIndex=adminIndex)
index += 1
# if we are updating the local node, we might end up *moving* the admin channel index as we are writing
# if we are updating the local node, we might end up
# *moving* the admin channel index as we are writing
if (self.iface.localNode == self) and index &gt;= adminIndex:
# We&#39;ve now passed the old location for admin index (and writen it), so we can start finding it by name again
# We&#39;ve now passed the old location for admin index
# (and writen it), so we can start finding it by name again
adminIndex = 0
def getChannelByName(self, name):
@@ -525,16 +535,15 @@ class Node:
return self._sendAdmin(p)
def getURL(self, includeAll: bool = True):
&#34;&#34;&#34;The sharable URL that describes the current channel
&#34;&#34;&#34;
&#34;&#34;&#34;The sharable URL that describes the current channel&#34;&#34;&#34;
# Only keep the primary/secondary channels, assume primary is first
channelSet = apponly_pb2.ChannelSet()
if self.channels:
for c in self.channels:
if c.role == channel_pb2.Channel.Role.PRIMARY or (includeAll and c.role == channel_pb2.Channel.Role.SECONDARY):
channelSet.settings.append(c.settings)
bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(bytes).decode(&#39;ascii&#39;)
some_bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(some_bytes).decode(&#39;ascii&#39;)
return f&#34;https://www.meshtastic.org/d/#{s}&#34;.replace(&#34;=&#34;, &#34;&#34;)
def setURL(self, url):
@@ -572,41 +581,40 @@ class Node:
i = i + 1
def _requestSettings(self):
&#34;&#34;&#34;
Done with initial config messages, now send regular MeshPackets to ask for settings
&#34;&#34;&#34;
&#34;&#34;&#34;Done with initial config messages, now send regular
MeshPackets to ask for settings.&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.get_radio_request = True
def onResponse(p):
&#34;&#34;&#34;A closure to handle the response packet&#34;&#34;&#34;
self.radioConfig = p[&#34;decoded&#34;][&#34;admin&#34;][&#34;raw&#34;].get_radio_response
logging.debug(&#34;Received radio config, now fetching channels...&#34;)
self._timeout.reset() # We made foreward progress
self._requestChannel(0) # now start fetching channels
errorFound = False
if &#39;routing&#39; in p[&#34;decoded&#34;]:
if p[&#34;decoded&#34;][&#34;routing&#34;][&#34;errorReason&#34;] != &#34;NONE&#34;:
errorFound = True
print(f&#39;Error on response: {p[&#34;decoded&#34;][&#34;routing&#34;][&#34;errorReason&#34;]}&#39;)
if errorFound is False:
self.radioConfig = p[&#34;decoded&#34;][&#34;admin&#34;][&#34;raw&#34;].get_radio_response
logging.debug(&#34;Received radio config, now fetching channels...&#34;)
self._timeout.reset() # We made foreward progress
self._requestChannel(0) # now start fetching channels
# Show progress message for super slow operations
if self != self.iface.localNode:
logging.info(
&#34;Requesting preferences from remote node (this could take a while)&#34;)
print(&#34;Requesting preferences from remote node (this could take a while)&#34;)
return self._sendAdmin(p,
wantResponse=True,
onResponse=onResponse)
return self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
def exitSimulator(self):
&#34;&#34;&#34;
Tell a simulator node to exit (this message is ignored for other nodes)
&#34;&#34;&#34;
&#34;&#34;&#34;Tell a simulator node to exit (this message
is ignored for other nodes)&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.exit_simulator = True
return self._sendAdmin(p)
def reboot(self, secs: int = 10):
&#34;&#34;&#34;
Tell the node to reboot
&#34;&#34;&#34;
&#34;&#34;&#34;Tell the node to reboot.&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.reboot_seconds = secs
logging.info(f&#34;Telling node to reboot in {secs} seconds&#34;)
@@ -617,6 +625,7 @@ class Node:
&#34;&#34;&#34;Fixup indexes and add disabled channels as needed&#34;&#34;&#34;
# Add extra disabled channels as needed
# TODO: These 2 lines seem to not do anything.
for index, ch in enumerate(self.channels):
ch.index = index # fixup indexes
@@ -635,21 +644,19 @@ class Node:
index += 1
def _requestChannel(self, channelNum: int):
&#34;&#34;&#34;
Done with initial config messages, now send regular MeshPackets to ask for settings
&#34;&#34;&#34;
&#34;&#34;&#34;Done with initial config messages, now send regular
MeshPackets to ask for settings&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.get_channel_request = channelNum + 1
# Show progress message for super slow operations
if self != self.iface.localNode:
logging.info(
f&#34;Requesting channel {channelNum} info from remote node (this could take a while)&#34;)
logging.info(f&#34;Requesting channel {channelNum} info from remote node (this could take a while)&#34;)
else:
logging.debug(f&#34;Requesting channel {channelNum}&#34;)
def onResponse(p):
&#34;&#34;&#34;A closure to handle the response packet&#34;&#34;&#34;
&#34;&#34;&#34;A closure to handle the response packet for requesting a channel&#34;&#34;&#34;
c = p[&#34;decoded&#34;][&#34;admin&#34;][&#34;raw&#34;].get_channel_response
self.partialChannels.append(c)
self._timeout.reset() # We made foreward progress
@@ -659,9 +666,9 @@ class Node:
# for stress testing, we can always download all channels
fastChannelDownload = True
# Once we see a response that has NO settings, assume we are at the end of channels and stop fetching
quitEarly = (
c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload
# Once we see a response that has NO settings, assume
# we are at the end of channels and stop fetching
quitEarly = (c.role == channel_pb2.Channel.Role.DISABLED) and fastChannelDownload
if quitEarly or index &gt;= self.iface.myInfo.max_channels - 1:
logging.debug(&#34;Finished downloading channels&#34;)
@@ -674,13 +681,10 @@ class Node:
else:
self._requestChannel(index + 1)
return self._sendAdmin(p,
wantResponse=True,
onResponse=onResponse)
return self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
def _sendAdmin(self, p: admin_pb2.AdminMessage, wantResponse=False,
onResponse=None,
adminIndex=0):
onResponse=None, adminIndex=0):
&#34;&#34;&#34;Send an admin message to the specified node (or the local node if destNodeNum is zero)&#34;&#34;&#34;
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
@@ -707,7 +711,7 @@ class Node:
<pre><code class="python">def deleteChannel(self, channelIndex):
&#34;&#34;&#34;Delete the specifed channelIndex and shift other channels up&#34;&#34;&#34;
ch = self.channels[channelIndex]
if ch.role != channel_pb2.Channel.Role.SECONDARY:
if ch.role not in (channel_pb2.Channel.Role.SECONDARY, channel_pb2.Channel.Role.DISABLED):
our_exit(&#34;Warning: Only SECONDARY channels can be deleted&#34;)
# we are careful here because if we move the &#34;admin&#34; channel the channelIndex we need to use
@@ -722,9 +726,11 @@ class Node:
self.writeChannel(index, adminIndex=adminIndex)
index += 1
# if we are updating the local node, we might end up *moving* the admin channel index as we are writing
# if we are updating the local node, we might end up
# *moving* the admin channel index as we are writing
if (self.iface.localNode == self) and index &gt;= adminIndex:
# We&#39;ve now passed the old location for admin index (and writen it), so we can start finding it by name again
# We&#39;ve now passed the old location for admin index
# (and writen it), so we can start finding it by name again
adminIndex = 0</code></pre>
</details>
</dd>
@@ -732,15 +738,15 @@ class Node:
<span>def <span class="ident">exitSimulator</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Tell a simulator node to exit (this message is ignored for other nodes)</p></div>
<div class="desc"><p>Tell a simulator node to exit (this message
is ignored for other nodes)</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def exitSimulator(self):
&#34;&#34;&#34;
Tell a simulator node to exit (this message is ignored for other nodes)
&#34;&#34;&#34;
&#34;&#34;&#34;Tell a simulator node to exit (this message
is ignored for other nodes)&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.exit_simulator = True
@@ -791,16 +797,15 @@ class Node:
<span>Expand source code</span>
</summary>
<pre><code class="python">def getURL(self, includeAll: bool = True):
&#34;&#34;&#34;The sharable URL that describes the current channel
&#34;&#34;&#34;
&#34;&#34;&#34;The sharable URL that describes the current channel&#34;&#34;&#34;
# Only keep the primary/secondary channels, assume primary is first
channelSet = apponly_pb2.ChannelSet()
if self.channels:
for c in self.channels:
if c.role == channel_pb2.Channel.Role.PRIMARY or (includeAll and c.role == channel_pb2.Channel.Role.SECONDARY):
channelSet.settings.append(c.settings)
bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(bytes).decode(&#39;ascii&#39;)
some_bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(some_bytes).decode(&#39;ascii&#39;)
return f&#34;https://www.meshtastic.org/d/#{s}&#34;.replace(&#34;=&#34;, &#34;&#34;)</code></pre>
</details>
</dd>
@@ -808,15 +813,13 @@ class Node:
<span>def <span class="ident">reboot</span></span>(<span>self, secs: int = 10)</span>
</code></dt>
<dd>
<div class="desc"><p>Tell the node to reboot</p></div>
<div class="desc"><p>Tell the node to reboot.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def reboot(self, secs: int = 10):
&#34;&#34;&#34;
Tell the node to reboot
&#34;&#34;&#34;
&#34;&#34;&#34;Tell the node to reboot.&#34;&#34;&#34;
p = admin_pb2.AdminMessage()
p.reboot_seconds = secs
logging.info(f&#34;Telling node to reboot in {secs} seconds&#34;)
@@ -828,15 +831,13 @@ class Node:
<span>def <span class="ident">requestConfig</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Send regular MeshPackets to ask for settings and channels</p></div>
<div class="desc"><p>Send regular MeshPackets to ask for settings and channels.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def requestConfig(self):
&#34;&#34;&#34;
Send regular MeshPackets to ask for settings and channels
&#34;&#34;&#34;
&#34;&#34;&#34;Send regular MeshPackets to ask for settings and channels.&#34;&#34;&#34;
self.radioConfig = None
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
@@ -935,18 +936,19 @@ class Node:
<span>def <span class="ident">showChannels</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Show human readable description of our channels</p></div>
<div class="desc"><p>Show human readable description of our channels.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def showChannels(self):
&#34;&#34;&#34;Show human readable description of our channels&#34;&#34;&#34;
&#34;&#34;&#34;Show human readable description of our channels.&#34;&#34;&#34;
print(&#34;Channels:&#34;)
if self.channels:
for c in self.channels:
if c.role != channel_pb2.Channel.Role.DISABLED:
cStr = stripnl(MessageToJson(c.settings))
cStr = stripnl(MessageToJson(c.settings))
# only show if there is no psk (meaning disabled channel)
if c.settings.psk:
print(f&#34; {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}&#34;)
publicURL = self.getURL(includeAll=False)
adminURL = self.getURL(includeAll=True)
@@ -973,6 +975,22 @@ class Node:
self.showChannels()</code></pre>
</details>
</dd>
<dt id="meshtastic.node.Node.turnOffEncryptionOnPrimaryChannel"><code class="name flex">
<span>def <span class="ident">turnOffEncryptionOnPrimaryChannel</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"><p>Turn off encryption on primary channel.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def turnOffEncryptionOnPrimaryChannel(self):
&#34;&#34;&#34;Turn off encryption on primary channel.&#34;&#34;&#34;
self.channels[0].settings.psk = fromPSK(&#34;none&#34;)
print(&#34;Writing modified channels to device&#34;)
self.writeChannel(0)</code></pre>
</details>
</dd>
<dt id="meshtastic.node.Node.waitForConfig"><code class="name flex">
<span>def <span class="ident">waitForConfig</span></span>(<span>self)</span>
</code></dt>
@@ -1047,7 +1065,7 @@ class Node:
<ul>
<li>
<h4><code><a title="meshtastic.node.Node" href="#meshtastic.node.Node">Node</a></code></h4>
<ul class="two-column">
<ul class="">
<li><code><a title="meshtastic.node.Node.deleteChannel" href="#meshtastic.node.Node.deleteChannel">deleteChannel</a></code></li>
<li><code><a title="meshtastic.node.Node.exitSimulator" href="#meshtastic.node.Node.exitSimulator">exitSimulator</a></code></li>
<li><code><a title="meshtastic.node.Node.getChannelByName" href="#meshtastic.node.Node.getChannelByName">getChannelByName</a></code></li>
@@ -1059,6 +1077,7 @@ class Node:
<li><code><a title="meshtastic.node.Node.setURL" href="#meshtastic.node.Node.setURL">setURL</a></code></li>
<li><code><a title="meshtastic.node.Node.showChannels" href="#meshtastic.node.Node.showChannels">showChannels</a></code></li>
<li><code><a title="meshtastic.node.Node.showInfo" href="#meshtastic.node.Node.showInfo">showInfo</a></code></li>
<li><code><a title="meshtastic.node.Node.turnOffEncryptionOnPrimaryChannel" href="#meshtastic.node.Node.turnOffEncryptionOnPrimaryChannel">turnOffEncryptionOnPrimaryChannel</a></code></li>
<li><code><a title="meshtastic.node.Node.waitForConfig" href="#meshtastic.node.Node.waitForConfig">waitForConfig</a></code></li>
<li><code><a title="meshtastic.node.Node.writeChannel" href="#meshtastic.node.Node.writeChannel">writeChannel</a></code></li>
<li><code><a title="meshtastic.node.Node.writeConfig" href="#meshtastic.node.Node.writeConfig">writeConfig</a></code></li>

View File

@@ -67,9 +67,8 @@ class RemoteHardwareClient:
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
if not nodeid:
# pylint: disable=W1401
raise Exception(
&#34;You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)&#34;)
r&#34;You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)&#34;)
return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP,
wantAck=True, channelIndex=self.channelIndex, wantResponse=wantResponse, onResponse=onResponse)
@@ -170,9 +169,8 @@ code for how you can connect to your own custom meshtastic services</p>
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
if not nodeid:
# pylint: disable=W1401
raise Exception(
&#34;You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)&#34;)
r&#34;You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)&#34;)
return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP,
wantAck=True, channelIndex=self.channelIndex, wantResponse=wantResponse, onResponse=onResponse)

View File

@@ -35,8 +35,8 @@ import os
import stat
import serial
import meshtastic.util
from .stream_interface import StreamInterface
from .util import findPorts, our_exit
class SerialInterface(StreamInterface):
&#34;&#34;&#34;Interface class for meshtastic devices over a serial link&#34;&#34;&#34;
@@ -51,14 +51,14 @@ class SerialInterface(StreamInterface):
&#34;&#34;&#34;
if devPath is None:
ports = findPorts()
ports = meshtastic.util.findPorts()
logging.debug(f&#34;ports:{ports}&#34;)
if len(ports) == 0:
our_exit(&#34;Warning: No Meshtastic devices detected.&#34;)
meshtastic.util.our_exit(&#34;Warning: No Meshtastic devices detected.&#34;)
elif len(ports) &gt; 1:
message = &#34;Warning: Multiple serial ports were detected so one serial port must be specified with the &#39;--port&#39;.\n&#34;
message += f&#34; Ports detected:{ports}&#34;
our_exit(message)
meshtastic.util.our_exit(message)
else:
devPath = ports[0]
@@ -143,14 +143,14 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
&#34;&#34;&#34;
if devPath is None:
ports = findPorts()
ports = meshtastic.util.findPorts()
logging.debug(f&#34;ports:{ports}&#34;)
if len(ports) == 0:
our_exit(&#34;Warning: No Meshtastic devices detected.&#34;)
meshtastic.util.our_exit(&#34;Warning: No Meshtastic devices detected.&#34;)
elif len(ports) &gt; 1:
message = &#34;Warning: Multiple serial ports were detected so one serial port must be specified with the &#39;--port&#39;.\n&#34;
message += f&#34; Ports detected:{ports}&#34;
our_exit(message)
meshtastic.util.our_exit(message)
else:
devPath = ports[0]

View File

@@ -27,7 +27,7 @@
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; Stream Interface base class
<pre><code class="python">&#34;&#34;&#34;Stream Interface base class
&#34;&#34;&#34;
import logging
import threading
@@ -54,7 +54,8 @@ class StreamInterface(MeshInterface):
Keyword Arguments:
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the
device will be emitted to that stream. (default: {None})
Raises:
Exception: [description]
@@ -82,12 +83,14 @@ class StreamInterface(MeshInterface):
def connect(self):
&#34;&#34;&#34;Connect to our radio
Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
start the reading thread later.
Normally this is called automatically by the constructor, but if you
passed in connectNow=False you can manually start the reading thread later.
&#34;&#34;&#34;
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync (we don&#39;t use START1 because we want to ensure it is looking for START1)
# Send some bogus UART characters to force a sleeping device to wake, and
# if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync (we don&#39;t use START1
# because we want to ensure it is looking for START1)
p = bytearray([START2] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running
@@ -104,8 +107,11 @@ class StreamInterface(MeshInterface):
MeshInterface._disconnected(self)
logging.debug(&#34;Closing our port&#34;)
# pylint: disable=E0203
if not self.stream is None:
# pylint: disable=E0203
self.stream.close()
# pylint: disable=W0201
self.stream = None
def _writeBytes(self, b):
@@ -131,7 +137,8 @@ class StreamInterface(MeshInterface):
&#34;&#34;&#34;Close a connection to the device&#34;&#34;&#34;
logging.debug(&#34;Closing stream&#34;)
MeshInterface.close(self)
# pyserial cancel_read doesn&#39;t seem to work, therefore we ask the reader thread to close things for us
# pyserial cancel_read doesn&#39;t seem to work, therefore we ask the
# reader thread to close things for us
self._wantExit = True
if self._rxThread != threading.current_thread():
self._rxThread.join() # wait for it to exit
@@ -177,8 +184,7 @@ class StreamInterface(MeshInterface):
try:
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
except Exception as ex:
logging.error(
f&#34;Error while handling message from radio {ex}&#34;)
logging.error(f&#34;Error while handling message from radio {ex}&#34;)
traceback.print_exc()
self._rxBuf = empty
else:
@@ -186,15 +192,12 @@ class StreamInterface(MeshInterface):
pass
except serial.SerialException as ex:
if not self._wantExit: # We might intentionally get an exception during shutdown
logging.warning(
f&#34;Meshtastic serial port disconnected, disconnecting... {ex}&#34;)
logging.warning(f&#34;Meshtastic serial port disconnected, disconnecting... {ex}&#34;)
except OSError as ex:
if not self._wantExit: # We might intentionally get an exception during shutdown
logging.error(
f&#34;Unexpected OSError, terminating meshtastic reader... {ex}&#34;)
logging.error(f&#34;Unexpected OSError, terminating meshtastic reader... {ex}&#34;)
except Exception as ex:
logging.error(
f&#34;Unexpected exception, terminating meshtastic reader... {ex}&#34;)
logging.error(f&#34;Unexpected exception, terminating meshtastic reader... {ex}&#34;)
finally:
logging.debug(&#34;reader is exiting&#34;)
self._disconnected()</code></pre>
@@ -218,7 +221,8 @@ class StreamInterface(MeshInterface):
<p>Constructor, opens a connection to self.stream</p>
<p>Keyword Arguments:
devPath {string} &ndash; A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
debugOut {stream} &ndash; If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})</p>
debugOut {stream} &ndash; If a stream is provided, any debug serial output from the
device will be emitted to that stream. (default: {None})</p>
<h2 id="raises">Raises</h2>
<dl>
<dt><code>Exception</code></dt>
@@ -238,7 +242,8 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
Keyword Arguments:
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the
device will be emitted to that stream. (default: {None})
Raises:
Exception: [description]
@@ -266,12 +271,14 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
def connect(self):
&#34;&#34;&#34;Connect to our radio
Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
start the reading thread later.
Normally this is called automatically by the constructor, but if you
passed in connectNow=False you can manually start the reading thread later.
&#34;&#34;&#34;
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync (we don&#39;t use START1 because we want to ensure it is looking for START1)
# Send some bogus UART characters to force a sleeping device to wake, and
# if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync (we don&#39;t use START1
# because we want to ensure it is looking for START1)
p = bytearray([START2] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running
@@ -288,8 +295,11 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
MeshInterface._disconnected(self)
logging.debug(&#34;Closing our port&#34;)
# pylint: disable=E0203
if not self.stream is None:
# pylint: disable=E0203
self.stream.close()
# pylint: disable=W0201
self.stream = None
def _writeBytes(self, b):
@@ -315,7 +325,8 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
&#34;&#34;&#34;Close a connection to the device&#34;&#34;&#34;
logging.debug(&#34;Closing stream&#34;)
MeshInterface.close(self)
# pyserial cancel_read doesn&#39;t seem to work, therefore we ask the reader thread to close things for us
# pyserial cancel_read doesn&#39;t seem to work, therefore we ask the
# reader thread to close things for us
self._wantExit = True
if self._rxThread != threading.current_thread():
self._rxThread.join() # wait for it to exit
@@ -361,8 +372,7 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
try:
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
except Exception as ex:
logging.error(
f&#34;Error while handling message from radio {ex}&#34;)
logging.error(f&#34;Error while handling message from radio {ex}&#34;)
traceback.print_exc()
self._rxBuf = empty
else:
@@ -370,15 +380,12 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
pass
except serial.SerialException as ex:
if not self._wantExit: # We might intentionally get an exception during shutdown
logging.warning(
f&#34;Meshtastic serial port disconnected, disconnecting... {ex}&#34;)
logging.warning(f&#34;Meshtastic serial port disconnected, disconnecting... {ex}&#34;)
except OSError as ex:
if not self._wantExit: # We might intentionally get an exception during shutdown
logging.error(
f&#34;Unexpected OSError, terminating meshtastic reader... {ex}&#34;)
logging.error(f&#34;Unexpected OSError, terminating meshtastic reader... {ex}&#34;)
except Exception as ex:
logging.error(
f&#34;Unexpected exception, terminating meshtastic reader... {ex}&#34;)
logging.error(f&#34;Unexpected exception, terminating meshtastic reader... {ex}&#34;)
finally:
logging.debug(&#34;reader is exiting&#34;)
self._disconnected()</code></pre>
@@ -407,7 +414,8 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
&#34;&#34;&#34;Close a connection to the device&#34;&#34;&#34;
logging.debug(&#34;Closing stream&#34;)
MeshInterface.close(self)
# pyserial cancel_read doesn&#39;t seem to work, therefore we ask the reader thread to close things for us
# pyserial cancel_read doesn&#39;t seem to work, therefore we ask the
# reader thread to close things for us
self._wantExit = True
if self._rxThread != threading.current_thread():
self._rxThread.join() # wait for it to exit</code></pre>
@@ -418,8 +426,8 @@ debugOut {stream} &ndash; If a stream is provided, any debug serial output from
</code></dt>
<dd>
<div class="desc"><p>Connect to our radio</p>
<p>Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
start the reading thread later.</p></div>
<p>Normally this is called automatically by the constructor, but if you
passed in connectNow=False you can manually start the reading thread later.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
@@ -427,12 +435,14 @@ start the reading thread later.</p></div>
<pre><code class="python">def connect(self):
&#34;&#34;&#34;Connect to our radio
Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
start the reading thread later.
Normally this is called automatically by the constructor, but if you
passed in connectNow=False you can manually start the reading thread later.
&#34;&#34;&#34;
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync (we don&#39;t use START1 because we want to ensure it is looking for START1)
# Send some bogus UART characters to force a sleeping device to wake, and
# if the reading statemachine was parsing a bad packet make sure
# we write enought start bytes to force it to resync (we don&#39;t use START1
# because we want to ensure it is looking for START1)
p = bytearray([START2] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running

View File

@@ -27,7 +27,7 @@
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; TCPInterface class for interfacing with http endpoint
<pre><code class="python">&#34;&#34;&#34;TCPInterface class for interfacing with http endpoint
&#34;&#34;&#34;
import logging
import socket
@@ -38,7 +38,8 @@ from .stream_interface import StreamInterface
class TCPInterface(StreamInterface):
&#34;&#34;&#34;Interface class for meshtastic devices over a TCP link&#34;&#34;&#34;
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
connectNow=True, portNumber=4403):
&#34;&#34;&#34;Constructor, opens a connection to a specified IP address/hostname
Keyword Arguments:
@@ -62,8 +63,8 @@ class TCPInterface(StreamInterface):
&#34;&#34;&#34;Close a connection to the device&#34;&#34;&#34;
logging.debug(&#34;Closing TCP stream&#34;)
StreamInterface.close(self)
# Sometimes the socket read might be blocked in the reader thread. Therefore we force the shutdown by closing
# the socket here
# Sometimes the socket read might be blocked in the reader thread.
# Therefore we force the shutdown by closing the socket here
self._wantExit = True
if not self.socket is None:
try:
@@ -106,7 +107,8 @@ hostname {string} &ndash; Hostname/IP address of the device to connect to</p></d
<pre><code class="python">class TCPInterface(StreamInterface):
&#34;&#34;&#34;Interface class for meshtastic devices over a TCP link&#34;&#34;&#34;
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
connectNow=True, portNumber=4403):
&#34;&#34;&#34;Constructor, opens a connection to a specified IP address/hostname
Keyword Arguments:
@@ -130,8 +132,8 @@ hostname {string} &ndash; Hostname/IP address of the device to connect to</p></d
&#34;&#34;&#34;Close a connection to the device&#34;&#34;&#34;
logging.debug(&#34;Closing TCP stream&#34;)
StreamInterface.close(self)
# Sometimes the socket read might be blocked in the reader thread. Therefore we force the shutdown by closing
# the socket here
# Sometimes the socket read might be blocked in the reader thread.
# Therefore we force the shutdown by closing the socket here
self._wantExit = True
if not self.socket is None:
try:

View File

@@ -5,7 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.10.0" />
<title>meshtastic.test API documentation</title>
<meta name="description" content="Testing" />
<meta name="description" content="With two radios connected serially, send and receive test
messages and report back if successful." />
<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>
@@ -22,12 +23,14 @@
<h1 class="title">Module <code>meshtastic.test</code></h1>
</header>
<section id="section-intro">
<p>Testing</p>
<p>With two radios connected serially, send and receive test
messages and report back if successful.</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; Testing
<pre><code class="python">&#34;&#34;&#34;With two radios connected serially, send and receive test
messages and report back if successful.
&#34;&#34;&#34;
import logging
import time
@@ -35,8 +38,11 @@ import sys
import traceback
from dotmap import DotMap
from pubsub import pub
from . import util
from . import SerialInterface, TCPInterface, BROADCAST_NUM
import meshtastic.util
from .__init__ import BROADCAST_NUM
from .serial_interface import SerialInterface
from .tcp_interface import TCPInterface
&#34;&#34;&#34;The interfaces we are using for our tests&#34;&#34;&#34;
interfaces = None
@@ -134,25 +140,23 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
logging.info(
f&#34;Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far&#34;)
# if numFail &gt;= 3:
# for i in interfaces:
# i.close()
# return
time.sleep(1)
if numFail &gt; maxFailures:
logging.error(&#34;Too many failures! Test failed!&#34;)
return numFail
return False
return True
def testThread(numTests=50):
&#34;&#34;&#34;Test thread&#34;&#34;&#34;
logging.info(&#34;Found devices, starting tests...&#34;)
runTests(numTests, wantAck=True)
# Allow a few dropped packets
runTests(numTests, wantAck=False, maxFailures=5)
result = runTests(numTests, wantAck=True)
if result:
# Run another test
# Allow a few dropped packets
result = runTests(numTests, wantAck=False, maxFailures=1)
return result
def onConnection(topic=pub.AUTO_TOPIC):
@@ -164,19 +168,18 @@ def openDebugLog(portName):
&#34;&#34;&#34;Open the debug log file&#34;&#34;&#34;
debugname = &#34;log&#34; + portName.replace(&#34;/&#34;, &#34;_&#34;)
logging.info(f&#34;Writing serial debugging to {debugname}&#34;)
return open(debugname, &#39;w+&#39;, buffering=1)
return open(debugname, &#39;w+&#39;, buffering=1, encoding=&#39;utf8&#39;)
def testAll():
def testAll(numTests=5):
&#34;&#34;&#34;
Run a series of tests using devices we can find.
This is called from the cli with the &#34;--test&#34; option.
Raises:
Exception: If not enough devices are found
&#34;&#34;&#34;
ports = util.findPorts()
ports = meshtastic.util.findPorts()
if len(ports) &lt; 2:
raise Exception(&#34;Must have at least two devices connected to USB&#34;)
meshtastic.util.our_exit(&#34;Warning: Must have at least two devices connected to USB.&#34;)
pub.subscribe(onConnection, &#34;meshtastic.connection&#34;)
pub.subscribe(onReceive, &#34;meshtastic.receive&#34;)
@@ -185,11 +188,13 @@ def testAll():
port, debugOut=openDebugLog(port), connectNow=True), ports))
logging.info(&#34;Ports opened, starting test&#34;)
testThread()
result = testThread(numTests)
for i in interfaces:
i.close()
return result
def testSimulator():
&#34;&#34;&#34;
@@ -200,7 +205,7 @@ def testSimulator():
Run with
python3 -c &#39;from meshtastic.test import testSimulator; testSimulator()&#39;
&#34;&#34;&#34;
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
logging.basicConfig(level=logging.DEBUG)
logging.info(&#34;Connecting to simulator on localhost!&#34;)
try:
iface = TCPInterface(&#34;localhost&#34;)
@@ -295,7 +300,7 @@ def testSimulator():
&#34;&#34;&#34;Open the debug log file&#34;&#34;&#34;
debugname = &#34;log&#34; + portName.replace(&#34;/&#34;, &#34;_&#34;)
logging.info(f&#34;Writing serial debugging to {debugname}&#34;)
return open(debugname, &#39;w+&#39;, buffering=1)</code></pre>
return open(debugname, &#39;w+&#39;, buffering=1, encoding=&#39;utf8&#39;)</code></pre>
</details>
</dd>
<dt id="meshtastic.test.runTests"><code class="name flex">
@@ -328,17 +333,12 @@ def testSimulator():
logging.info(
f&#34;Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far&#34;)
# if numFail &gt;= 3:
# for i in interfaces:
# i.close()
# return
time.sleep(1)
if numFail &gt; maxFailures:
logging.error(&#34;Too many failures! Test failed!&#34;)
return numFail</code></pre>
return False
return True</code></pre>
</details>
</dd>
<dt id="meshtastic.test.subscribe"><code class="name flex">
@@ -357,29 +357,24 @@ def testSimulator():
</details>
</dd>
<dt id="meshtastic.test.testAll"><code class="name flex">
<span>def <span class="ident">testAll</span></span>(<span>)</span>
<span>def <span class="ident">testAll</span></span>(<span>numTests=5)</span>
</code></dt>
<dd>
<div class="desc"><p>Run a series of tests using devices we can find.</p>
<h2 id="raises">Raises</h2>
<dl>
<dt><code>Exception</code></dt>
<dd>If not enough devices are found</dd>
</dl></div>
<div class="desc"><p>Run a series of tests using devices we can find.
This is called from the cli with the "&ndash;test" option.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def testAll():
<pre><code class="python">def testAll(numTests=5):
&#34;&#34;&#34;
Run a series of tests using devices we can find.
This is called from the cli with the &#34;--test&#34; option.
Raises:
Exception: If not enough devices are found
&#34;&#34;&#34;
ports = util.findPorts()
ports = meshtastic.util.findPorts()
if len(ports) &lt; 2:
raise Exception(&#34;Must have at least two devices connected to USB&#34;)
meshtastic.util.our_exit(&#34;Warning: Must have at least two devices connected to USB.&#34;)
pub.subscribe(onConnection, &#34;meshtastic.connection&#34;)
pub.subscribe(onReceive, &#34;meshtastic.receive&#34;)
@@ -388,10 +383,12 @@ def testSimulator():
port, debugOut=openDebugLog(port), connectNow=True), ports))
logging.info(&#34;Ports opened, starting test&#34;)
testThread()
result = testThread(numTests)
for i in interfaces:
i.close()</code></pre>
i.close()
return result</code></pre>
</details>
</dd>
<dt id="meshtastic.test.testSend"><code class="name flex">
@@ -466,7 +463,7 @@ python3 -c 'from meshtastic.test import testSimulator; testSimulator()'</p></div
Run with
python3 -c &#39;from meshtastic.test import testSimulator; testSimulator()&#39;
&#34;&#34;&#34;
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
logging.basicConfig(level=logging.DEBUG)
logging.info(&#34;Connecting to simulator on localhost!&#34;)
try:
iface = TCPInterface(&#34;localhost&#34;)
@@ -494,9 +491,12 @@ python3 -c 'from meshtastic.test import testSimulator; testSimulator()'</p></div
<pre><code class="python">def testThread(numTests=50):
&#34;&#34;&#34;Test thread&#34;&#34;&#34;
logging.info(&#34;Found devices, starting tests...&#34;)
runTests(numTests, wantAck=True)
# Allow a few dropped packets
runTests(numTests, wantAck=False, maxFailures=5)</code></pre>
result = runTests(numTests, wantAck=True)
if result:
# Run another test
# Allow a few dropped packets
result = runTests(numTests, wantAck=False, maxFailures=1)
return result</code></pre>
</details>
</dd>
</dl>

View File

@@ -0,0 +1,100 @@
<!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.10.0" />
<title>meshtastic.tests.conftest API documentation</title>
<meta name="description" content="Common pytest code (place for fixtures)." />
<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.tests.conftest</code></h1>
</header>
<section id="section-intro">
<p>Common pytest code (place for fixtures).</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Common pytest code (place for fixtures).&#34;&#34;&#34;
import argparse
import pytest
from meshtastic.__main__ import Globals
@pytest.fixture
def reset_globals():
&#34;&#34;&#34;Fixture to reset globals.&#34;&#34;&#34;
parser = None
parser = argparse.ArgumentParser()
Globals.getInstance().reset()
Globals.getInstance().set_parser(parser)</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.conftest.reset_globals"><code class="name flex">
<span>def <span class="ident">reset_globals</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Fixture to reset globals.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.fixture
def reset_globals():
&#34;&#34;&#34;Fixture to reset globals.&#34;&#34;&#34;
parser = None
parser = argparse.ArgumentParser()
Globals.getInstance().reset()
Globals.getInstance().set_parser(parser)</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.conftest.reset_globals" href="#meshtastic.tests.conftest.reset_globals">reset_globals</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,130 @@
<!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.10.0" />
<title>meshtastic.tests 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.tests</code></h1>
</header>
<section id="section-intro">
</section>
<section>
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
<dl>
<dt><code class="name"><a title="meshtastic.tests.conftest" href="conftest.html">meshtastic.tests.conftest</a></code></dt>
<dd>
<div class="desc"><p>Common pytest code (place for fixtures).</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_ble_interface" href="test_ble_interface.html">meshtastic.tests.test_ble_interface</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for ble_interface.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_globals" href="test_globals.html">meshtastic.tests.test_globals</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for globals.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_int" href="test_int.html">meshtastic.tests.test_int</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic integration tests</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_main" href="test_main.html">meshtastic.tests.test_main</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for <strong>main</strong>.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_mesh_interface" href="test_mesh_interface.html">meshtastic.tests.test_mesh_interface</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for mesh_interface.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_node" href="test_node.html">meshtastic.tests.test_node</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for node.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_serial_interface" href="test_serial_interface.html">meshtastic.tests.test_serial_interface</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for serial_interface.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_smoke1" href="test_smoke1.html">meshtastic.tests.test_smoke1</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic smoke tests with a single device via USB</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_smoke2" href="test_smoke2.html">meshtastic.tests.test_smoke2</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic smoke tests with 2 devices connected via USB</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_smoke_wifi" href="test_smoke_wifi.html">meshtastic.tests.test_smoke_wifi</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic smoke tests a device setup with wifi …</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_stream_interface" href="test_stream_interface.html">meshtastic.tests.test_stream_interface</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for stream_interface.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_tcp_interface" href="test_tcp_interface.html">meshtastic.tests.test_tcp_interface</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for tcp_interface.py</p></div>
</dd>
<dt><code class="name"><a title="meshtastic.tests.test_util" href="test_util.html">meshtastic.tests.test_util</a></code></dt>
<dd>
<div class="desc"><p>Meshtastic unit tests for util.py</p></div>
</dd>
</dl>
</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>
<li><h3><a href="#header-submodules">Sub-modules</a></h3>
<ul>
<li><code><a title="meshtastic.tests.conftest" href="conftest.html">meshtastic.tests.conftest</a></code></li>
<li><code><a title="meshtastic.tests.test_ble_interface" href="test_ble_interface.html">meshtastic.tests.test_ble_interface</a></code></li>
<li><code><a title="meshtastic.tests.test_globals" href="test_globals.html">meshtastic.tests.test_globals</a></code></li>
<li><code><a title="meshtastic.tests.test_int" href="test_int.html">meshtastic.tests.test_int</a></code></li>
<li><code><a title="meshtastic.tests.test_main" href="test_main.html">meshtastic.tests.test_main</a></code></li>
<li><code><a title="meshtastic.tests.test_mesh_interface" href="test_mesh_interface.html">meshtastic.tests.test_mesh_interface</a></code></li>
<li><code><a title="meshtastic.tests.test_node" href="test_node.html">meshtastic.tests.test_node</a></code></li>
<li><code><a title="meshtastic.tests.test_serial_interface" href="test_serial_interface.html">meshtastic.tests.test_serial_interface</a></code></li>
<li><code><a title="meshtastic.tests.test_smoke1" href="test_smoke1.html">meshtastic.tests.test_smoke1</a></code></li>
<li><code><a title="meshtastic.tests.test_smoke2" href="test_smoke2.html">meshtastic.tests.test_smoke2</a></code></li>
<li><code><a title="meshtastic.tests.test_smoke_wifi" href="test_smoke_wifi.html">meshtastic.tests.test_smoke_wifi</a></code></li>
<li><code><a title="meshtastic.tests.test_stream_interface" href="test_stream_interface.html">meshtastic.tests.test_stream_interface</a></code></li>
<li><code><a title="meshtastic.tests.test_tcp_interface" href="test_tcp_interface.html">meshtastic.tests.test_tcp_interface</a></code></li>
<li><code><a title="meshtastic.tests.test_util" href="test_util.html">meshtastic.tests.test_util</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,95 @@
<!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.10.0" />
<title>meshtastic.tests.test_ble_interface API documentation</title>
<meta name="description" content="Meshtastic unit tests for ble_interface.py" />
<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.tests.test_ble_interface</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for ble_interface.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for ble_interface.py&#34;&#34;&#34;
import pytest
from ..ble_interface import BLEInterface
@pytest.mark.unit
def test_BLEInterface():
&#34;&#34;&#34;Test that we can instantiate a BLEInterface&#34;&#34;&#34;
iface = BLEInterface(&#39;foo&#39;, debugOut=True, noProto=True)
iface.close()</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_ble_interface.test_BLEInterface"><code class="name flex">
<span>def <span class="ident">test_BLEInterface</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a BLEInterface</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_BLEInterface():
&#34;&#34;&#34;Test that we can instantiate a BLEInterface&#34;&#34;&#34;
iface = BLEInterface(&#39;foo&#39;, debugOut=True, noProto=True)
iface.close()</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_ble_interface.test_BLEInterface" href="#meshtastic.tests.test_ble_interface.test_BLEInterface">test_BLEInterface</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,130 @@
<!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.10.0" />
<title>meshtastic.tests.test_globals API documentation</title>
<meta name="description" content="Meshtastic unit tests for globals.py" />
<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.tests.test_globals</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for globals.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for globals.py
&#34;&#34;&#34;
import pytest
from ..globals import Globals
@pytest.mark.unit
def test_globals_get_instaance():
&#34;&#34;&#34;Test that we can instantiate a Globals instance&#34;&#34;&#34;
ourglobals = Globals.getInstance()
ourglobals2 = Globals.getInstance()
assert ourglobals == ourglobals2
@pytest.mark.unit
def test_globals_there_can_be_only_one():
&#34;&#34;&#34;Test that we can cannot create two Globals instances&#34;&#34;&#34;
# if we have an instance, delete it
Globals.getInstance()
with pytest.raises(Exception) as pytest_wrapped_e:
# try to create another instance
Globals()
assert pytest_wrapped_e.type == Exception</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_globals.test_globals_get_instaance"><code class="name flex">
<span>def <span class="ident">test_globals_get_instaance</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a Globals instance</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_globals_get_instaance():
&#34;&#34;&#34;Test that we can instantiate a Globals instance&#34;&#34;&#34;
ourglobals = Globals.getInstance()
ourglobals2 = Globals.getInstance()
assert ourglobals == ourglobals2</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_globals.test_globals_there_can_be_only_one"><code class="name flex">
<span>def <span class="ident">test_globals_there_can_be_only_one</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can cannot create two Globals instances</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_globals_there_can_be_only_one():
&#34;&#34;&#34;Test that we can cannot create two Globals instances&#34;&#34;&#34;
# if we have an instance, delete it
Globals.getInstance()
with pytest.raises(Exception) as pytest_wrapped_e:
# try to create another instance
Globals()
assert pytest_wrapped_e.type == Exception</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_globals.test_globals_get_instaance" href="#meshtastic.tests.test_globals.test_globals_get_instaance">test_globals_get_instaance</a></code></li>
<li><code><a title="meshtastic.tests.test_globals.test_globals_there_can_be_only_one" href="#meshtastic.tests.test_globals.test_globals_there_can_be_only_one">test_globals_there_can_be_only_one</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,177 @@
<!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.10.0" />
<title>meshtastic.tests.test_int API documentation</title>
<meta name="description" content="Meshtastic integration tests" />
<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.tests.test_int</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic integration tests</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic integration tests&#34;&#34;&#34;
import re
import subprocess
import pytest
@pytest.mark.int
def test_int_no_args():
&#34;&#34;&#34;Test without any args&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic&#39;)
assert re.match(r&#39;usage: meshtastic&#39;, out)
assert return_value == 1
@pytest.mark.int
def test_int_version():
&#34;&#34;&#34;Test &#39;--version&#39;.&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --version&#39;)
assert re.match(r&#39;[0-9]+\.[0-9]+\.[0-9]&#39;, out)
assert return_value == 0
@pytest.mark.int
def test_int_help():
&#34;&#34;&#34;Test &#39;--help&#39;.&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --help&#39;)
assert re.match(r&#39;usage: meshtastic &#39;, out)
assert return_value == 0
@pytest.mark.int
def test_int_support():
&#34;&#34;&#34;Test &#39;--support&#39;.&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --support&#39;)
assert re.search(r&#39;System&#39;, out)
assert re.search(r&#39;Python&#39;, out)
assert return_value == 0</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_int.test_int_help"><code class="name flex">
<span>def <span class="ident">test_int_help</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test '&ndash;help'.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.int
def test_int_help():
&#34;&#34;&#34;Test &#39;--help&#39;.&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --help&#39;)
assert re.match(r&#39;usage: meshtastic &#39;, out)
assert return_value == 0</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_int.test_int_no_args"><code class="name flex">
<span>def <span class="ident">test_int_no_args</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test without any args</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.int
def test_int_no_args():
&#34;&#34;&#34;Test without any args&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic&#39;)
assert re.match(r&#39;usage: meshtastic&#39;, out)
assert return_value == 1</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_int.test_int_support"><code class="name flex">
<span>def <span class="ident">test_int_support</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test '&ndash;support'.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.int
def test_int_support():
&#34;&#34;&#34;Test &#39;--support&#39;.&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --support&#39;)
assert re.search(r&#39;System&#39;, out)
assert re.search(r&#39;Python&#39;, out)
assert return_value == 0</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_int.test_int_version"><code class="name flex">
<span>def <span class="ident">test_int_version</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test '&ndash;version'.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.int
def test_int_version():
&#34;&#34;&#34;Test &#39;--version&#39;.&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --version&#39;)
assert re.match(r&#39;[0-9]+\.[0-9]+\.[0-9]&#39;, out)
assert return_value == 0</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_int.test_int_help" href="#meshtastic.tests.test_int.test_int_help">test_int_help</a></code></li>
<li><code><a title="meshtastic.tests.test_int.test_int_no_args" href="#meshtastic.tests.test_int.test_int_no_args">test_int_no_args</a></code></li>
<li><code><a title="meshtastic.tests.test_int.test_int_support" href="#meshtastic.tests.test_int.test_int_support">test_int_support</a></code></li>
<li><code><a title="meshtastic.tests.test_int.test_int_version" href="#meshtastic.tests.test_int.test_int_version">test_int_version</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
<!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.10.0" />
<title>meshtastic.tests.test_mesh_interface API documentation</title>
<meta name="description" content="Meshtastic unit tests for mesh_interface.py" />
<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.tests.test_mesh_interface</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for mesh_interface.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for mesh_interface.py&#34;&#34;&#34;
import re
import pytest
from ..mesh_interface import MeshInterface
@pytest.mark.unit
def test_MeshInterface(capsys):
&#34;&#34;&#34;Test that we can instantiate a MeshInterface&#34;&#34;&#34;
iface = MeshInterface(noProto=True)
iface.showInfo()
iface.localNode.showInfo()
iface.showNodes()
iface.close()
out, err = capsys.readouterr()
assert re.search(r&#39;Owner: None \(None\)&#39;, out, re.MULTILINE)
assert re.search(r&#39;Nodes&#39;, out, re.MULTILINE)
assert re.search(r&#39;Preferences&#39;, out, re.MULTILINE)
assert re.search(r&#39;Channels&#39;, out, re.MULTILINE)
assert re.search(r&#39;Primary channel URL&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_mesh_interface.test_MeshInterface"><code class="name flex">
<span>def <span class="ident">test_MeshInterface</span></span>(<span>capsys)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a MeshInterface</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_MeshInterface(capsys):
&#34;&#34;&#34;Test that we can instantiate a MeshInterface&#34;&#34;&#34;
iface = MeshInterface(noProto=True)
iface.showInfo()
iface.localNode.showInfo()
iface.showNodes()
iface.close()
out, err = capsys.readouterr()
assert re.search(r&#39;Owner: None \(None\)&#39;, out, re.MULTILINE)
assert re.search(r&#39;Nodes&#39;, out, re.MULTILINE)
assert re.search(r&#39;Preferences&#39;, out, re.MULTILINE)
assert re.search(r&#39;Channels&#39;, out, re.MULTILINE)
assert re.search(r&#39;Primary channel URL&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_mesh_interface.test_MeshInterface" href="#meshtastic.tests.test_mesh_interface.test_MeshInterface">test_MeshInterface</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,951 @@
<!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.10.0" />
<title>meshtastic.tests.test_node API documentation</title>
<meta name="description" content="Meshtastic unit tests for node.py" />
<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.tests.test_node</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for node.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for node.py&#34;&#34;&#34;
import re
from unittest.mock import patch, MagicMock
import pytest
from ..node import Node
from ..serial_interface import SerialInterface
from ..admin_pb2 import AdminMessage
@pytest.mark.unit
def test_node(capsys):
&#34;&#34;&#34;Test that we can instantiate a Node&#34;&#34;&#34;
anode = Node(&#39;foo&#39;, &#39;bar&#39;)
anode.showChannels()
anode.showInfo()
out, err = capsys.readouterr()
assert re.search(r&#39;Preferences&#39;, out)
assert re.search(r&#39;Channels&#39;, out)
assert re.search(r&#39;Primary channel URL&#39;, out)
assert err == &#39;&#39;
@pytest.mark.unit
def test_node_reqquestConfig():
&#34;&#34;&#34;Test run requestConfig&#34;&#34;&#34;
iface = MagicMock(autospec=SerialInterface)
amesg = MagicMock(autospec=AdminMessage)
with patch(&#39;meshtastic.serial_interface.SerialInterface&#39;, return_value=iface) as mo:
with patch(&#39;meshtastic.admin_pb2.AdminMessage&#39;, return_value=amesg):
anode = Node(mo, &#39;bar&#39;)
anode.requestConfig()</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_node.test_node"><code class="name flex">
<span>def <span class="ident">test_node</span></span>(<span>capsys)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a Node</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_node(capsys):
&#34;&#34;&#34;Test that we can instantiate a Node&#34;&#34;&#34;
anode = Node(&#39;foo&#39;, &#39;bar&#39;)
anode.showChannels()
anode.showInfo()
out, err = capsys.readouterr()
assert re.search(r&#39;Preferences&#39;, out)
assert re.search(r&#39;Channels&#39;, out)
assert re.search(r&#39;Primary channel URL&#39;, out)
assert err == &#39;&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_node.test_node_reqquestConfig"><code class="name flex">
<span>def <span class="ident">test_node_reqquestConfig</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test run requestConfig</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_node_reqquestConfig():
&#34;&#34;&#34;Test run requestConfig&#34;&#34;&#34;
iface = MagicMock(autospec=SerialInterface)
amesg = MagicMock(autospec=AdminMessage)
with patch(&#39;meshtastic.serial_interface.SerialInterface&#39;, return_value=iface) as mo:
with patch(&#39;meshtastic.admin_pb2.AdminMessage&#39;, return_value=amesg):
anode = Node(mo, &#39;bar&#39;)
anode.requestConfig()</code></pre>
</details>
</dd>
</dl>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="meshtastic.tests.test_node.AdminMessage"><code class="flex name class">
<span>class <span class="ident">AdminMessage</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.tests.test_node.AdminMessage.CONFIRM_SET_CHANNEL_FIELD_NUMBER"><code class="name">var <span class="ident">CONFIRM_SET_CHANNEL_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.CONFIRM_SET_RADIO_FIELD_NUMBER"><code class="name">var <span class="ident">CONFIRM_SET_RADIO_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.EXIT_SIMULATOR_FIELD_NUMBER"><code class="name">var <span class="ident">EXIT_SIMULATOR_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.GET_CHANNEL_REQUEST_FIELD_NUMBER"><code class="name">var <span class="ident">GET_CHANNEL_REQUEST_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.GET_CHANNEL_RESPONSE_FIELD_NUMBER"><code class="name">var <span class="ident">GET_CHANNEL_RESPONSE_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.GET_RADIO_REQUEST_FIELD_NUMBER"><code class="name">var <span class="ident">GET_RADIO_REQUEST_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.GET_RADIO_RESPONSE_FIELD_NUMBER"><code class="name">var <span class="ident">GET_RADIO_RESPONSE_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.REBOOT_SECONDS_FIELD_NUMBER"><code class="name">var <span class="ident">REBOOT_SECONDS_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.SET_CHANNEL_FIELD_NUMBER"><code class="name">var <span class="ident">SET_CHANNEL_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.SET_OWNER_FIELD_NUMBER"><code class="name">var <span class="ident">SET_OWNER_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.SET_RADIO_FIELD_NUMBER"><code class="name">var <span class="ident">SET_RADIO_FIELD_NUMBER</span></code></dt>
<dd>
<div class="desc"></div>
</dd>
</dl>
<h3>Static methods</h3>
<dl>
<dt id="meshtastic.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.confirm_set_channel"><code class="name">var <span class="ident">confirm_set_channel</span></code></dt>
<dd>
<div class="desc"><p>Getter for confirm_set_channel.</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.tests.test_node.AdminMessage.confirm_set_radio"><code class="name">var <span class="ident">confirm_set_radio</span></code></dt>
<dd>
<div class="desc"><p>Getter for confirm_set_radio.</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.tests.test_node.AdminMessage.exit_simulator"><code class="name">var <span class="ident">exit_simulator</span></code></dt>
<dd>
<div class="desc"><p>Getter for exit_simulator.</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.tests.test_node.AdminMessage.get_channel_request"><code class="name">var <span class="ident">get_channel_request</span></code></dt>
<dd>
<div class="desc"><p>Getter for get_channel_request.</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.tests.test_node.AdminMessage.get_channel_response"><code class="name">var <span class="ident">get_channel_response</span></code></dt>
<dd>
<div class="desc"><p>Getter for get_channel_response.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
# take that object and discard ours.
# WARNING: We are relying on setdefault() being atomic. This is true
# in CPython but we haven&#39;t investigated others. This warning appears
# in several other locations in this file.
field_value = self._fields.setdefault(field, field_value)
return field_value</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.get_radio_request"><code class="name">var <span class="ident">get_radio_request</span></code></dt>
<dd>
<div class="desc"><p>Getter for get_radio_request.</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.tests.test_node.AdminMessage.get_radio_response"><code class="name">var <span class="ident">get_radio_response</span></code></dt>
<dd>
<div class="desc"><p>Getter for get_radio_response.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
# take that object and discard ours.
# WARNING: We are relying on setdefault() being atomic. This is true
# in CPython but we haven&#39;t investigated others. This warning appears
# in several other locations in this file.
field_value = self._fields.setdefault(field, field_value)
return field_value</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.reboot_seconds"><code class="name">var <span class="ident">reboot_seconds</span></code></dt>
<dd>
<div class="desc"><p>Getter for reboot_seconds.</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.tests.test_node.AdminMessage.set_channel"><code class="name">var <span class="ident">set_channel</span></code></dt>
<dd>
<div class="desc"><p>Getter for set_channel.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
# take that object and discard ours.
# WARNING: We are relying on setdefault() being atomic. This is true
# in CPython but we haven&#39;t investigated others. This warning appears
# in several other locations in this file.
field_value = self._fields.setdefault(field, field_value)
return field_value</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.set_owner"><code class="name">var <span class="ident">set_owner</span></code></dt>
<dd>
<div class="desc"><p>Getter for set_owner.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
# take that object and discard ours.
# WARNING: We are relying on setdefault() being atomic. This is true
# in CPython but we haven&#39;t investigated others. This warning appears
# in several other locations in this file.
field_value = self._fields.setdefault(field, field_value)
return field_value</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_node.AdminMessage.set_radio"><code class="name">var <span class="ident">set_radio</span></code></dt>
<dd>
<div class="desc"><p>Getter for set_radio.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
# take that object and discard ours.
# WARNING: We are relying on setdefault() being atomic. This is true
# in CPython but we haven&#39;t investigated others. This warning appears
# in several other locations in this file.
field_value = self._fields.setdefault(field, field_value)
return field_value</code></pre>
</details>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="meshtastic.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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; % (_FullyQualifiedClassName(cls),
_FullyQualifiedClassName(msg.__class__)))
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.tests.test_node.AdminMessage.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):
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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests.test_node.AdminMessage.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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_node.test_node" href="#meshtastic.tests.test_node.test_node">test_node</a></code></li>
<li><code><a title="meshtastic.tests.test_node.test_node_reqquestConfig" href="#meshtastic.tests.test_node.test_node_reqquestConfig">test_node_reqquestConfig</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="meshtastic.tests.test_node.AdminMessage" href="#meshtastic.tests.test_node.AdminMessage">AdminMessage</a></code></h4>
<ul class="">
<li><code><a title="meshtastic.tests.test_node.AdminMessage.ByteSize" href="#meshtastic.tests.test_node.AdminMessage.ByteSize">ByteSize</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.CONFIRM_SET_CHANNEL_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.CONFIRM_SET_CHANNEL_FIELD_NUMBER">CONFIRM_SET_CHANNEL_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.CONFIRM_SET_RADIO_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.CONFIRM_SET_RADIO_FIELD_NUMBER">CONFIRM_SET_RADIO_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.Clear" href="#meshtastic.tests.test_node.AdminMessage.Clear">Clear</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.ClearField" href="#meshtastic.tests.test_node.AdminMessage.ClearField">ClearField</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.DESCRIPTOR" href="#meshtastic.tests.test_node.AdminMessage.DESCRIPTOR">DESCRIPTOR</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.DiscardUnknownFields" href="#meshtastic.tests.test_node.AdminMessage.DiscardUnknownFields">DiscardUnknownFields</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.EXIT_SIMULATOR_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.EXIT_SIMULATOR_FIELD_NUMBER">EXIT_SIMULATOR_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.FindInitializationErrors" href="#meshtastic.tests.test_node.AdminMessage.FindInitializationErrors">FindInitializationErrors</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.FromString" href="#meshtastic.tests.test_node.AdminMessage.FromString">FromString</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.GET_CHANNEL_REQUEST_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.GET_CHANNEL_REQUEST_FIELD_NUMBER">GET_CHANNEL_REQUEST_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.GET_CHANNEL_RESPONSE_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.GET_CHANNEL_RESPONSE_FIELD_NUMBER">GET_CHANNEL_RESPONSE_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.GET_RADIO_REQUEST_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.GET_RADIO_REQUEST_FIELD_NUMBER">GET_RADIO_REQUEST_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.GET_RADIO_RESPONSE_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.GET_RADIO_RESPONSE_FIELD_NUMBER">GET_RADIO_RESPONSE_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.HasField" href="#meshtastic.tests.test_node.AdminMessage.HasField">HasField</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.IsInitialized" href="#meshtastic.tests.test_node.AdminMessage.IsInitialized">IsInitialized</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.ListFields" href="#meshtastic.tests.test_node.AdminMessage.ListFields">ListFields</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.MergeFrom" href="#meshtastic.tests.test_node.AdminMessage.MergeFrom">MergeFrom</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.MergeFromString" href="#meshtastic.tests.test_node.AdminMessage.MergeFromString">MergeFromString</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.REBOOT_SECONDS_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.REBOOT_SECONDS_FIELD_NUMBER">REBOOT_SECONDS_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.RegisterExtension" href="#meshtastic.tests.test_node.AdminMessage.RegisterExtension">RegisterExtension</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.SET_CHANNEL_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.SET_CHANNEL_FIELD_NUMBER">SET_CHANNEL_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.SET_OWNER_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.SET_OWNER_FIELD_NUMBER">SET_OWNER_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.SET_RADIO_FIELD_NUMBER" href="#meshtastic.tests.test_node.AdminMessage.SET_RADIO_FIELD_NUMBER">SET_RADIO_FIELD_NUMBER</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.SerializePartialToString" href="#meshtastic.tests.test_node.AdminMessage.SerializePartialToString">SerializePartialToString</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.SerializeToString" href="#meshtastic.tests.test_node.AdminMessage.SerializeToString">SerializeToString</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.SetInParent" href="#meshtastic.tests.test_node.AdminMessage.SetInParent">SetInParent</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.UnknownFields" href="#meshtastic.tests.test_node.AdminMessage.UnknownFields">UnknownFields</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.WhichOneof" href="#meshtastic.tests.test_node.AdminMessage.WhichOneof">WhichOneof</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.confirm_set_channel" href="#meshtastic.tests.test_node.AdminMessage.confirm_set_channel">confirm_set_channel</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.confirm_set_radio" href="#meshtastic.tests.test_node.AdminMessage.confirm_set_radio">confirm_set_radio</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.exit_simulator" href="#meshtastic.tests.test_node.AdminMessage.exit_simulator">exit_simulator</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.get_channel_request" href="#meshtastic.tests.test_node.AdminMessage.get_channel_request">get_channel_request</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.get_channel_response" href="#meshtastic.tests.test_node.AdminMessage.get_channel_response">get_channel_response</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.get_radio_request" href="#meshtastic.tests.test_node.AdminMessage.get_radio_request">get_radio_request</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.get_radio_response" href="#meshtastic.tests.test_node.AdminMessage.get_radio_response">get_radio_response</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.reboot_seconds" href="#meshtastic.tests.test_node.AdminMessage.reboot_seconds">reboot_seconds</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.set_channel" href="#meshtastic.tests.test_node.AdminMessage.set_channel">set_channel</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.set_owner" href="#meshtastic.tests.test_node.AdminMessage.set_owner">set_owner</a></code></li>
<li><code><a title="meshtastic.tests.test_node.AdminMessage.set_radio" href="#meshtastic.tests.test_node.AdminMessage.set_radio">set_radio</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,186 @@
<!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.10.0" />
<title>meshtastic.tests.test_serial_interface API documentation</title>
<meta name="description" content="Meshtastic unit tests for serial_interface.py" />
<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.tests.test_serial_interface</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for serial_interface.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for serial_interface.py&#34;&#34;&#34;
import re
from unittest.mock import patch
import pytest
from ..serial_interface import SerialInterface
@pytest.mark.unit
@patch(&#39;serial.Serial&#39;)
@patch(&#39;meshtastic.util.findPorts&#39;, return_value=[&#39;/dev/ttyUSBfake&#39;])
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
&#34;&#34;&#34;Test that we can instantiate a SerialInterface with a single port&#34;&#34;&#34;
iface = SerialInterface(noProto=True)
iface.showInfo()
iface.localNode.showInfo()
iface.close()
mocked_findPorts.assert_called()
mocked_serial.assert_called()
@pytest.mark.unit
@patch(&#39;meshtastic.util.findPorts&#39;, return_value=[])
def test_SerialInterface_no_ports(mocked_findPorts, capsys):
&#34;&#34;&#34;Test that we can instantiate a SerialInterface with no ports&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
SerialInterface(noProto=True)
mocked_findPorts.assert_called()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.search(r&#39;Warning: No Meshtastic devices detected&#39;, out, re.MULTILINE)
assert err == &#39;&#39;
@pytest.mark.unit
@patch(&#39;meshtastic.util.findPorts&#39;, return_value=[&#39;/dev/ttyUSBfake1&#39;, &#39;/dev/ttyUSBfake2&#39;])
def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
&#34;&#34;&#34;Test that we can instantiate a SerialInterface with two ports&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
SerialInterface(noProto=True)
mocked_findPorts.assert_called()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.search(r&#39;Warning: Multiple serial ports were detected&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_serial_interface.test_SerialInterface_multiple_ports"><code class="name flex">
<span>def <span class="ident">test_SerialInterface_multiple_ports</span></span>(<span>mocked_findPorts, capsys)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a SerialInterface with two ports</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
@patch(&#39;meshtastic.util.findPorts&#39;, return_value=[&#39;/dev/ttyUSBfake1&#39;, &#39;/dev/ttyUSBfake2&#39;])
def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
&#34;&#34;&#34;Test that we can instantiate a SerialInterface with two ports&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
SerialInterface(noProto=True)
mocked_findPorts.assert_called()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.search(r&#39;Warning: Multiple serial ports were detected&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_serial_interface.test_SerialInterface_no_ports"><code class="name flex">
<span>def <span class="ident">test_SerialInterface_no_ports</span></span>(<span>mocked_findPorts, capsys)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a SerialInterface with no ports</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
@patch(&#39;meshtastic.util.findPorts&#39;, return_value=[])
def test_SerialInterface_no_ports(mocked_findPorts, capsys):
&#34;&#34;&#34;Test that we can instantiate a SerialInterface with no ports&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
SerialInterface(noProto=True)
mocked_findPorts.assert_called()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.search(r&#39;Warning: No Meshtastic devices detected&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_serial_interface.test_SerialInterface_single_port"><code class="name flex">
<span>def <span class="ident">test_SerialInterface_single_port</span></span>(<span>mocked_findPorts, mocked_serial)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a SerialInterface with a single port</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
@patch(&#39;serial.Serial&#39;)
@patch(&#39;meshtastic.util.findPorts&#39;, return_value=[&#39;/dev/ttyUSBfake&#39;])
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
&#34;&#34;&#34;Test that we can instantiate a SerialInterface with a single port&#34;&#34;&#34;
iface = SerialInterface(noProto=True)
iface.showInfo()
iface.localNode.showInfo()
iface.close()
mocked_findPorts.assert_called()
mocked_serial.assert_called()</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_serial_interface.test_SerialInterface_multiple_ports" href="#meshtastic.tests.test_serial_interface.test_SerialInterface_multiple_ports">test_SerialInterface_multiple_ports</a></code></li>
<li><code><a title="meshtastic.tests.test_serial_interface.test_SerialInterface_no_ports" href="#meshtastic.tests.test_serial_interface.test_SerialInterface_no_ports">test_SerialInterface_no_ports</a></code></li>
<li><code><a title="meshtastic.tests.test_serial_interface.test_SerialInterface_single_port" href="#meshtastic.tests.test_serial_interface.test_SerialInterface_single_port">test_SerialInterface_single_port</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,127 @@
<!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.10.0" />
<title>meshtastic.tests.test_smoke2 API documentation</title>
<meta name="description" content="Meshtastic smoke tests with 2 devices connected via USB" />
<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.tests.test_smoke2</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic smoke tests with 2 devices connected via USB</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic smoke tests with 2 devices connected via USB&#34;&#34;&#34;
import re
import subprocess
import pytest
@pytest.mark.smoke2
def test_smoke2_info():
&#34;&#34;&#34;Test --info with 2 devices connected serially&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --info&#39;)
assert re.search(r&#39;Warning: Multiple&#39;, out, re.MULTILINE)
assert return_value == 1
@pytest.mark.smoke2
def test_smoke2_test():
&#34;&#34;&#34;Test --test&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --test&#39;)
assert re.search(r&#39;Writing serial debugging&#39;, out, re.MULTILINE)
assert re.search(r&#39;Ports opened&#39;, out, re.MULTILINE)
assert re.search(r&#39;Running 5 tests&#39;, out, re.MULTILINE)
assert return_value == 0</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_smoke2.test_smoke2_info"><code class="name flex">
<span>def <span class="ident">test_smoke2_info</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test &ndash;info with 2 devices connected serially</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.smoke2
def test_smoke2_info():
&#34;&#34;&#34;Test --info with 2 devices connected serially&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --info&#39;)
assert re.search(r&#39;Warning: Multiple&#39;, out, re.MULTILINE)
assert return_value == 1</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_smoke2.test_smoke2_test"><code class="name flex">
<span>def <span class="ident">test_smoke2_test</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test &ndash;test</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.smoke2
def test_smoke2_test():
&#34;&#34;&#34;Test --test&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --test&#39;)
assert re.search(r&#39;Writing serial debugging&#39;, out, re.MULTILINE)
assert re.search(r&#39;Ports opened&#39;, out, re.MULTILINE)
assert re.search(r&#39;Running 5 tests&#39;, out, re.MULTILINE)
assert return_value == 0</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_smoke2.test_smoke2_info" href="#meshtastic.tests.test_smoke2.test_smoke2_info">test_smoke2_info</a></code></li>
<li><code><a title="meshtastic.tests.test_smoke2.test_smoke2_test" href="#meshtastic.tests.test_smoke2.test_smoke2_test">test_smoke2_test</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,115 @@
<!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.10.0" />
<title>meshtastic.tests.test_smoke_wifi API documentation</title>
<meta name="description" content="Meshtastic smoke tests a device setup with wifi …" />
<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.tests.test_smoke_wifi</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic smoke tests a device setup with wifi.</p>
<p>Need to have run the following on an esp32 device:
meshtastic &ndash;set wifi_ssid 'foo' &ndash;set wifi_password 'sekret'</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic smoke tests a device setup with wifi.
Need to have run the following on an esp32 device:
meshtastic --set wifi_ssid &#39;foo&#39; --set wifi_password &#39;sekret&#39;
&#34;&#34;&#34;
import re
import subprocess
import pytest
@pytest.mark.smokewifi
def test_smokewifi_info():
&#34;&#34;&#34;Test --info&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --info --host meshtastic.local&#39;)
assert re.search(r&#39;^Owner&#39;, out, re.MULTILINE)
assert re.search(r&#39;^My info&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Nodes in mesh&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Preferences&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Channels&#39;, out, re.MULTILINE)
assert re.search(r&#39;^ PRIMARY&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Primary channel URL&#39;, out, re.MULTILINE)
assert return_value == 0</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_smoke_wifi.test_smokewifi_info"><code class="name flex">
<span>def <span class="ident">test_smokewifi_info</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test &ndash;info</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.smokewifi
def test_smokewifi_info():
&#34;&#34;&#34;Test --info&#34;&#34;&#34;
return_value, out = subprocess.getstatusoutput(&#39;meshtastic --info --host meshtastic.local&#39;)
assert re.search(r&#39;^Owner&#39;, out, re.MULTILINE)
assert re.search(r&#39;^My info&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Nodes in mesh&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Preferences&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Channels&#39;, out, re.MULTILINE)
assert re.search(r&#39;^ PRIMARY&#39;, out, re.MULTILINE)
assert re.search(r&#39;^Primary channel URL&#39;, out, re.MULTILINE)
assert return_value == 0</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_smoke_wifi.test_smokewifi_info" href="#meshtastic.tests.test_smoke_wifi.test_smokewifi_info">test_smokewifi_info</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,98 @@
<!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.10.0" />
<title>meshtastic.tests.test_stream_interface API documentation</title>
<meta name="description" content="Meshtastic unit tests for stream_interface.py" />
<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.tests.test_stream_interface</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for stream_interface.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for stream_interface.py&#34;&#34;&#34;
import pytest
from ..stream_interface import StreamInterface
@pytest.mark.unit
def test_StreamInterface():
&#34;&#34;&#34;Test that we can instantiate a StreamInterface&#34;&#34;&#34;
with pytest.raises(Exception) as pytest_wrapped_e:
StreamInterface(noProto=True)
assert pytest_wrapped_e.type == Exception</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_stream_interface.test_StreamInterface"><code class="name flex">
<span>def <span class="ident">test_StreamInterface</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a StreamInterface</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_StreamInterface():
&#34;&#34;&#34;Test that we can instantiate a StreamInterface&#34;&#34;&#34;
with pytest.raises(Exception) as pytest_wrapped_e:
StreamInterface(noProto=True)
assert pytest_wrapped_e.type == Exception</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_stream_interface.test_StreamInterface" href="#meshtastic.tests.test_stream_interface.test_StreamInterface">test_StreamInterface</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,120 @@
<!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.10.0" />
<title>meshtastic.tests.test_tcp_interface API documentation</title>
<meta name="description" content="Meshtastic unit tests for tcp_interface.py" />
<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.tests.test_tcp_interface</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for tcp_interface.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for tcp_interface.py&#34;&#34;&#34;
import re
from unittest.mock import patch
import pytest
from ..tcp_interface import TCPInterface
@pytest.mark.unit
def test_TCPInterface(capsys):
&#34;&#34;&#34;Test that we can instantiate a TCPInterface&#34;&#34;&#34;
with patch(&#39;socket.socket&#39;) as mock_socket:
iface = TCPInterface(hostname=&#39;localhost&#39;, noProto=True)
iface.showInfo()
iface.localNode.showInfo()
out, err = capsys.readouterr()
assert re.search(r&#39;Owner: None \(None\)&#39;, out, re.MULTILINE)
assert re.search(r&#39;Nodes&#39;, out, re.MULTILINE)
assert re.search(r&#39;Preferences&#39;, out, re.MULTILINE)
assert re.search(r&#39;Channels&#39;, out, re.MULTILINE)
assert re.search(r&#39;Primary channel URL&#39;, out, re.MULTILINE)
assert err == &#39;&#39;
assert mock_socket.called
iface.close()</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_tcp_interface.test_TCPInterface"><code class="name flex">
<span>def <span class="ident">test_TCPInterface</span></span>(<span>capsys)</span>
</code></dt>
<dd>
<div class="desc"><p>Test that we can instantiate a TCPInterface</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_TCPInterface(capsys):
&#34;&#34;&#34;Test that we can instantiate a TCPInterface&#34;&#34;&#34;
with patch(&#39;socket.socket&#39;) as mock_socket:
iface = TCPInterface(hostname=&#39;localhost&#39;, noProto=True)
iface.showInfo()
iface.localNode.showInfo()
out, err = capsys.readouterr()
assert re.search(r&#39;Owner: None \(None\)&#39;, out, re.MULTILINE)
assert re.search(r&#39;Nodes&#39;, out, re.MULTILINE)
assert re.search(r&#39;Preferences&#39;, out, re.MULTILINE)
assert re.search(r&#39;Channels&#39;, out, re.MULTILINE)
assert re.search(r&#39;Primary channel URL&#39;, out, re.MULTILINE)
assert err == &#39;&#39;
assert mock_socket.called
iface.close()</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_tcp_interface.test_TCPInterface" href="#meshtastic.tests.test_tcp_interface.test_TCPInterface">test_TCPInterface</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,455 @@
<!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.10.0" />
<title>meshtastic.tests.test_util API documentation</title>
<meta name="description" content="Meshtastic unit tests for util.py" />
<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.tests.test_util</code></h1>
</header>
<section id="section-intro">
<p>Meshtastic unit tests for util.py</p>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34;Meshtastic unit tests for util.py&#34;&#34;&#34;
import re
import pytest
from meshtastic.util import fixme, stripnl, pskToString, our_exit, support_info, genPSK256, fromStr, fromPSK
@pytest.mark.unit
def test_genPSK256():
&#34;&#34;&#34;Test genPSK256&#34;&#34;&#34;
assert genPSK256() != &#39;&#39;
@pytest.mark.unit
def test_fromStr():
&#34;&#34;&#34;Test fromStr&#34;&#34;&#34;
assert fromStr(&#39;&#39;) == b&#39;&#39;
assert fromStr(&#39;0x12&#39;) == b&#39;\x12&#39;
assert fromStr(&#39;t&#39;)
assert fromStr(&#39;T&#39;)
assert fromStr(&#39;true&#39;)
assert fromStr(&#39;True&#39;)
assert fromStr(&#39;yes&#39;)
assert fromStr(&#39;Yes&#39;)
assert fromStr(&#39;f&#39;) is False
assert fromStr(&#39;F&#39;) is False
assert fromStr(&#39;false&#39;) is False
assert fromStr(&#39;False&#39;) is False
assert fromStr(&#39;no&#39;) is False
assert fromStr(&#39;No&#39;) is False
assert fromStr(&#39;100.01&#39;) == 100.01
assert fromStr(&#39;123&#39;) == 123
assert fromStr(&#39;abc&#39;) == &#39;abc&#39;
@pytest.mark.unit
def test_fromPSK():
&#34;&#34;&#34;Test fromPSK&#34;&#34;&#34;
assert fromPSK(&#39;random&#39;) != &#39;&#39;
assert fromPSK(&#39;none&#39;) == b&#39;\x00&#39;
assert fromPSK(&#39;default&#39;) == b&#39;\x01&#39;
assert fromPSK(&#39;simple22&#39;) == b&#39;\x17&#39;
assert fromPSK(&#39;trash&#39;) == &#39;trash&#39;
@pytest.mark.unit
def test_stripnl():
&#34;&#34;&#34;Test stripnl&#34;&#34;&#34;
assert stripnl(&#39;&#39;) == &#39;&#39;
assert stripnl(&#39;a\n&#39;) == &#39;a&#39;
assert stripnl(&#39; a \n &#39;) == &#39;a&#39;
assert stripnl(&#39;a\nb&#39;) == &#39;a b&#39;
@pytest.mark.unit
def test_pskToString_empty_string():
&#34;&#34;&#34;Test pskToString empty string&#34;&#34;&#34;
assert pskToString(&#39;&#39;) == &#39;unencrypted&#39;
@pytest.mark.unit
def test_pskToString_string():
&#34;&#34;&#34;Test pskToString string&#34;&#34;&#34;
assert pskToString(&#39;hunter123&#39;) == &#39;secret&#39;
@pytest.mark.unit
def test_pskToString_one_byte_zero_value():
&#34;&#34;&#34;Test pskToString one byte that is value of 0&#34;&#34;&#34;
assert pskToString(bytes([0x00])) == &#39;unencrypted&#39;
@pytest.mark.unit
def test_pskToString_one_byte_non_zero_value():
&#34;&#34;&#34;Test pskToString one byte that is non-zero&#34;&#34;&#34;
assert pskToString(bytes([0x01])) == &#39;default&#39;
@pytest.mark.unit
def test_pskToString_many_bytes():
&#34;&#34;&#34;Test pskToString many bytes&#34;&#34;&#34;
assert pskToString(bytes([0x02, 0x01])) == &#39;secret&#39;
@pytest.mark.unit
def test_pskToString_simple():
&#34;&#34;&#34;Test pskToString simple&#34;&#34;&#34;
assert pskToString(bytes([0x03])) == &#39;simple2&#39;
@pytest.mark.unit
def test_our_exit_zero_return_value():
&#34;&#34;&#34;Test our_exit with a zero return value&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
our_exit(&#34;Warning: Some message&#34;, 0)
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 0
@pytest.mark.unit
def test_our_exit_non_zero_return_value():
&#34;&#34;&#34;Test our_exit with a non-zero return value&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
our_exit(&#34;Error: Some message&#34;, 1)
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
@pytest.mark.unit
def test_fixme():
&#34;&#34;&#34;Test fixme&#34;&#34;&#34;
with pytest.raises(Exception) as pytest_wrapped_e:
fixme(&#34;some exception&#34;)
assert pytest_wrapped_e.type == Exception
@pytest.mark.unit
def test_support_info(capsys):
&#34;&#34;&#34;Test support_info&#34;&#34;&#34;
support_info()
out, err = capsys.readouterr()
assert re.search(r&#39;System&#39;, out, re.MULTILINE)
assert re.search(r&#39;Platform&#39;, out, re.MULTILINE)
assert re.search(r&#39;Machine&#39;, out, re.MULTILINE)
assert re.search(r&#39;Executable&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="meshtastic.tests.test_util.test_fixme"><code class="name flex">
<span>def <span class="ident">test_fixme</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test fixme</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_fixme():
&#34;&#34;&#34;Test fixme&#34;&#34;&#34;
with pytest.raises(Exception) as pytest_wrapped_e:
fixme(&#34;some exception&#34;)
assert pytest_wrapped_e.type == Exception</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_fromPSK"><code class="name flex">
<span>def <span class="ident">test_fromPSK</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test fromPSK</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_fromPSK():
&#34;&#34;&#34;Test fromPSK&#34;&#34;&#34;
assert fromPSK(&#39;random&#39;) != &#39;&#39;
assert fromPSK(&#39;none&#39;) == b&#39;\x00&#39;
assert fromPSK(&#39;default&#39;) == b&#39;\x01&#39;
assert fromPSK(&#39;simple22&#39;) == b&#39;\x17&#39;
assert fromPSK(&#39;trash&#39;) == &#39;trash&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_fromStr"><code class="name flex">
<span>def <span class="ident">test_fromStr</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test fromStr</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_fromStr():
&#34;&#34;&#34;Test fromStr&#34;&#34;&#34;
assert fromStr(&#39;&#39;) == b&#39;&#39;
assert fromStr(&#39;0x12&#39;) == b&#39;\x12&#39;
assert fromStr(&#39;t&#39;)
assert fromStr(&#39;T&#39;)
assert fromStr(&#39;true&#39;)
assert fromStr(&#39;True&#39;)
assert fromStr(&#39;yes&#39;)
assert fromStr(&#39;Yes&#39;)
assert fromStr(&#39;f&#39;) is False
assert fromStr(&#39;F&#39;) is False
assert fromStr(&#39;false&#39;) is False
assert fromStr(&#39;False&#39;) is False
assert fromStr(&#39;no&#39;) is False
assert fromStr(&#39;No&#39;) is False
assert fromStr(&#39;100.01&#39;) == 100.01
assert fromStr(&#39;123&#39;) == 123
assert fromStr(&#39;abc&#39;) == &#39;abc&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_genPSK256"><code class="name flex">
<span>def <span class="ident">test_genPSK256</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test genPSK256</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_genPSK256():
&#34;&#34;&#34;Test genPSK256&#34;&#34;&#34;
assert genPSK256() != &#39;&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_our_exit_non_zero_return_value"><code class="name flex">
<span>def <span class="ident">test_our_exit_non_zero_return_value</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test our_exit with a non-zero return value</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_our_exit_non_zero_return_value():
&#34;&#34;&#34;Test our_exit with a non-zero return value&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
our_exit(&#34;Error: Some message&#34;, 1)
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_our_exit_zero_return_value"><code class="name flex">
<span>def <span class="ident">test_our_exit_zero_return_value</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test our_exit with a zero return value</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_our_exit_zero_return_value():
&#34;&#34;&#34;Test our_exit with a zero return value&#34;&#34;&#34;
with pytest.raises(SystemExit) as pytest_wrapped_e:
our_exit(&#34;Warning: Some message&#34;, 0)
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 0</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_pskToString_empty_string"><code class="name flex">
<span>def <span class="ident">test_pskToString_empty_string</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test pskToString empty string</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_pskToString_empty_string():
&#34;&#34;&#34;Test pskToString empty string&#34;&#34;&#34;
assert pskToString(&#39;&#39;) == &#39;unencrypted&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_pskToString_many_bytes"><code class="name flex">
<span>def <span class="ident">test_pskToString_many_bytes</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test pskToString many bytes</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_pskToString_many_bytes():
&#34;&#34;&#34;Test pskToString many bytes&#34;&#34;&#34;
assert pskToString(bytes([0x02, 0x01])) == &#39;secret&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_pskToString_one_byte_non_zero_value"><code class="name flex">
<span>def <span class="ident">test_pskToString_one_byte_non_zero_value</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test pskToString one byte that is non-zero</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_pskToString_one_byte_non_zero_value():
&#34;&#34;&#34;Test pskToString one byte that is non-zero&#34;&#34;&#34;
assert pskToString(bytes([0x01])) == &#39;default&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_pskToString_one_byte_zero_value"><code class="name flex">
<span>def <span class="ident">test_pskToString_one_byte_zero_value</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test pskToString one byte that is value of 0</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_pskToString_one_byte_zero_value():
&#34;&#34;&#34;Test pskToString one byte that is value of 0&#34;&#34;&#34;
assert pskToString(bytes([0x00])) == &#39;unencrypted&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_pskToString_simple"><code class="name flex">
<span>def <span class="ident">test_pskToString_simple</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test pskToString simple</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_pskToString_simple():
&#34;&#34;&#34;Test pskToString simple&#34;&#34;&#34;
assert pskToString(bytes([0x03])) == &#39;simple2&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_pskToString_string"><code class="name flex">
<span>def <span class="ident">test_pskToString_string</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test pskToString string</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_pskToString_string():
&#34;&#34;&#34;Test pskToString string&#34;&#34;&#34;
assert pskToString(&#39;hunter123&#39;) == &#39;secret&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_stripnl"><code class="name flex">
<span>def <span class="ident">test_stripnl</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Test stripnl</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_stripnl():
&#34;&#34;&#34;Test stripnl&#34;&#34;&#34;
assert stripnl(&#39;&#39;) == &#39;&#39;
assert stripnl(&#39;a\n&#39;) == &#39;a&#39;
assert stripnl(&#39; a \n &#39;) == &#39;a&#39;
assert stripnl(&#39;a\nb&#39;) == &#39;a b&#39;</code></pre>
</details>
</dd>
<dt id="meshtastic.tests.test_util.test_support_info"><code class="name flex">
<span>def <span class="ident">test_support_info</span></span>(<span>capsys)</span>
</code></dt>
<dd>
<div class="desc"><p>Test support_info</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@pytest.mark.unit
def test_support_info(capsys):
&#34;&#34;&#34;Test support_info&#34;&#34;&#34;
support_info()
out, err = capsys.readouterr()
assert re.search(r&#39;System&#39;, out, re.MULTILINE)
assert re.search(r&#39;Platform&#39;, out, re.MULTILINE)
assert re.search(r&#39;Machine&#39;, out, re.MULTILINE)
assert re.search(r&#39;Executable&#39;, out, re.MULTILINE)
assert err == &#39;&#39;</code></pre>
</details>
</dd>
</dl>
</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.tests" href="index.html">meshtastic.tests</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="meshtastic.tests.test_util.test_fixme" href="#meshtastic.tests.test_util.test_fixme">test_fixme</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_fromPSK" href="#meshtastic.tests.test_util.test_fromPSK">test_fromPSK</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_fromStr" href="#meshtastic.tests.test_util.test_fromStr">test_fromStr</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_genPSK256" href="#meshtastic.tests.test_util.test_genPSK256">test_genPSK256</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_our_exit_non_zero_return_value" href="#meshtastic.tests.test_util.test_our_exit_non_zero_return_value">test_our_exit_non_zero_return_value</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_our_exit_zero_return_value" href="#meshtastic.tests.test_util.test_our_exit_zero_return_value">test_our_exit_zero_return_value</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_pskToString_empty_string" href="#meshtastic.tests.test_util.test_pskToString_empty_string">test_pskToString_empty_string</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_pskToString_many_bytes" href="#meshtastic.tests.test_util.test_pskToString_many_bytes">test_pskToString_many_bytes</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_pskToString_one_byte_non_zero_value" href="#meshtastic.tests.test_util.test_pskToString_one_byte_non_zero_value">test_pskToString_one_byte_non_zero_value</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_pskToString_one_byte_zero_value" href="#meshtastic.tests.test_util.test_pskToString_one_byte_zero_value">test_pskToString_one_byte_zero_value</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_pskToString_simple" href="#meshtastic.tests.test_util.test_pskToString_simple">test_pskToString_simple</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_pskToString_string" href="#meshtastic.tests.test_util.test_pskToString_string">test_pskToString_string</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_stripnl" href="#meshtastic.tests.test_util.test_stripnl">test_stripnl</a></code></li>
<li><code><a title="meshtastic.tests.test_util.test_support_info" href="#meshtastic.tests.test_util.test_support_info">test_support_info</a></code></li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>

View File

@@ -39,7 +39,7 @@
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; Code for IP tunnel over a mesh
<pre><code class="python">&#34;&#34;&#34;Code for IP tunnel over a mesh
# Note python-pytuntap was too buggy
# using pip3 install pytap2
@@ -59,6 +59,9 @@
import logging
import threading
from pubsub import pub
from pytap2 import TapDevice
from . import portnums_pb2
# A new non standard log level that is lower level than DEBUG
@@ -126,8 +129,8 @@ class Tunnel:
global tunnelInstance
tunnelInstance = self
logging.info(
&#34;Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work). Mesh members:&#34;)
logging.info(&#34;Starting IP to mesh tunnel (you must be root for this *pre-alpha* &#34;\
&#34;feature to work). Mesh members:&#34;)
pub.subscribe(onTunnelReceive, &#34;meshtastic.receive.data.IP_TUNNEL_APP&#34;)
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
@@ -139,7 +142,6 @@ class Tunnel:
logging.debug(&#34;creating TUN device with MTU=200&#34;)
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
from pytap2 import TapDevice
self.tun = TapDevice(name=&#34;mesh&#34;)
self.tun.up()
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
@@ -149,14 +151,15 @@ class Tunnel:
self._rxThread.start()
def onReceive(self, packet):
&#34;&#34;&#34;onReceive&#34;&#34;&#34;
p = packet[&#34;decoded&#34;][&#34;payload&#34;]
if packet[&#34;from&#34;] == self.iface.myInfo.my_node_num:
logging.debug(&#34;Ignoring message we sent&#34;)
else:
logging.debug(
f&#34;Received mesh tunnel message type={type(p)} len={len(p)}&#34;)
# we don&#39;t really need to check for filtering here (sender should have checked), but this provides
# useful debug printing on types of packets received
# we don&#39;t really need to check for filtering here (sender should have checked),
# but this provides useful debug printing on types of packets received
if not self._shouldFilterPacket(p):
self.tun.write(p)
@@ -175,8 +178,8 @@ class Tunnel:
icmpType = p[20]
icmpCode = p[21]
checksum = p[22:24]
logging.debug(
f&#34;forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}&#34;)
# pylint: disable=line-too-long
logging.debug(f&#34;forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}&#34;)
# reply to pings (swap src and dest but keep rest of packet unchanged)
#pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
# tap.write(pingback)
@@ -195,14 +198,12 @@ class Tunnel:
destport = readnet_u16(p, subheader + 2)
if destport in tcpBlacklist:
ignore = True
logging.log(
LOG_TRACE, f&#34;ignoring blacklisted TCP port {destport}&#34;)
logging.log(LOG_TRACE, f&#34;ignoring blacklisted TCP port {destport}&#34;)
else:
logging.debug(
f&#34;forwarding tcp srcport={srcport}, destport={destport}&#34;)
logging.debug(f&#34;forwarding tcp srcport={srcport}, destport={destport}&#34;)
else:
logging.warning(
f&#34;forwarding unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destAddr)}&#34;)
logging.warning(f&#34;forwarding unexpected protocol 0x{protocol:02x}, &#34;\
&#34;src={ipstr(srcaddr)}, dest={ipstr(destAddr)}&#34;)
return ignore
@@ -238,15 +239,14 @@ class Tunnel:
&#34;&#34;&#34;Forward the provided IP packet into the mesh&#34;&#34;&#34;
nodeId = self._ipToNodeId(destAddr)
if nodeId is not None:
logging.debug(
f&#34;Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}&#34;)
logging.debug(f&#34;Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}&#34;)
self.iface.sendData(
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
else:
logging.warning(
f&#34;Dropping packet because no node found for destIP={ipstr(destAddr)}&#34;)
logging.warning(f&#34;Dropping packet because no node found for destIP={ipstr(destAddr)}&#34;)
def close(self):
&#34;&#34;&#34;Close&#34;&#34;&#34;
self.tun.close()</code></pre>
</details>
</section>
@@ -370,8 +370,8 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
global tunnelInstance
tunnelInstance = self
logging.info(
&#34;Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work). Mesh members:&#34;)
logging.info(&#34;Starting IP to mesh tunnel (you must be root for this *pre-alpha* &#34;\
&#34;feature to work). Mesh members:&#34;)
pub.subscribe(onTunnelReceive, &#34;meshtastic.receive.data.IP_TUNNEL_APP&#34;)
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
@@ -383,7 +383,6 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
logging.debug(&#34;creating TUN device with MTU=200&#34;)
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
from pytap2 import TapDevice
self.tun = TapDevice(name=&#34;mesh&#34;)
self.tun.up()
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
@@ -393,14 +392,15 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
self._rxThread.start()
def onReceive(self, packet):
&#34;&#34;&#34;onReceive&#34;&#34;&#34;
p = packet[&#34;decoded&#34;][&#34;payload&#34;]
if packet[&#34;from&#34;] == self.iface.myInfo.my_node_num:
logging.debug(&#34;Ignoring message we sent&#34;)
else:
logging.debug(
f&#34;Received mesh tunnel message type={type(p)} len={len(p)}&#34;)
# we don&#39;t really need to check for filtering here (sender should have checked), but this provides
# useful debug printing on types of packets received
# we don&#39;t really need to check for filtering here (sender should have checked),
# but this provides useful debug printing on types of packets received
if not self._shouldFilterPacket(p):
self.tun.write(p)
@@ -419,8 +419,8 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
icmpType = p[20]
icmpCode = p[21]
checksum = p[22:24]
logging.debug(
f&#34;forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}&#34;)
# pylint: disable=line-too-long
logging.debug(f&#34;forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}&#34;)
# reply to pings (swap src and dest but keep rest of packet unchanged)
#pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
# tap.write(pingback)
@@ -439,14 +439,12 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
destport = readnet_u16(p, subheader + 2)
if destport in tcpBlacklist:
ignore = True
logging.log(
LOG_TRACE, f&#34;ignoring blacklisted TCP port {destport}&#34;)
logging.log(LOG_TRACE, f&#34;ignoring blacklisted TCP port {destport}&#34;)
else:
logging.debug(
f&#34;forwarding tcp srcport={srcport}, destport={destport}&#34;)
logging.debug(f&#34;forwarding tcp srcport={srcport}, destport={destport}&#34;)
else:
logging.warning(
f&#34;forwarding unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destAddr)}&#34;)
logging.warning(f&#34;forwarding unexpected protocol 0x{protocol:02x}, &#34;\
&#34;src={ipstr(srcaddr)}, dest={ipstr(destAddr)}&#34;)
return ignore
@@ -482,15 +480,14 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
&#34;&#34;&#34;Forward the provided IP packet into the mesh&#34;&#34;&#34;
nodeId = self._ipToNodeId(destAddr)
if nodeId is not None:
logging.debug(
f&#34;Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}&#34;)
logging.debug(f&#34;Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}&#34;)
self.iface.sendData(
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
else:
logging.warning(
f&#34;Dropping packet because no node found for destIP={ipstr(destAddr)}&#34;)
logging.warning(f&#34;Dropping packet because no node found for destIP={ipstr(destAddr)}&#34;)
def close(self):
&#34;&#34;&#34;Close&#34;&#34;&#34;
self.tun.close()</code></pre>
</details>
<h3>Methods</h3>
@@ -499,12 +496,13 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
<span>def <span class="ident">close</span></span>(<span>self)</span>
</code></dt>
<dd>
<div class="desc"></div>
<div class="desc"><p>Close</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def close(self):
&#34;&#34;&#34;Close&#34;&#34;&#34;
self.tun.close()</code></pre>
</details>
</dd>
@@ -512,20 +510,21 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
<span>def <span class="ident">onReceive</span></span>(<span>self, packet)</span>
</code></dt>
<dd>
<div class="desc"></div>
<div class="desc"><p>onReceive</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def onReceive(self, packet):
&#34;&#34;&#34;onReceive&#34;&#34;&#34;
p = packet[&#34;decoded&#34;][&#34;payload&#34;]
if packet[&#34;from&#34;] == self.iface.myInfo.my_node_num:
logging.debug(&#34;Ignoring message we sent&#34;)
else:
logging.debug(
f&#34;Received mesh tunnel message type={type(p)} len={len(p)}&#34;)
# we don&#39;t really need to check for filtering here (sender should have checked), but this provides
# useful debug printing on types of packets received
# we don&#39;t really need to check for filtering here (sender should have checked),
# but this provides useful debug printing on types of packets received
if not self._shouldFilterPacket(p):
self.tun.write(p)</code></pre>
</details>
@@ -543,13 +542,11 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
&#34;&#34;&#34;Forward the provided IP packet into the mesh&#34;&#34;&#34;
nodeId = self._ipToNodeId(destAddr)
if nodeId is not None:
logging.debug(
f&#34;Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}&#34;)
logging.debug(f&#34;Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}&#34;)
self.iface.sendData(
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
else:
logging.warning(
f&#34;Dropping packet because no node found for destIP={ipstr(destAddr)}&#34;)</code></pre>
logging.warning(f&#34;Dropping packet because no node found for destIP={ipstr(destAddr)}&#34;)</code></pre>
</details>
</dd>
</dl>

View File

@@ -27,10 +27,11 @@
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">&#34;&#34;&#34; Utility functions.
<pre><code class="python">&#34;&#34;&#34;Utility functions.
&#34;&#34;&#34;
import traceback
from queue import Queue
import os
import sys
import time
import platform
@@ -44,6 +45,56 @@ import pkg_resources
blacklistVids = dict.fromkeys([0x1366])
def genPSK256():
&#34;&#34;&#34;Generate a random preshared key&#34;&#34;&#34;
return os.urandom(32)
def fromPSK(valstr):
&#34;&#34;&#34;A special version of fromStr that assumes the user is trying to set a PSK.
In that case we also allow &#34;none&#34;, &#34;default&#34; or &#34;random&#34; (to have python generate one), or simpleN
&#34;&#34;&#34;
if valstr == &#34;random&#34;:
return genPSK256()
elif valstr == &#34;none&#34;:
return bytes([0]) # Use the &#39;no encryption&#39; PSK
elif valstr == &#34;default&#34;:
return bytes([1]) # Use default channel psk
elif valstr.startswith(&#34;simple&#34;):
# Use one of the single byte encodings
return bytes([int(valstr[6:]) + 1])
else:
return fromStr(valstr)
def fromStr(valstr):
&#34;&#34;&#34;Try to parse as int, float or bool (and fallback to a string as last resort)
Returns: an int, bool, float, str or byte array (for strings of hex digits)
Args:
valstr (string): A user provided string
&#34;&#34;&#34;
if len(valstr) == 0: # Treat an emptystring as an empty bytes
val = bytes()
elif valstr.startswith(&#39;0x&#39;):
# if needed convert to string with asBytes.decode(&#39;utf-8&#39;)
val = bytes.fromhex(valstr[2:])
elif valstr.lower() in {&#34;t&#34;, &#34;true&#34;, &#34;yes&#34;}:
val = True
elif valstr.lower() in {&#34;f&#34;, &#34;false&#34;, &#34;no&#34;}:
val = False
else:
try:
val = int(valstr)
except ValueError:
try:
val = float(valstr)
except ValueError:
val = valstr # Not a float or an int, assume string
return val
def pskToString(psk: bytes):
&#34;&#34;&#34;Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string&#34;&#34;&#34;
if len(psk) == 0:
@@ -61,13 +112,13 @@ def pskToString(psk: bytes):
def stripnl(s):
&#34;&#34;&#34;remove newlines from a string (and remove extra whitespace)&#34;&#34;&#34;
&#34;&#34;&#34;Remove newlines from a string (and remove extra whitespace)&#34;&#34;&#34;
s = str(s).replace(&#34;\n&#34;, &#34; &#34;)
return &#39; &#39;.join(s.split())
def fixme(message):
&#34;&#34;&#34;raise an exception for things that needs to be fixed&#34;&#34;&#34;
&#34;&#34;&#34;Raise an exception for things that needs to be fixed&#34;&#34;&#34;
raise Exception(f&#34;FIXME: {message}&#34;)
@@ -153,7 +204,8 @@ def our_exit(message, return_value = 1):
def support_info():
&#34;&#34;&#34;Print out info that is helping in support of the cli.&#34;&#34;&#34;
&#34;&#34;&#34;Print out info that helps troubleshooting of the cli.&#34;&#34;&#34;
print(&#39;&#39;)
print(&#39;If having issues with meshtastic cli or python library&#39;)
print(&#39;or wish to make feature requests, visit:&#39;)
print(&#39;https://github.com/meshtastic/Meshtastic-python/issues&#39;)
@@ -162,13 +214,14 @@ def support_info():
print(&#39; Platform: {0}&#39;.format(platform.platform()))
print(&#39; Release: {0}&#39;.format(platform.uname().release))
print(&#39; Machine: {0}&#39;.format(platform.uname().machine))
print(&#39; Encoding (stdin): {0}&#39;.format(sys.stdin.encoding))
print(&#39; Encoding (stdout): {0}&#39;.format(sys.stdout.encoding))
print(&#39; meshtastic: v{0}&#39;.format(pkg_resources.require(&#39;meshtastic&#39;)[0].version))
print(&#39; Executable: {0}&#39;.format(sys.argv[0]))
print(&#39; Python: {0} {1} {2}&#39;.format(platform.python_version(),
platform.python_implementation(), platform.python_compiler()))
print(&#39;&#39;)
print(&#39;Please add the output from the command: meshtastic --info&#39;)
print(&#39;&#39;)</code></pre>
print(&#39;Please add the output from the command: meshtastic --info&#39;)</code></pre>
</details>
</section>
<section>
@@ -223,16 +276,100 @@ def support_info():
<span>def <span class="ident">fixme</span></span>(<span>message)</span>
</code></dt>
<dd>
<div class="desc"><p>raise an exception for things that needs to be fixed</p></div>
<div class="desc"><p>Raise an exception for things that needs to be fixed</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def fixme(message):
&#34;&#34;&#34;raise an exception for things that needs to be fixed&#34;&#34;&#34;
&#34;&#34;&#34;Raise an exception for things that needs to be fixed&#34;&#34;&#34;
raise Exception(f&#34;FIXME: {message}&#34;)</code></pre>
</details>
</dd>
<dt id="meshtastic.util.fromPSK"><code class="name flex">
<span>def <span class="ident">fromPSK</span></span>(<span>valstr)</span>
</code></dt>
<dd>
<div class="desc"><p>A special version of fromStr that assumes the user is trying to set a PSK.
In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def fromPSK(valstr):
&#34;&#34;&#34;A special version of fromStr that assumes the user is trying to set a PSK.
In that case we also allow &#34;none&#34;, &#34;default&#34; or &#34;random&#34; (to have python generate one), or simpleN
&#34;&#34;&#34;
if valstr == &#34;random&#34;:
return genPSK256()
elif valstr == &#34;none&#34;:
return bytes([0]) # Use the &#39;no encryption&#39; PSK
elif valstr == &#34;default&#34;:
return bytes([1]) # Use default channel psk
elif valstr.startswith(&#34;simple&#34;):
# Use one of the single byte encodings
return bytes([int(valstr[6:]) + 1])
else:
return fromStr(valstr)</code></pre>
</details>
</dd>
<dt id="meshtastic.util.fromStr"><code class="name flex">
<span>def <span class="ident">fromStr</span></span>(<span>valstr)</span>
</code></dt>
<dd>
<div class="desc"><p>Try to parse as int, float or bool (and fallback to a string as last resort)</p>
<p>Returns: an int, bool, float, str or byte array (for strings of hex digits)</p>
<h2 id="args">Args</h2>
<dl>
<dt><strong><code>valstr</code></strong> :&ensp;<code>string</code></dt>
<dd>A user provided string</dd>
</dl></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def fromStr(valstr):
&#34;&#34;&#34;Try to parse as int, float or bool (and fallback to a string as last resort)
Returns: an int, bool, float, str or byte array (for strings of hex digits)
Args:
valstr (string): A user provided string
&#34;&#34;&#34;
if len(valstr) == 0: # Treat an emptystring as an empty bytes
val = bytes()
elif valstr.startswith(&#39;0x&#39;):
# if needed convert to string with asBytes.decode(&#39;utf-8&#39;)
val = bytes.fromhex(valstr[2:])
elif valstr.lower() in {&#34;t&#34;, &#34;true&#34;, &#34;yes&#34;}:
val = True
elif valstr.lower() in {&#34;f&#34;, &#34;false&#34;, &#34;no&#34;}:
val = False
else:
try:
val = int(valstr)
except ValueError:
try:
val = float(valstr)
except ValueError:
val = valstr # Not a float or an int, assume string
return val</code></pre>
</details>
</dd>
<dt id="meshtastic.util.genPSK256"><code class="name flex">
<span>def <span class="ident">genPSK256</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Generate a random preshared key</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def genPSK256():
&#34;&#34;&#34;Generate a random preshared key&#34;&#34;&#34;
return os.urandom(32)</code></pre>
</details>
</dd>
<dt id="meshtastic.util.our_exit"><code class="name flex">
<span>def <span class="ident">our_exit</span></span>(<span>message, return_value=1)</span>
</code></dt>
@@ -280,13 +417,13 @@ return_value defaults to 1 (non-successful)</p></div>
<span>def <span class="ident">stripnl</span></span>(<span>s)</span>
</code></dt>
<dd>
<div class="desc"><p>remove newlines from a string (and remove extra whitespace)</p></div>
<div class="desc"><p>Remove newlines from a string (and remove extra whitespace)</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def stripnl(s):
&#34;&#34;&#34;remove newlines from a string (and remove extra whitespace)&#34;&#34;&#34;
&#34;&#34;&#34;Remove newlines from a string (and remove extra whitespace)&#34;&#34;&#34;
s = str(s).replace(&#34;\n&#34;, &#34; &#34;)
return &#39; &#39;.join(s.split())</code></pre>
</details>
@@ -295,13 +432,14 @@ return_value defaults to 1 (non-successful)</p></div>
<span>def <span class="ident">support_info</span></span>(<span>)</span>
</code></dt>
<dd>
<div class="desc"><p>Print out info that is helping in support of the cli.</p></div>
<div class="desc"><p>Print out info that helps troubleshooting of the cli.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def support_info():
&#34;&#34;&#34;Print out info that is helping in support of the cli.&#34;&#34;&#34;
&#34;&#34;&#34;Print out info that helps troubleshooting of the cli.&#34;&#34;&#34;
print(&#39;&#39;)
print(&#39;If having issues with meshtastic cli or python library&#39;)
print(&#39;or wish to make feature requests, visit:&#39;)
print(&#39;https://github.com/meshtastic/Meshtastic-python/issues&#39;)
@@ -310,13 +448,14 @@ return_value defaults to 1 (non-successful)</p></div>
print(&#39; Platform: {0}&#39;.format(platform.platform()))
print(&#39; Release: {0}&#39;.format(platform.uname().release))
print(&#39; Machine: {0}&#39;.format(platform.uname().machine))
print(&#39; Encoding (stdin): {0}&#39;.format(sys.stdin.encoding))
print(&#39; Encoding (stdout): {0}&#39;.format(sys.stdout.encoding))
print(&#39; meshtastic: v{0}&#39;.format(pkg_resources.require(&#39;meshtastic&#39;)[0].version))
print(&#39; Executable: {0}&#39;.format(sys.argv[0]))
print(&#39; Python: {0} {1} {2}&#39;.format(platform.python_version(),
platform.python_implementation(), platform.python_compiler()))
print(&#39;&#39;)
print(&#39;Please add the output from the command: meshtastic --info&#39;)
print(&#39;&#39;)</code></pre>
print(&#39;Please add the output from the command: meshtastic --info&#39;)</code></pre>
</details>
</dd>
</dl>
@@ -482,6 +621,9 @@ return_value defaults to 1 (non-successful)</p></div>
<li><code><a title="meshtastic.util.catchAndIgnore" href="#meshtastic.util.catchAndIgnore">catchAndIgnore</a></code></li>
<li><code><a title="meshtastic.util.findPorts" href="#meshtastic.util.findPorts">findPorts</a></code></li>
<li><code><a title="meshtastic.util.fixme" href="#meshtastic.util.fixme">fixme</a></code></li>
<li><code><a title="meshtastic.util.fromPSK" href="#meshtastic.util.fromPSK">fromPSK</a></code></li>
<li><code><a title="meshtastic.util.fromStr" href="#meshtastic.util.fromStr">fromStr</a></code></li>
<li><code><a title="meshtastic.util.genPSK256" href="#meshtastic.util.genPSK256">genPSK256</a></code></li>
<li><code><a title="meshtastic.util.our_exit" href="#meshtastic.util.our_exit">our_exit</a></code></li>
<li><code><a title="meshtastic.util.pskToString" href="#meshtastic.util.pskToString">pskToString</a></code></li>
<li><code><a title="meshtastic.util.stripnl" href="#meshtastic.util.stripnl">stripnl</a></code></li>