mirror of
https://github.com/meshtastic/python.git
synced 2026-01-06 14:57:58 -05:00
Bump version to 1.2.45
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">""" Bluetooth interface
|
||||
<pre><code class="python">"""Bluetooth interface
|
||||
"""
|
||||
import logging
|
||||
import pygatt
|
||||
@@ -44,22 +44,27 @@ FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453"
|
||||
class BLEInterface(MeshInterface):
|
||||
"""A not quite ready - FIXME - BLE interface to devices"""
|
||||
|
||||
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"Connecting to {self.address}")
|
||||
self.device = self.adapter.connect(address)
|
||||
if not noProto:
|
||||
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
|
||||
self.adapter.start()
|
||||
logging.debug(f"Connecting to {self.address}")
|
||||
self.device = self.adapter.connect(address)
|
||||
else:
|
||||
self.adapter = None
|
||||
self.device = None
|
||||
logging.debug("Connected to device")
|
||||
# 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):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
@@ -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 – If True, don't try to run our protocol on the link - just be a dumb serial client.</p></div>
|
||||
noProto – 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 – If True, don't try to run our protocol on the link - just be a d
|
||||
<pre><code class="python">class BLEInterface(MeshInterface):
|
||||
"""A not quite ready - FIXME - BLE interface to devices"""
|
||||
|
||||
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"Connecting to {self.address}")
|
||||
self.device = self.adapter.connect(address)
|
||||
if not noProto:
|
||||
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
|
||||
self.adapter.start()
|
||||
logging.debug(f"Connecting to {self.address}")
|
||||
self.device = self.adapter.connect(address)
|
||||
else:
|
||||
self.adapter = None
|
||||
self.device = None
|
||||
logging.debug("Connected to device")
|
||||
# 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):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
@@ -130,15 +144,18 @@ noProto – 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">
|
||||
|
||||
380
docs/meshtastic/globals.html
Normal file
380
docs/meshtastic/globals.html
Normal 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">"""Globals singleton class.
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
class Globals:
|
||||
"""Globals class is a Singleton."""
|
||||
__instance = None
|
||||
|
||||
@staticmethod
|
||||
def getInstance():
|
||||
"""Get an instance of the Globals class."""
|
||||
if Globals.__instance is None:
|
||||
Globals()
|
||||
return Globals.__instance
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor for the Globals CLass"""
|
||||
if Globals.__instance is not None:
|
||||
raise Exception("This class is a singleton")
|
||||
else:
|
||||
Globals.__instance = self
|
||||
self.args = None
|
||||
self.parser = None
|
||||
self.target_node = None
|
||||
self.channel_index = None
|
||||
|
||||
def reset(self):
|
||||
"""Reset all of our globals. If you add a member, add it to this method, too."""
|
||||
self.args = None
|
||||
self.parser = None
|
||||
self.target_node = None
|
||||
self.channel_index = None
|
||||
|
||||
def set_args(self, args):
|
||||
"""Set the args"""
|
||||
self.args = args
|
||||
|
||||
def set_parser(self, parser):
|
||||
"""Set the parser"""
|
||||
self.parser = parser
|
||||
|
||||
def set_target_node(self, target_node):
|
||||
"""Set the target_node"""
|
||||
self.target_node = target_node
|
||||
|
||||
def set_channel_index(self, channel_index):
|
||||
"""Set the channel_index"""
|
||||
self.channel_index = channel_index
|
||||
|
||||
def get_args(self):
|
||||
"""Get args"""
|
||||
return self.args
|
||||
|
||||
def get_parser(self):
|
||||
"""Get parser"""
|
||||
return self.parser
|
||||
|
||||
def get_target_node(self):
|
||||
"""Get target_node"""
|
||||
return self.target_node
|
||||
|
||||
def get_channel_index(self):
|
||||
"""Get channel_index"""
|
||||
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:
|
||||
"""Globals class is a Singleton."""
|
||||
__instance = None
|
||||
|
||||
@staticmethod
|
||||
def getInstance():
|
||||
"""Get an instance of the Globals class."""
|
||||
if Globals.__instance is None:
|
||||
Globals()
|
||||
return Globals.__instance
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor for the Globals CLass"""
|
||||
if Globals.__instance is not None:
|
||||
raise Exception("This class is a singleton")
|
||||
else:
|
||||
Globals.__instance = self
|
||||
self.args = None
|
||||
self.parser = None
|
||||
self.target_node = None
|
||||
self.channel_index = None
|
||||
|
||||
def reset(self):
|
||||
"""Reset all of our globals. If you add a member, add it to this method, too."""
|
||||
self.args = None
|
||||
self.parser = None
|
||||
self.target_node = None
|
||||
self.channel_index = None
|
||||
|
||||
def set_args(self, args):
|
||||
"""Set the args"""
|
||||
self.args = args
|
||||
|
||||
def set_parser(self, parser):
|
||||
"""Set the parser"""
|
||||
self.parser = parser
|
||||
|
||||
def set_target_node(self, target_node):
|
||||
"""Set the target_node"""
|
||||
self.target_node = target_node
|
||||
|
||||
def set_channel_index(self, channel_index):
|
||||
"""Set the channel_index"""
|
||||
self.channel_index = channel_index
|
||||
|
||||
def get_args(self):
|
||||
"""Get args"""
|
||||
return self.args
|
||||
|
||||
def get_parser(self):
|
||||
"""Get parser"""
|
||||
return self.parser
|
||||
|
||||
def get_target_node(self):
|
||||
"""Get target_node"""
|
||||
return self.target_node
|
||||
|
||||
def get_channel_index(self):
|
||||
"""Get channel_index"""
|
||||
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():
|
||||
"""Get an instance of the Globals class."""
|
||||
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):
|
||||
"""Get args"""
|
||||
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):
|
||||
"""Get channel_index"""
|
||||
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):
|
||||
"""Get parser"""
|
||||
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):
|
||||
"""Get target_node"""
|
||||
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):
|
||||
"""Reset all of our globals. If you add a member, add it to this method, too."""
|
||||
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):
|
||||
"""Set the args"""
|
||||
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):
|
||||
"""Set the channel_index"""
|
||||
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):
|
||||
"""Set the parser"""
|
||||
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):
|
||||
"""Set the target_node"""
|
||||
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>
|
||||
@@ -178,9 +178,10 @@ BROADCAST_NUM = 0xffffffff
|
||||
BROADCAST_ADDR = "^all"
|
||||
|
||||
|
||||
"""The numeric buildnumber (shared with android apps) specifying the level of device code we are guaranteed to understand
|
||||
"""The numeric buildnumber (shared with android apps) specifying the
|
||||
level of device code we are guaranteed to understand
|
||||
|
||||
format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
"""
|
||||
OUR_APP_VERSION = 20200
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -68,7 +68,8 @@ class MeshInterface:
|
||||
"""Constructor
|
||||
|
||||
Keyword Arguments:
|
||||
noProto -- If True, don't try to run our protocol on the link - just be a dumb serial client.
|
||||
noProto -- If True, don't try to run our protocol on the
|
||||
link - just be a dumb serial client.
|
||||
"""
|
||||
self.debugOut = debugOut
|
||||
self.nodes = None # FIXME
|
||||
@@ -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):
|
||||
"""Shutdown this interface"""
|
||||
@@ -119,54 +122,55 @@ class MeshInterface:
|
||||
def showNodes(self, includeSelf=True, file=sys.stdout):
|
||||
"""Show table summary of nodes in mesh"""
|
||||
def formatFloat(value, precision=2, unit=''):
|
||||
"""Format a float value with precsion."""
|
||||
return f'{value:.{precision}f}{unit}' if value else None
|
||||
|
||||
def getLH(ts):
|
||||
"""Format last heard"""
|
||||
return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
|
||||
|
||||
def getTimeAgo(ts):
|
||||
"""Format how long ago have we heard from this node (aka timeago)."""
|
||||
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
|
||||
|
||||
rows = []
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
if self.nodes:
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
|
||||
row = {"N": 0}
|
||||
row = {"N": 0}
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH(node.get("lastHeard")),
|
||||
"Since": getTimeAgo(node.get("lastHeard")),
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
rows.append(row)
|
||||
|
||||
row.update({
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH(node.get("lastHeard")),
|
||||
"Since": getTimeAgo(node.get("lastHeard")),
|
||||
})
|
||||
|
||||
rows.append(row)
|
||||
|
||||
# Why doesn't this way work?
|
||||
#rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
|
||||
rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
|
||||
for i, row in enumerate(rows):
|
||||
row['N'] = i+1
|
||||
|
||||
table = tabulate(rows, headers='keys', missingval='N/A',
|
||||
tablefmt='fancy_grid')
|
||||
table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid')
|
||||
print(table)
|
||||
return table
|
||||
|
||||
@@ -189,18 +193,24 @@ class MeshInterface:
|
||||
hopLimit=defaultHopLimit,
|
||||
onResponse=None,
|
||||
channelIndex=0):
|
||||
"""Send a utf8 string to some other node, if the node has a display it will also be shown on the device.
|
||||
"""Send a utf8 string to some other node, if the node has a display it
|
||||
will also be shown on the device.
|
||||
|
||||
Arguments:
|
||||
text {string} -- The text to send
|
||||
|
||||
Keyword Arguments:
|
||||
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to send an application layer response
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner
|
||||
(with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to
|
||||
send an application layer response
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
"""
|
||||
return self.sendData(text.encode("utf-8"), destinationId,
|
||||
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
|
||||
@@ -219,15 +229,23 @@ class MeshInterface:
|
||||
"""Send a data packet to some other node
|
||||
|
||||
Keyword Arguments:
|
||||
data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes)
|
||||
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to send an application layer response
|
||||
onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives
|
||||
(or the transaction is NAKed due to non receipt)
|
||||
data -- the data to send, either as an array of bytes or
|
||||
as a protobuf (which will be automatically
|
||||
serialized to bytes)
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable
|
||||
manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other
|
||||
side to send an application layer response
|
||||
onResponse -- A closure of the form funct(packet), that will be
|
||||
called when a response packet arrives (or the transaction
|
||||
is NAKed due to non receipt)
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
"""
|
||||
if getattr(data, "SerializeToString", None):
|
||||
logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
|
||||
@@ -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):
|
||||
"""
|
||||
Send a position packet to some other node (normally a broadcast)
|
||||
|
||||
Also, the device software will notice this packet and use it to automatically set its notion of
|
||||
the local position.
|
||||
Also, the device software will notice this packet and use it to automatically
|
||||
set its notion of the local position.
|
||||
|
||||
If timeSec is not specified (recommended), we will use the local machine time.
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet and
|
||||
can be used to track future message acks/naks.
|
||||
"""
|
||||
p = mesh_pb2.Position()
|
||||
if latitude != 0.0:
|
||||
@@ -321,8 +341,8 @@ class MeshInterface:
|
||||
meshPacket.want_ack = wantAck
|
||||
meshPacket.hop_limit = hopLimit
|
||||
|
||||
# if the user hasn't set an ID for this packet (likely and recommended), we should pick a new unique ID
|
||||
# so the message can be tracked.
|
||||
# if the user hasn't set an ID for this packet (likely and recommended),
|
||||
# we should pick a new unique ID so the message can be tracked.
|
||||
if meshPacket.id == 0:
|
||||
meshPacket.id = self._generatePacketId()
|
||||
|
||||
@@ -333,8 +353,7 @@ class MeshInterface:
|
||||
|
||||
def waitForConfig(self):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')
|
||||
) and self.localNode.waitForConfig()
|
||||
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
|
||||
if not success:
|
||||
raise Exception("Timed out waiting for interface config")
|
||||
|
||||
@@ -436,8 +455,7 @@ class MeshInterface:
|
||||
def _sendToRadio(self, toRadio):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
if self.noProto:
|
||||
logging.warning(
|
||||
f"Not sending packet because protocol use is disabled by noProto")
|
||||
logging.warning(f"Not sending packet because protocol use is disabled by noProto")
|
||||
else:
|
||||
#logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
|
||||
self._sendToRadioImpl(toRadio)
|
||||
@@ -448,7 +466,8 @@ class MeshInterface:
|
||||
|
||||
def _handleConfigComplete(self):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings and channels
|
||||
Done with initial config messages, now send regular MeshPackets
|
||||
to ask for settings and channels
|
||||
"""
|
||||
self.localNode.requestConfig()
|
||||
|
||||
@@ -469,7 +488,7 @@ class MeshInterface:
|
||||
failmsg = None
|
||||
# Check for app too old
|
||||
if self.myInfo.min_app_version > OUR_APP_VERSION:
|
||||
failmsg = "This device needs a newer python client, please \"pip install --upgrade meshtastic\". "\
|
||||
failmsg = "This device needs a newer python client, run 'pip install --upgrade meshtastic'."\
|
||||
"For more information see https://tinyurl.com/5bjsxu32"
|
||||
|
||||
# check for firmware too old
|
||||
@@ -497,13 +516,15 @@ class MeshInterface:
|
||||
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated",
|
||||
node=node, interface=self))
|
||||
elif fromRadio.config_complete_id == self.configId:
|
||||
# we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id
|
||||
# we ignore the config_complete_id, it is unneeded for our
|
||||
# stream API fromRadio.config_complete_id
|
||||
logging.debug(f"Config complete ID {self.configId}")
|
||||
self._handleConfigComplete()
|
||||
elif fromRadio.HasField("packet"):
|
||||
self._handlePacketFromRadio(fromRadio.packet)
|
||||
elif fromRadio.rebooted:
|
||||
# Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port
|
||||
# Tell clients the device went away. Careful not to call the overridden
|
||||
# subclass version that closes the serial port
|
||||
MeshInterface._disconnected(self)
|
||||
|
||||
self._startConfig() # redownload the node db etc...
|
||||
@@ -569,12 +590,12 @@ class MeshInterface:
|
||||
asDict["raw"] = meshPacket
|
||||
|
||||
# from might be missing if the nodenum was zero.
|
||||
if not "from" in asDict:
|
||||
if "from" not in asDict:
|
||||
asDict["from"] = 0
|
||||
logging.error(
|
||||
f"Device returned a packet we sent, ignoring: {stripnl(asDict)}")
|
||||
return
|
||||
if not "to" in asDict:
|
||||
if "to" not in asDict:
|
||||
asDict["to"] = 0
|
||||
|
||||
# /add fromId and toId fields based on the node ID
|
||||
@@ -597,8 +618,9 @@ class MeshInterface:
|
||||
# byte array.
|
||||
decoded["payload"] = meshPacket.decoded.payload
|
||||
|
||||
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not set it will not be populated at all
|
||||
# to make API usage easier, set it to prevent confusion
|
||||
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not
|
||||
# set it will not be populated at all to make API usage easier, set
|
||||
# it to prevent confusion
|
||||
if not "portnum" in decoded:
|
||||
decoded["portnum"] = portnums_pb2.PortNum.Name(
|
||||
portnums_pb2.PortNum.UNKNOWN_APP)
|
||||
@@ -607,8 +629,9 @@ class MeshInterface:
|
||||
|
||||
topic = f"meshtastic.receive.data.{portnum}"
|
||||
|
||||
# decode position protobufs and update nodedb, provide decoded version as "position" in the published msg
|
||||
# move the following into a 'decoders' API that clients could register?
|
||||
# decode position protobufs and update nodedb, provide decoded version
|
||||
# as "position" in the published msg move the following into a 'decoders'
|
||||
# API that clients could register?
|
||||
portNumInt = meshPacket.decoded.portnum # we want portnum as an int
|
||||
handler = protocols.get(portNumInt)
|
||||
# The decoded protobuf as a dictionary (if we understand this message)
|
||||
@@ -667,7 +690,8 @@ nodes
|
||||
debugOut</p>
|
||||
<p>Constructor</p>
|
||||
<p>Keyword Arguments:
|
||||
noProto – If True, don't try to run our protocol on the link - just be a dumb serial client.</p></div>
|
||||
noProto – 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 – If True, don't try to run our protocol on the link - just be a d
|
||||
"""Constructor
|
||||
|
||||
Keyword Arguments:
|
||||
noProto -- If True, don't try to run our protocol on the link - just be a dumb serial client.
|
||||
noProto -- If True, don't try to run our protocol on the
|
||||
link - just be a dumb serial client.
|
||||
"""
|
||||
self.debugOut = debugOut
|
||||
self.nodes = None # FIXME
|
||||
@@ -700,6 +725,8 @@ noProto – 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):
|
||||
"""Shutdown this interface"""
|
||||
@@ -737,54 +764,55 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
def showNodes(self, includeSelf=True, file=sys.stdout):
|
||||
"""Show table summary of nodes in mesh"""
|
||||
def formatFloat(value, precision=2, unit=''):
|
||||
"""Format a float value with precsion."""
|
||||
return f'{value:.{precision}f}{unit}' if value else None
|
||||
|
||||
def getLH(ts):
|
||||
"""Format last heard"""
|
||||
return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
|
||||
|
||||
def getTimeAgo(ts):
|
||||
"""Format how long ago have we heard from this node (aka timeago)."""
|
||||
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
|
||||
|
||||
rows = []
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
if self.nodes:
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
|
||||
row = {"N": 0}
|
||||
row = {"N": 0}
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH(node.get("lastHeard")),
|
||||
"Since": getTimeAgo(node.get("lastHeard")),
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
rows.append(row)
|
||||
|
||||
row.update({
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH(node.get("lastHeard")),
|
||||
"Since": getTimeAgo(node.get("lastHeard")),
|
||||
})
|
||||
|
||||
rows.append(row)
|
||||
|
||||
# Why doesn't this way work?
|
||||
#rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
|
||||
rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
|
||||
for i, row in enumerate(rows):
|
||||
row['N'] = i+1
|
||||
|
||||
table = tabulate(rows, headers='keys', missingval='N/A',
|
||||
tablefmt='fancy_grid')
|
||||
table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid')
|
||||
print(table)
|
||||
return table
|
||||
|
||||
@@ -807,18 +835,24 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
hopLimit=defaultHopLimit,
|
||||
onResponse=None,
|
||||
channelIndex=0):
|
||||
"""Send a utf8 string to some other node, if the node has a display it will also be shown on the device.
|
||||
"""Send a utf8 string to some other node, if the node has a display it
|
||||
will also be shown on the device.
|
||||
|
||||
Arguments:
|
||||
text {string} -- The text to send
|
||||
|
||||
Keyword Arguments:
|
||||
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to send an application layer response
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner
|
||||
(with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to
|
||||
send an application layer response
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
"""
|
||||
return self.sendData(text.encode("utf-8"), destinationId,
|
||||
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
|
||||
@@ -837,15 +871,23 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
"""Send a data packet to some other node
|
||||
|
||||
Keyword Arguments:
|
||||
data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes)
|
||||
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to send an application layer response
|
||||
onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives
|
||||
(or the transaction is NAKed due to non receipt)
|
||||
data -- the data to send, either as an array of bytes or
|
||||
as a protobuf (which will be automatically
|
||||
serialized to bytes)
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable
|
||||
manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other
|
||||
side to send an application layer response
|
||||
onResponse -- A closure of the form funct(packet), that will be
|
||||
called when a response packet arrives (or the transaction
|
||||
is NAKed due to non receipt)
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
"""
|
||||
if getattr(data, "SerializeToString", None):
|
||||
logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
|
||||
@@ -869,16 +911,18 @@ noProto – 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):
|
||||
"""
|
||||
Send a position packet to some other node (normally a broadcast)
|
||||
|
||||
Also, the device software will notice this packet and use it to automatically set its notion of
|
||||
the local position.
|
||||
Also, the device software will notice this packet and use it to automatically
|
||||
set its notion of the local position.
|
||||
|
||||
If timeSec is not specified (recommended), we will use the local machine time.
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet and
|
||||
can be used to track future message acks/naks.
|
||||
"""
|
||||
p = mesh_pb2.Position()
|
||||
if latitude != 0.0:
|
||||
@@ -939,8 +983,8 @@ noProto – 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't set an ID for this packet (likely and recommended), we should pick a new unique ID
|
||||
# so the message can be tracked.
|
||||
# if the user hasn't set an ID for this packet (likely and recommended),
|
||||
# we should pick a new unique ID so the message can be tracked.
|
||||
if meshPacket.id == 0:
|
||||
meshPacket.id = self._generatePacketId()
|
||||
|
||||
@@ -951,8 +995,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
|
||||
def waitForConfig(self):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')
|
||||
) and self.localNode.waitForConfig()
|
||||
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
|
||||
if not success:
|
||||
raise Exception("Timed out waiting for interface config")
|
||||
|
||||
@@ -1054,8 +1097,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
def _sendToRadio(self, toRadio):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
if self.noProto:
|
||||
logging.warning(
|
||||
f"Not sending packet because protocol use is disabled by noProto")
|
||||
logging.warning(f"Not sending packet because protocol use is disabled by noProto")
|
||||
else:
|
||||
#logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
|
||||
self._sendToRadioImpl(toRadio)
|
||||
@@ -1066,7 +1108,8 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
|
||||
def _handleConfigComplete(self):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings and channels
|
||||
Done with initial config messages, now send regular MeshPackets
|
||||
to ask for settings and channels
|
||||
"""
|
||||
self.localNode.requestConfig()
|
||||
|
||||
@@ -1087,7 +1130,7 @@ noProto – 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 > OUR_APP_VERSION:
|
||||
failmsg = "This device needs a newer python client, please \"pip install --upgrade meshtastic\". "\
|
||||
failmsg = "This device needs a newer python client, run 'pip install --upgrade meshtastic'."\
|
||||
"For more information see https://tinyurl.com/5bjsxu32"
|
||||
|
||||
# check for firmware too old
|
||||
@@ -1115,13 +1158,15 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.node.updated",
|
||||
node=node, interface=self))
|
||||
elif fromRadio.config_complete_id == self.configId:
|
||||
# we ignore the config_complete_id, it is unneeded for our stream API fromRadio.config_complete_id
|
||||
# we ignore the config_complete_id, it is unneeded for our
|
||||
# stream API fromRadio.config_complete_id
|
||||
logging.debug(f"Config complete ID {self.configId}")
|
||||
self._handleConfigComplete()
|
||||
elif fromRadio.HasField("packet"):
|
||||
self._handlePacketFromRadio(fromRadio.packet)
|
||||
elif fromRadio.rebooted:
|
||||
# Tell clients the device went away. Careful not to call the overridden subclass version that closes the serial port
|
||||
# Tell clients the device went away. Careful not to call the overridden
|
||||
# subclass version that closes the serial port
|
||||
MeshInterface._disconnected(self)
|
||||
|
||||
self._startConfig() # redownload the node db etc...
|
||||
@@ -1187,12 +1232,12 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
asDict["raw"] = meshPacket
|
||||
|
||||
# from might be missing if the nodenum was zero.
|
||||
if not "from" in asDict:
|
||||
if "from" not in asDict:
|
||||
asDict["from"] = 0
|
||||
logging.error(
|
||||
f"Device returned a packet we sent, ignoring: {stripnl(asDict)}")
|
||||
return
|
||||
if not "to" in asDict:
|
||||
if "to" not in asDict:
|
||||
asDict["to"] = 0
|
||||
|
||||
# /add fromId and toId fields based on the node ID
|
||||
@@ -1215,8 +1260,9 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
# byte array.
|
||||
decoded["payload"] = meshPacket.decoded.payload
|
||||
|
||||
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not set it will not be populated at all
|
||||
# to make API usage easier, set it to prevent confusion
|
||||
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not
|
||||
# set it will not be populated at all to make API usage easier, set
|
||||
# it to prevent confusion
|
||||
if not "portnum" in decoded:
|
||||
decoded["portnum"] = portnums_pb2.PortNum.Name(
|
||||
portnums_pb2.PortNum.UNKNOWN_APP)
|
||||
@@ -1225,8 +1271,9 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
|
||||
topic = f"meshtastic.receive.data.{portnum}"
|
||||
|
||||
# decode position protobufs and update nodedb, provide decoded version as "position" in the published msg
|
||||
# move the following into a 'decoders' API that clients could register?
|
||||
# decode position protobufs and update nodedb, provide decoded version
|
||||
# as "position" in the published msg move the following into a 'decoders'
|
||||
# API that clients could register?
|
||||
portNumInt = meshPacket.decoded.portnum # we want portnum as an int
|
||||
handler = protocols.get(portNumInt)
|
||||
# The decoded protobuf as a dictionary (if we understand this message)
|
||||
@@ -1381,14 +1428,22 @@ noProto – 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 – 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)</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 – 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)</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 – A closure of the form funct(packet), that will be called when
|
||||
"""Send a data packet to some other node
|
||||
|
||||
Keyword Arguments:
|
||||
data -- the data to send, either as an array of bytes or as a protobuf (which will be automatically serialized to bytes)
|
||||
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to send an application layer response
|
||||
onResponse -- A closure of the form funct(packet), that will be called when a response packet arrives
|
||||
(or the transaction is NAKed due to non receipt)
|
||||
data -- the data to send, either as an array of bytes or
|
||||
as a protobuf (which will be automatically
|
||||
serialized to bytes)
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable
|
||||
manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other
|
||||
side to send an application layer response
|
||||
onResponse -- A closure of the form funct(packet), that will be
|
||||
called when a response packet arrives (or the transaction
|
||||
is NAKed due to non receipt)
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
"""
|
||||
if getattr(data, "SerializeToString", None):
|
||||
logging.debug(f"Serializing protobuf as data: {stripnl(data)}")
|
||||
@@ -1440,24 +1503,27 @@ onResponse – 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):
|
||||
"""
|
||||
Send a position packet to some other node (normally a broadcast)
|
||||
|
||||
Also, the device software will notice this packet and use it to automatically set its notion of
|
||||
the local position.
|
||||
Also, the device software will notice this packet and use it to automatically
|
||||
set its notion of the local position.
|
||||
|
||||
If timeSec is not specified (recommended), we will use the local machine time.
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet and
|
||||
can be used to track future message acks/naks.
|
||||
"""
|
||||
p = mesh_pb2.Position()
|
||||
if latitude != 0.0:
|
||||
@@ -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} – The text to send</p>
|
||||
<p>Keyword Arguments:
|
||||
destinationId {nodeId or nodeNum} – where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum – the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck – True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse – 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} – 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</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 – True if you want the service on the other side to send an a
|
||||
hopLimit=defaultHopLimit,
|
||||
onResponse=None,
|
||||
channelIndex=0):
|
||||
"""Send a utf8 string to some other node, if the node has a display it will also be shown on the device.
|
||||
"""Send a utf8 string to some other node, if the node has a display it
|
||||
will also be shown on the device.
|
||||
|
||||
Arguments:
|
||||
text {string} -- The text to send
|
||||
|
||||
Keyword Arguments:
|
||||
destinationId {nodeId or nodeNum} -- where to send this message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers) of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner (with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to send an application layer response
|
||||
destinationId {nodeId or nodeNum} -- where to send this
|
||||
message (default: {BROADCAST_ADDR})
|
||||
portNum -- the application portnum (similar to IP port numbers)
|
||||
of the destination, see portnums.proto for a list
|
||||
wantAck -- True if you want the message sent in a reliable manner
|
||||
(with retries and ack/nak provided for delivery)
|
||||
wantResponse -- True if you want the service on the other side to
|
||||
send an application layer response
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks.
|
||||
Returns the sent packet. The id field will be populated in this packet
|
||||
and can be used to track future message acks/naks.
|
||||
"""
|
||||
return self.sendData(text.encode("utf-8"), destinationId,
|
||||
portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
|
||||
@@ -1562,54 +1640,55 @@ wantResponse – 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):
|
||||
"""Show table summary of nodes in mesh"""
|
||||
def formatFloat(value, precision=2, unit=''):
|
||||
"""Format a float value with precsion."""
|
||||
return f'{value:.{precision}f}{unit}' if value else None
|
||||
|
||||
def getLH(ts):
|
||||
"""Format last heard"""
|
||||
return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
|
||||
|
||||
def getTimeAgo(ts):
|
||||
"""Format how long ago have we heard from this node (aka timeago)."""
|
||||
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
|
||||
|
||||
rows = []
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
if self.nodes:
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
|
||||
row = {"N": 0}
|
||||
row = {"N": 0}
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH(node.get("lastHeard")),
|
||||
"Since": getTimeAgo(node.get("lastHeard")),
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
rows.append(row)
|
||||
|
||||
row.update({
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH(node.get("lastHeard")),
|
||||
"Since": getTimeAgo(node.get("lastHeard")),
|
||||
})
|
||||
|
||||
rows.append(row)
|
||||
|
||||
# Why doesn't this way work?
|
||||
#rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
|
||||
rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
|
||||
for i, row in enumerate(rows):
|
||||
row['N'] = i+1
|
||||
|
||||
table = tabulate(rows, headers='keys', missingval='N/A',
|
||||
tablefmt='fancy_grid')
|
||||
table = tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid')
|
||||
print(table)
|
||||
return table</code></pre>
|
||||
</details>
|
||||
@@ -1625,8 +1704,7 @@ wantResponse – True if you want the service on the other side to send an a
|
||||
</summary>
|
||||
<pre><code class="python">def waitForConfig(self):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')
|
||||
) and self.localNode.waitForConfig()
|
||||
success = self._timeout.waitForSet(self, attrs=('myInfo', 'nodes')) and self.localNode.waitForConfig()
|
||||
if not success:
|
||||
raise Exception("Timed out waiting for interface config")</code></pre>
|
||||
</details>
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">""" Node class
|
||||
<pre><code class="python">"""Node class
|
||||
"""
|
||||
|
||||
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):
|
||||
"""Show human readable description of our channels"""
|
||||
"""Show human readable description of our channels."""
|
||||
print("Channels:")
|
||||
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" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
|
||||
publicURL = self.getURL(includeAll=False)
|
||||
adminURL = self.getURL(includeAll=True)
|
||||
@@ -74,15 +76,19 @@ class Node:
|
||||
self.showChannels()
|
||||
|
||||
def requestConfig(self):
|
||||
"""
|
||||
Send regular MeshPackets to ask for settings and channels
|
||||
"""
|
||||
"""Send regular MeshPackets to ask for settings and channels."""
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||
|
||||
self._requestSettings()
|
||||
|
||||
def turnOffEncryptionOnPrimaryChannel(self):
|
||||
"""Turn off encryption on primary channel."""
|
||||
self.channels[0].settings.psk = fromPSK("none")
|
||||
print("Writing modified channels to device")
|
||||
self.writeChannel(0)
|
||||
|
||||
def waitForConfig(self):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
return self._timeout.waitForSet(self, attrs=('radioConfig', 'channels'))
|
||||
@@ -110,7 +116,7 @@ class Node:
|
||||
def deleteChannel(self, channelIndex):
|
||||
"""Delete the specifed channelIndex and shift other channels up"""
|
||||
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("Warning: Only SECONDARY channels can be deleted")
|
||||
|
||||
# we are careful here because if we move the "admin" 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 >= adminIndex:
|
||||
# We've now passed the old location for admin index (and writen it), so we can start finding it by name again
|
||||
# We've now passed the old location for admin index
|
||||
# (and writen it), so we can start finding it by name again
|
||||
adminIndex = 0
|
||||
|
||||
def getChannelByName(self, name):
|
||||
@@ -186,16 +194,15 @@ class Node:
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def getURL(self, includeAll: bool = True):
|
||||
"""The sharable URL that describes the current channel
|
||||
"""
|
||||
"""The sharable URL that describes the current channel"""
|
||||
# Only keep the primary/secondary channels, assume primary is first
|
||||
channelSet = apponly_pb2.ChannelSet()
|
||||
if self.channels:
|
||||
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('ascii')
|
||||
some_bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
|
||||
return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
|
||||
|
||||
def setURL(self, url):
|
||||
@@ -233,41 +240,40 @@ class Node:
|
||||
i = i + 1
|
||||
|
||||
def _requestSettings(self):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings
|
||||
"""
|
||||
"""Done with initial config messages, now send regular
|
||||
MeshPackets to ask for settings."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_radio_request = True
|
||||
|
||||
def onResponse(p):
|
||||
"""A closure to handle the response packet"""
|
||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
||||
logging.debug("Received radio config, now fetching channels...")
|
||||
self._timeout.reset() # We made foreward progress
|
||||
self._requestChannel(0) # now start fetching channels
|
||||
errorFound = False
|
||||
if 'routing' in p["decoded"]:
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
errorFound = True
|
||||
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
|
||||
if errorFound is False:
|
||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
||||
logging.debug("Received radio config, now fetching channels...")
|
||||
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(
|
||||
"Requesting preferences from remote node (this could take a while)")
|
||||
print("Requesting preferences from remote node (this could take a while)")
|
||||
|
||||
return self._sendAdmin(p,
|
||||
wantResponse=True,
|
||||
onResponse=onResponse)
|
||||
return self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
||||
|
||||
def exitSimulator(self):
|
||||
"""
|
||||
Tell a simulator node to exit (this message is ignored for other nodes)
|
||||
"""
|
||||
"""Tell a simulator node to exit (this message
|
||||
is ignored for other nodes)"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.exit_simulator = True
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def reboot(self, secs: int = 10):
|
||||
"""
|
||||
Tell the node to reboot
|
||||
"""
|
||||
"""Tell the node to reboot."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.reboot_seconds = secs
|
||||
logging.info(f"Telling node to reboot in {secs} seconds")
|
||||
@@ -278,6 +284,7 @@ class Node:
|
||||
"""Fixup indexes and add disabled channels as needed"""
|
||||
|
||||
# Add extra disabled channels as needed
|
||||
# TODO: These 2 lines seem to not do anything.
|
||||
for index, ch in enumerate(self.channels):
|
||||
ch.index = index # fixup indexes
|
||||
|
||||
@@ -296,21 +303,19 @@ class Node:
|
||||
index += 1
|
||||
|
||||
def _requestChannel(self, channelNum: int):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings
|
||||
"""
|
||||
"""Done with initial config messages, now send regular
|
||||
MeshPackets to ask for settings"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_channel_request = channelNum + 1
|
||||
|
||||
# Show progress message for super slow operations
|
||||
if self != self.iface.localNode:
|
||||
logging.info(
|
||||
f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
||||
logging.info(f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
||||
else:
|
||||
logging.debug(f"Requesting channel {channelNum}")
|
||||
|
||||
def onResponse(p):
|
||||
"""A closure to handle the response packet"""
|
||||
"""A closure to handle the response packet for requesting a channel"""
|
||||
c = p["decoded"]["admin"]["raw"].get_channel_response
|
||||
self.partialChannels.append(c)
|
||||
self._timeout.reset() # We made foreward progress
|
||||
@@ -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 >= self.iface.myInfo.max_channels - 1:
|
||||
logging.debug("Finished downloading channels")
|
||||
@@ -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):
|
||||
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
|
||||
|
||||
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
|
||||
@@ -389,14 +391,16 @@ class Node:
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self._timeout = Timeout(maxSecs=60)
|
||||
self.partialChannels = None
|
||||
|
||||
def showChannels(self):
|
||||
"""Show human readable description of our channels"""
|
||||
"""Show human readable description of our channels."""
|
||||
print("Channels:")
|
||||
if self.channels:
|
||||
for c in self.channels:
|
||||
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" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
|
||||
publicURL = self.getURL(includeAll=False)
|
||||
adminURL = self.getURL(includeAll=True)
|
||||
@@ -413,15 +417,19 @@ class Node:
|
||||
self.showChannels()
|
||||
|
||||
def requestConfig(self):
|
||||
"""
|
||||
Send regular MeshPackets to ask for settings and channels
|
||||
"""
|
||||
"""Send regular MeshPackets to ask for settings and channels."""
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||
|
||||
self._requestSettings()
|
||||
|
||||
def turnOffEncryptionOnPrimaryChannel(self):
|
||||
"""Turn off encryption on primary channel."""
|
||||
self.channels[0].settings.psk = fromPSK("none")
|
||||
print("Writing modified channels to device")
|
||||
self.writeChannel(0)
|
||||
|
||||
def waitForConfig(self):
|
||||
"""Block until radio config is received. Returns True if config has been received."""
|
||||
return self._timeout.waitForSet(self, attrs=('radioConfig', 'channels'))
|
||||
@@ -449,7 +457,7 @@ class Node:
|
||||
def deleteChannel(self, channelIndex):
|
||||
"""Delete the specifed channelIndex and shift other channels up"""
|
||||
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("Warning: Only SECONDARY channels can be deleted")
|
||||
|
||||
# we are careful here because if we move the "admin" 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 >= adminIndex:
|
||||
# We've now passed the old location for admin index (and writen it), so we can start finding it by name again
|
||||
# We've now passed the old location for admin index
|
||||
# (and writen it), so we can start finding it by name again
|
||||
adminIndex = 0
|
||||
|
||||
def getChannelByName(self, name):
|
||||
@@ -525,16 +535,15 @@ class Node:
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def getURL(self, includeAll: bool = True):
|
||||
"""The sharable URL that describes the current channel
|
||||
"""
|
||||
"""The sharable URL that describes the current channel"""
|
||||
# Only keep the primary/secondary channels, assume primary is first
|
||||
channelSet = apponly_pb2.ChannelSet()
|
||||
if self.channels:
|
||||
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('ascii')
|
||||
some_bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
|
||||
return f"https://www.meshtastic.org/d/#{s}".replace("=", "")
|
||||
|
||||
def setURL(self, url):
|
||||
@@ -572,41 +581,40 @@ class Node:
|
||||
i = i + 1
|
||||
|
||||
def _requestSettings(self):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings
|
||||
"""
|
||||
"""Done with initial config messages, now send regular
|
||||
MeshPackets to ask for settings."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_radio_request = True
|
||||
|
||||
def onResponse(p):
|
||||
"""A closure to handle the response packet"""
|
||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
||||
logging.debug("Received radio config, now fetching channels...")
|
||||
self._timeout.reset() # We made foreward progress
|
||||
self._requestChannel(0) # now start fetching channels
|
||||
errorFound = False
|
||||
if 'routing' in p["decoded"]:
|
||||
if p["decoded"]["routing"]["errorReason"] != "NONE":
|
||||
errorFound = True
|
||||
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
|
||||
if errorFound is False:
|
||||
self.radioConfig = p["decoded"]["admin"]["raw"].get_radio_response
|
||||
logging.debug("Received radio config, now fetching channels...")
|
||||
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(
|
||||
"Requesting preferences from remote node (this could take a while)")
|
||||
print("Requesting preferences from remote node (this could take a while)")
|
||||
|
||||
return self._sendAdmin(p,
|
||||
wantResponse=True,
|
||||
onResponse=onResponse)
|
||||
return self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
|
||||
|
||||
def exitSimulator(self):
|
||||
"""
|
||||
Tell a simulator node to exit (this message is ignored for other nodes)
|
||||
"""
|
||||
"""Tell a simulator node to exit (this message
|
||||
is ignored for other nodes)"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.exit_simulator = True
|
||||
|
||||
return self._sendAdmin(p)
|
||||
|
||||
def reboot(self, secs: int = 10):
|
||||
"""
|
||||
Tell the node to reboot
|
||||
"""
|
||||
"""Tell the node to reboot."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.reboot_seconds = secs
|
||||
logging.info(f"Telling node to reboot in {secs} seconds")
|
||||
@@ -617,6 +625,7 @@ class Node:
|
||||
"""Fixup indexes and add disabled channels as needed"""
|
||||
|
||||
# Add extra disabled channels as needed
|
||||
# TODO: These 2 lines seem to not do anything.
|
||||
for index, ch in enumerate(self.channels):
|
||||
ch.index = index # fixup indexes
|
||||
|
||||
@@ -635,21 +644,19 @@ class Node:
|
||||
index += 1
|
||||
|
||||
def _requestChannel(self, channelNum: int):
|
||||
"""
|
||||
Done with initial config messages, now send regular MeshPackets to ask for settings
|
||||
"""
|
||||
"""Done with initial config messages, now send regular
|
||||
MeshPackets to ask for settings"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.get_channel_request = channelNum + 1
|
||||
|
||||
# Show progress message for super slow operations
|
||||
if self != self.iface.localNode:
|
||||
logging.info(
|
||||
f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
||||
logging.info(f"Requesting channel {channelNum} info from remote node (this could take a while)")
|
||||
else:
|
||||
logging.debug(f"Requesting channel {channelNum}")
|
||||
|
||||
def onResponse(p):
|
||||
"""A closure to handle the response packet"""
|
||||
"""A closure to handle the response packet for requesting a channel"""
|
||||
c = p["decoded"]["admin"]["raw"].get_channel_response
|
||||
self.partialChannels.append(c)
|
||||
self._timeout.reset() # We made foreward progress
|
||||
@@ -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 >= self.iface.myInfo.max_channels - 1:
|
||||
logging.debug("Finished downloading channels")
|
||||
@@ -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):
|
||||
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
|
||||
|
||||
if adminIndex == 0: # unless a special channel index was used, we want to use the admin index
|
||||
@@ -707,7 +711,7 @@ class Node:
|
||||
<pre><code class="python">def deleteChannel(self, channelIndex):
|
||||
"""Delete the specifed channelIndex and shift other channels up"""
|
||||
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("Warning: Only SECONDARY channels can be deleted")
|
||||
|
||||
# we are careful here because if we move the "admin" 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 >= adminIndex:
|
||||
# We've now passed the old location for admin index (and writen it), so we can start finding it by name again
|
||||
# We've now passed the old location for admin index
|
||||
# (and writen it), so we can start finding it by name again
|
||||
adminIndex = 0</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):
|
||||
"""
|
||||
Tell a simulator node to exit (this message is ignored for other nodes)
|
||||
"""
|
||||
"""Tell a simulator node to exit (this message
|
||||
is ignored for other nodes)"""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.exit_simulator = True
|
||||
|
||||
@@ -791,16 +797,15 @@ class Node:
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def getURL(self, includeAll: bool = True):
|
||||
"""The sharable URL that describes the current channel
|
||||
"""
|
||||
"""The sharable URL that describes the current channel"""
|
||||
# Only keep the primary/secondary channels, assume primary is first
|
||||
channelSet = apponly_pb2.ChannelSet()
|
||||
if self.channels:
|
||||
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('ascii')
|
||||
some_bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode('ascii')
|
||||
return f"https://www.meshtastic.org/d/#{s}".replace("=", "")</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):
|
||||
"""
|
||||
Tell the node to reboot
|
||||
"""
|
||||
"""Tell the node to reboot."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
p.reboot_seconds = secs
|
||||
logging.info(f"Telling node to reboot in {secs} seconds")
|
||||
@@ -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):
|
||||
"""
|
||||
Send regular MeshPackets to ask for settings and channels
|
||||
"""
|
||||
"""Send regular MeshPackets to ask for settings and channels."""
|
||||
self.radioConfig = None
|
||||
self.channels = None
|
||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||
@@ -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):
|
||||
"""Show human readable description of our channels"""
|
||||
"""Show human readable description of our channels."""
|
||||
print("Channels:")
|
||||
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" {channel_pb2.Channel.Role.Name(c.role)} psk={pskToString(c.settings.psk)} {cStr}")
|
||||
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):
|
||||
"""Turn off encryption on primary channel."""
|
||||
self.channels[0].settings.psk = fromPSK("none")
|
||||
print("Writing modified channels to device")
|
||||
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>
|
||||
|
||||
@@ -67,9 +67,8 @@ class RemoteHardwareClient:
|
||||
|
||||
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
|
||||
if not nodeid:
|
||||
# pylint: disable=W1401
|
||||
raise Exception(
|
||||
"You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)")
|
||||
r"You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)")
|
||||
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(
|
||||
"You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)")
|
||||
r"You must set a destination node ID for this operation (use --dest \!xxxxxxxxx)")
|
||||
return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP,
|
||||
wantAck=True, channelIndex=self.channelIndex, wantResponse=wantResponse, onResponse=onResponse)
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""Interface class for meshtastic devices over a serial link"""
|
||||
@@ -51,14 +51,14 @@ class SerialInterface(StreamInterface):
|
||||
"""
|
||||
|
||||
if devPath is None:
|
||||
ports = findPorts()
|
||||
ports = meshtastic.util.findPorts()
|
||||
logging.debug(f"ports:{ports}")
|
||||
if len(ports) == 0:
|
||||
our_exit("Warning: No Meshtastic devices detected.")
|
||||
meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
|
||||
elif len(ports) > 1:
|
||||
message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
|
||||
message += f" Ports detected:{ports}"
|
||||
our_exit(message)
|
||||
meshtastic.util.our_exit(message)
|
||||
else:
|
||||
devPath = ports[0]
|
||||
|
||||
@@ -143,14 +143,14 @@ debugOut {stream} – If a stream is provided, any debug serial output from
|
||||
"""
|
||||
|
||||
if devPath is None:
|
||||
ports = findPorts()
|
||||
ports = meshtastic.util.findPorts()
|
||||
logging.debug(f"ports:{ports}")
|
||||
if len(ports) == 0:
|
||||
our_exit("Warning: No Meshtastic devices detected.")
|
||||
meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
|
||||
elif len(ports) > 1:
|
||||
message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
|
||||
message += f" Ports detected:{ports}"
|
||||
our_exit(message)
|
||||
meshtastic.util.our_exit(message)
|
||||
else:
|
||||
devPath = ports[0]
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">""" Stream Interface base class
|
||||
<pre><code class="python">"""Stream Interface base class
|
||||
"""
|
||||
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):
|
||||
"""Connect to our radio
|
||||
|
||||
Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
|
||||
start the reading thread later.
|
||||
Normally this is called automatically by the constructor, but if you
|
||||
passed in connectNow=False you can manually start the reading thread later.
|
||||
"""
|
||||
|
||||
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
|
||||
# we write enought start bytes to force it to resync (we don't use START1 because we want to ensure it is looking for START1)
|
||||
# Send some bogus UART characters to force a sleeping device to wake, and
|
||||
# if the reading statemachine was parsing a bad packet make sure
|
||||
# we write enought start bytes to force it to resync (we don't use START1
|
||||
# because we want to ensure it is looking for START1)
|
||||
p = bytearray([START2] * 32)
|
||||
self._writeBytes(p)
|
||||
time.sleep(0.1) # wait 100ms to give device time to start running
|
||||
@@ -104,8 +107,11 @@ class StreamInterface(MeshInterface):
|
||||
MeshInterface._disconnected(self)
|
||||
|
||||
logging.debug("Closing our port")
|
||||
# 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):
|
||||
"""Close a connection to the device"""
|
||||
logging.debug("Closing stream")
|
||||
MeshInterface.close(self)
|
||||
# pyserial cancel_read doesn't seem to work, therefore we ask the reader thread to close things for us
|
||||
# pyserial cancel_read doesn't seem to work, therefore we ask the
|
||||
# reader thread to close things for us
|
||||
self._wantExit = True
|
||||
if self._rxThread != threading.current_thread():
|
||||
self._rxThread.join() # wait for it to exit
|
||||
@@ -177,8 +184,7 @@ class StreamInterface(MeshInterface):
|
||||
try:
|
||||
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
||||
except Exception as ex:
|
||||
logging.error(
|
||||
f"Error while handling message from radio {ex}")
|
||||
logging.error(f"Error while handling message from radio {ex}")
|
||||
traceback.print_exc()
|
||||
self._rxBuf = empty
|
||||
else:
|
||||
@@ -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"Meshtastic serial port disconnected, disconnecting... {ex}")
|
||||
logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}")
|
||||
except OSError as ex:
|
||||
if not self._wantExit: # We might intentionally get an exception during shutdown
|
||||
logging.error(
|
||||
f"Unexpected OSError, terminating meshtastic reader... {ex}")
|
||||
logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}")
|
||||
except Exception as ex:
|
||||
logging.error(
|
||||
f"Unexpected exception, terminating meshtastic reader... {ex}")
|
||||
logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
|
||||
finally:
|
||||
logging.debug("reader is exiting")
|
||||
self._disconnected()</code></pre>
|
||||
@@ -218,7 +221,8 @@ class StreamInterface(MeshInterface):
|
||||
<p>Constructor, opens a connection to self.stream</p>
|
||||
<p>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})</p>
|
||||
debugOut {stream} – 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} – 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} – If a stream is provided, any debug serial output from
|
||||
def connect(self):
|
||||
"""Connect to our radio
|
||||
|
||||
Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
|
||||
start the reading thread later.
|
||||
Normally this is called automatically by the constructor, but if you
|
||||
passed in connectNow=False you can manually start the reading thread later.
|
||||
"""
|
||||
|
||||
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
|
||||
# we write enought start bytes to force it to resync (we don't use START1 because we want to ensure it is looking for START1)
|
||||
# Send some bogus UART characters to force a sleeping device to wake, and
|
||||
# if the reading statemachine was parsing a bad packet make sure
|
||||
# we write enought start bytes to force it to resync (we don't use START1
|
||||
# because we want to ensure it is looking for START1)
|
||||
p = bytearray([START2] * 32)
|
||||
self._writeBytes(p)
|
||||
time.sleep(0.1) # wait 100ms to give device time to start running
|
||||
@@ -288,8 +295,11 @@ debugOut {stream} – If a stream is provided, any debug serial output from
|
||||
MeshInterface._disconnected(self)
|
||||
|
||||
logging.debug("Closing our port")
|
||||
# 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} – If a stream is provided, any debug serial output from
|
||||
"""Close a connection to the device"""
|
||||
logging.debug("Closing stream")
|
||||
MeshInterface.close(self)
|
||||
# pyserial cancel_read doesn't seem to work, therefore we ask the reader thread to close things for us
|
||||
# pyserial cancel_read doesn't seem to work, therefore we ask the
|
||||
# reader thread to close things for us
|
||||
self._wantExit = True
|
||||
if self._rxThread != threading.current_thread():
|
||||
self._rxThread.join() # wait for it to exit
|
||||
@@ -361,8 +372,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from
|
||||
try:
|
||||
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
||||
except Exception as ex:
|
||||
logging.error(
|
||||
f"Error while handling message from radio {ex}")
|
||||
logging.error(f"Error while handling message from radio {ex}")
|
||||
traceback.print_exc()
|
||||
self._rxBuf = empty
|
||||
else:
|
||||
@@ -370,15 +380,12 @@ debugOut {stream} – 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"Meshtastic serial port disconnected, disconnecting... {ex}")
|
||||
logging.warning(f"Meshtastic serial port disconnected, disconnecting... {ex}")
|
||||
except OSError as ex:
|
||||
if not self._wantExit: # We might intentionally get an exception during shutdown
|
||||
logging.error(
|
||||
f"Unexpected OSError, terminating meshtastic reader... {ex}")
|
||||
logging.error(f"Unexpected OSError, terminating meshtastic reader... {ex}")
|
||||
except Exception as ex:
|
||||
logging.error(
|
||||
f"Unexpected exception, terminating meshtastic reader... {ex}")
|
||||
logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
|
||||
finally:
|
||||
logging.debug("reader is exiting")
|
||||
self._disconnected()</code></pre>
|
||||
@@ -407,7 +414,8 @@ debugOut {stream} – If a stream is provided, any debug serial output from
|
||||
"""Close a connection to the device"""
|
||||
logging.debug("Closing stream")
|
||||
MeshInterface.close(self)
|
||||
# pyserial cancel_read doesn't seem to work, therefore we ask the reader thread to close things for us
|
||||
# pyserial cancel_read doesn't seem to work, therefore we ask the
|
||||
# reader thread to close things for us
|
||||
self._wantExit = True
|
||||
if self._rxThread != threading.current_thread():
|
||||
self._rxThread.join() # wait for it to exit</code></pre>
|
||||
@@ -418,8 +426,8 @@ debugOut {stream} – 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):
|
||||
"""Connect to our radio
|
||||
|
||||
Normally this is called automatically by the constructor, but if you passed in connectNow=False you can manually
|
||||
start the reading thread later.
|
||||
Normally this is called automatically by the constructor, but if you
|
||||
passed in connectNow=False you can manually start the reading thread later.
|
||||
"""
|
||||
|
||||
# Send some bogus UART characters to force a sleeping device to wake, and if the reading statemachine was parsing a bad packet make sure
|
||||
# we write enought start bytes to force it to resync (we don't use START1 because we want to ensure it is looking for START1)
|
||||
# Send some bogus UART characters to force a sleeping device to wake, and
|
||||
# if the reading statemachine was parsing a bad packet make sure
|
||||
# we write enought start bytes to force it to resync (we don't use START1
|
||||
# because we want to ensure it is looking for START1)
|
||||
p = bytearray([START2] * 32)
|
||||
self._writeBytes(p)
|
||||
time.sleep(0.1) # wait 100ms to give device time to start running
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">""" TCPInterface class for interfacing with http endpoint
|
||||
<pre><code class="python">"""TCPInterface class for interfacing with http endpoint
|
||||
"""
|
||||
import logging
|
||||
import socket
|
||||
@@ -38,7 +38,8 @@ from .stream_interface import StreamInterface
|
||||
class TCPInterface(StreamInterface):
|
||||
"""Interface class for meshtastic devices over a TCP link"""
|
||||
|
||||
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
|
||||
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
|
||||
connectNow=True, portNumber=4403):
|
||||
"""Constructor, opens a connection to a specified IP address/hostname
|
||||
|
||||
Keyword Arguments:
|
||||
@@ -62,8 +63,8 @@ class TCPInterface(StreamInterface):
|
||||
"""Close a connection to the device"""
|
||||
logging.debug("Closing TCP stream")
|
||||
StreamInterface.close(self)
|
||||
# Sometimes the socket read might be blocked in the reader thread. Therefore we force the shutdown by closing
|
||||
# the socket here
|
||||
# Sometimes the socket read might be blocked in the reader thread.
|
||||
# Therefore we force the shutdown by closing the socket here
|
||||
self._wantExit = True
|
||||
if not self.socket is None:
|
||||
try:
|
||||
@@ -106,7 +107,8 @@ hostname {string} – Hostname/IP address of the device to connect to</p></d
|
||||
<pre><code class="python">class TCPInterface(StreamInterface):
|
||||
"""Interface class for meshtastic devices over a TCP link"""
|
||||
|
||||
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
|
||||
def __init__(self, hostname: AnyStr, debugOut=None, noProto=False,
|
||||
connectNow=True, portNumber=4403):
|
||||
"""Constructor, opens a connection to a specified IP address/hostname
|
||||
|
||||
Keyword Arguments:
|
||||
@@ -130,8 +132,8 @@ hostname {string} – Hostname/IP address of the device to connect to</p></d
|
||||
"""Close a connection to the device"""
|
||||
logging.debug("Closing TCP stream")
|
||||
StreamInterface.close(self)
|
||||
# Sometimes the socket read might be blocked in the reader thread. Therefore we force the shutdown by closing
|
||||
# the socket here
|
||||
# Sometimes the socket read might be blocked in the reader thread.
|
||||
# Therefore we force the shutdown by closing the socket here
|
||||
self._wantExit = True
|
||||
if not self.socket is None:
|
||||
try:
|
||||
|
||||
@@ -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">""" Testing
|
||||
<pre><code class="python">"""With two radios connected serially, send and receive test
|
||||
messages and report back if successful.
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
"""The interfaces we are using for our tests"""
|
||||
interfaces = None
|
||||
@@ -134,25 +140,23 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
|
||||
logging.info(
|
||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far")
|
||||
|
||||
# if numFail >= 3:
|
||||
# for i in interfaces:
|
||||
# i.close()
|
||||
# return
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if numFail > maxFailures:
|
||||
logging.error("Too many failures! Test failed!")
|
||||
|
||||
return numFail
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def testThread(numTests=50):
|
||||
"""Test thread"""
|
||||
logging.info("Found devices, starting tests...")
|
||||
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):
|
||||
"""Open the debug log file"""
|
||||
debugname = "log" + portName.replace("/", "_")
|
||||
logging.info(f"Writing serial debugging to {debugname}")
|
||||
return open(debugname, 'w+', buffering=1)
|
||||
return open(debugname, 'w+', buffering=1, encoding='utf8')
|
||||
|
||||
|
||||
def testAll():
|
||||
def testAll(numTests=5):
|
||||
"""
|
||||
Run a series of tests using devices we can find.
|
||||
This is called from the cli with the "--test" option.
|
||||
|
||||
Raises:
|
||||
Exception: If not enough devices are found
|
||||
"""
|
||||
ports = util.findPorts()
|
||||
ports = meshtastic.util.findPorts()
|
||||
if len(ports) < 2:
|
||||
raise Exception("Must have at least two devices connected to USB")
|
||||
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
|
||||
|
||||
pub.subscribe(onConnection, "meshtastic.connection")
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
@@ -185,11 +188,13 @@ def testAll():
|
||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
||||
|
||||
logging.info("Ports opened, starting test")
|
||||
testThread()
|
||||
result = testThread(numTests)
|
||||
|
||||
for i in interfaces:
|
||||
i.close()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def testSimulator():
|
||||
"""
|
||||
@@ -200,7 +205,7 @@ def testSimulator():
|
||||
Run with
|
||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
||||
"""
|
||||
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.info("Connecting to simulator on localhost!")
|
||||
try:
|
||||
iface = TCPInterface("localhost")
|
||||
@@ -295,7 +300,7 @@ def testSimulator():
|
||||
"""Open the debug log file"""
|
||||
debugname = "log" + portName.replace("/", "_")
|
||||
logging.info(f"Writing serial debugging to {debugname}")
|
||||
return open(debugname, 'w+', buffering=1)</code></pre>
|
||||
return open(debugname, 'w+', buffering=1, encoding='utf8')</code></pre>
|
||||
</details>
|
||||
</dd>
|
||||
<dt id="meshtastic.test.runTests"><code class="name flex">
|
||||
@@ -328,17 +333,12 @@ def testSimulator():
|
||||
logging.info(
|
||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far")
|
||||
|
||||
# if numFail >= 3:
|
||||
# for i in interfaces:
|
||||
# i.close()
|
||||
# return
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if numFail > maxFailures:
|
||||
logging.error("Too many failures! Test failed!")
|
||||
|
||||
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 "–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):
|
||||
"""
|
||||
Run a series of tests using devices we can find.
|
||||
This is called from the cli with the "--test" option.
|
||||
|
||||
Raises:
|
||||
Exception: If not enough devices are found
|
||||
"""
|
||||
ports = util.findPorts()
|
||||
ports = meshtastic.util.findPorts()
|
||||
if len(ports) < 2:
|
||||
raise Exception("Must have at least two devices connected to USB")
|
||||
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
|
||||
|
||||
pub.subscribe(onConnection, "meshtastic.connection")
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
@@ -388,10 +383,12 @@ def testSimulator():
|
||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
||||
|
||||
logging.info("Ports opened, starting test")
|
||||
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 'from meshtastic.test import testSimulator; testSimulator()'
|
||||
"""
|
||||
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.info("Connecting to simulator on localhost!")
|
||||
try:
|
||||
iface = TCPInterface("localhost")
|
||||
@@ -494,9 +491,12 @@ python3 -c 'from meshtastic.test import testSimulator; testSimulator()'</p></div
|
||||
<pre><code class="python">def testThread(numTests=50):
|
||||
"""Test thread"""
|
||||
logging.info("Found devices, starting tests...")
|
||||
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>
|
||||
|
||||
100
docs/meshtastic/tests/conftest.html
Normal file
100
docs/meshtastic/tests/conftest.html
Normal 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">"""Common pytest code (place for fixtures)."""
|
||||
|
||||
import argparse
|
||||
|
||||
import pytest
|
||||
|
||||
from meshtastic.__main__ import Globals
|
||||
|
||||
@pytest.fixture
|
||||
def reset_globals():
|
||||
"""Fixture to reset globals."""
|
||||
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():
|
||||
"""Fixture to reset globals."""
|
||||
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>
|
||||
130
docs/meshtastic/tests/index.html
Normal file
130
docs/meshtastic/tests/index.html
Normal 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>
|
||||
95
docs/meshtastic/tests/test_ble_interface.html
Normal file
95
docs/meshtastic/tests/test_ble_interface.html
Normal 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">"""Meshtastic unit tests for ble_interface.py"""
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
from ..ble_interface import BLEInterface
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_BLEInterface():
|
||||
"""Test that we can instantiate a BLEInterface"""
|
||||
iface = BLEInterface('foo', 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():
|
||||
"""Test that we can instantiate a BLEInterface"""
|
||||
iface = BLEInterface('foo', 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>
|
||||
130
docs/meshtastic/tests/test_globals.html
Normal file
130
docs/meshtastic/tests/test_globals.html
Normal 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">"""Meshtastic unit tests for globals.py
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from ..globals import Globals
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_globals_get_instaance():
|
||||
"""Test that we can instantiate a Globals instance"""
|
||||
ourglobals = Globals.getInstance()
|
||||
ourglobals2 = Globals.getInstance()
|
||||
assert ourglobals == ourglobals2
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_globals_there_can_be_only_one():
|
||||
"""Test that we can cannot create two Globals instances"""
|
||||
# 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():
|
||||
"""Test that we can instantiate a Globals instance"""
|
||||
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():
|
||||
"""Test that we can cannot create two Globals instances"""
|
||||
# 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>
|
||||
177
docs/meshtastic/tests/test_int.html
Normal file
177
docs/meshtastic/tests/test_int.html
Normal 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">"""Meshtastic integration tests"""
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.int
|
||||
def test_int_no_args():
|
||||
"""Test without any args"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic')
|
||||
assert re.match(r'usage: meshtastic', out)
|
||||
assert return_value == 1
|
||||
|
||||
|
||||
@pytest.mark.int
|
||||
def test_int_version():
|
||||
"""Test '--version'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --version')
|
||||
assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', out)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.int
|
||||
def test_int_help():
|
||||
"""Test '--help'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --help')
|
||||
assert re.match(r'usage: meshtastic ', out)
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
@pytest.mark.int
|
||||
def test_int_support():
|
||||
"""Test '--support'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --support')
|
||||
assert re.search(r'System', out)
|
||||
assert re.search(r'Python', 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 '–help'.</p></div>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.mark.int
|
||||
def test_int_help():
|
||||
"""Test '--help'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --help')
|
||||
assert re.match(r'usage: meshtastic ', 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():
|
||||
"""Test without any args"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic')
|
||||
assert re.match(r'usage: meshtastic', 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 '–support'.</p></div>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.mark.int
|
||||
def test_int_support():
|
||||
"""Test '--support'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --support')
|
||||
assert re.search(r'System', out)
|
||||
assert re.search(r'Python', 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 '–version'.</p></div>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.mark.int
|
||||
def test_int_version():
|
||||
"""Test '--version'."""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --version')
|
||||
assert re.match(r'[0-9]+\.[0-9]+\.[0-9]', 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>
|
||||
3606
docs/meshtastic/tests/test_main.html
Normal file
3606
docs/meshtastic/tests/test_main.html
Normal file
File diff suppressed because it is too large
Load Diff
117
docs/meshtastic/tests/test_mesh_interface.html
Normal file
117
docs/meshtastic/tests/test_mesh_interface.html
Normal 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">"""Meshtastic unit tests for mesh_interface.py"""
|
||||
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from ..mesh_interface import MeshInterface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_MeshInterface(capsys):
|
||||
"""Test that we can instantiate a MeshInterface"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.showNodes()
|
||||
iface.close()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
|
||||
assert re.search(r'Nodes', out, re.MULTILINE)
|
||||
assert re.search(r'Preferences', out, re.MULTILINE)
|
||||
assert re.search(r'Channels', out, re.MULTILINE)
|
||||
assert re.search(r'Primary channel URL', out, re.MULTILINE)
|
||||
assert err == ''</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):
|
||||
"""Test that we can instantiate a MeshInterface"""
|
||||
iface = MeshInterface(noProto=True)
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.showNodes()
|
||||
iface.close()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
|
||||
assert re.search(r'Nodes', out, re.MULTILINE)
|
||||
assert re.search(r'Preferences', out, re.MULTILINE)
|
||||
assert re.search(r'Channels', out, re.MULTILINE)
|
||||
assert re.search(r'Primary channel URL', out, re.MULTILINE)
|
||||
assert err == ''</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>
|
||||
951
docs/meshtastic/tests/test_node.html
Normal file
951
docs/meshtastic/tests/test_node.html
Normal 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">"""Meshtastic unit tests for node.py"""
|
||||
|
||||
import re
|
||||
|
||||
from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
|
||||
from ..node import Node
|
||||
from ..serial_interface import SerialInterface
|
||||
from ..admin_pb2 import AdminMessage
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_node(capsys):
|
||||
"""Test that we can instantiate a Node"""
|
||||
anode = Node('foo', 'bar')
|
||||
anode.showChannels()
|
||||
anode.showInfo()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Preferences', out)
|
||||
assert re.search(r'Channels', out)
|
||||
assert re.search(r'Primary channel URL', out)
|
||||
assert err == ''
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_node_reqquestConfig():
|
||||
"""Test run requestConfig"""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
amesg = MagicMock(autospec=AdminMessage)
|
||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||
with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
|
||||
anode = Node(mo, 'bar')
|
||||
anode.requestConfig()</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):
|
||||
"""Test that we can instantiate a Node"""
|
||||
anode = Node('foo', 'bar')
|
||||
anode.showChannels()
|
||||
anode.showInfo()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Preferences', out)
|
||||
assert re.search(r'Channels', out)
|
||||
assert re.search(r'Primary channel URL', out)
|
||||
assert err == ''</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():
|
||||
"""Test run requestConfig"""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
amesg = MagicMock(autospec=AdminMessage)
|
||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||
with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
|
||||
anode = Node(mo, 'bar')
|
||||
anode.requestConfig()</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'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'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'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'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'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['key']._sizer(self.key)
|
||||
size += descriptor.fields_by_name['value']._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('Protocol message %s has no "%s" field.' %
|
||||
(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], 'InvalidateIterators'):
|
||||
self._fields[field].InvalidateIterators()
|
||||
|
||||
# Note: If the field is a sub-message, its listener will still point
|
||||
# at us. That'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):
|
||||
"""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. "foo.bar[5].baz".
|
||||
"""
|
||||
|
||||
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 = '(%s)' % field.full_name
|
||||
else:
|
||||
name = field.name
|
||||
|
||||
if _IsMapField(field):
|
||||
if _IsMessageMapField(field):
|
||||
for key in value:
|
||||
element = value[key]
|
||||
prefix = '%s[%s].' % (name, key)
|
||||
sub_errors = element.FindInitializationErrors()
|
||||
errors += [prefix + error for error in sub_errors]
|
||||
else:
|
||||
# ScalarMaps can't have any initialization errors.
|
||||
pass
|
||||
elif field.label == _FieldDescriptor.LABEL_REPEATED:
|
||||
for i in range(len(value)):
|
||||
element = value[i]
|
||||
prefix = '%s[%d].' % (name, i)
|
||||
sub_errors = element.FindInitializationErrors()
|
||||
errors += [prefix + error for error in sub_errors]
|
||||
else:
|
||||
prefix = name + '.'
|
||||
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):
|
||||
"""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.
|
||||
"""
|
||||
|
||||
# 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(
|
||||
'Parameter to MergeFrom() must be instance of same class: '
|
||||
'expected %s got %s.' % (_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('Unexpected end-group tag.')
|
||||
except (IndexError, TypeError):
|
||||
# Now ord(buf[p:p+1]) == ord('') gets TypeError.
|
||||
raise message_mod.DecodeError('Truncated message.')
|
||||
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(
|
||||
'Message %s is missing required fields: %s' % (
|
||||
self.DESCRIPTOR.full_name, ','.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):
|
||||
"""Sets the _cached_byte_size_dirty bit to true,
|
||||
and propagates this to our listener iff this was a state change.
|
||||
"""
|
||||
|
||||
# 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):
|
||||
"""Returns the name of the currently set field inside a oneof, or None."""
|
||||
try:
|
||||
field = message_descriptor.oneofs_by_name[oneof_name]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
'Protocol message has no oneof "%s" field.' % 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>
|
||||
186
docs/meshtastic/tests/test_serial_interface.html
Normal file
186
docs/meshtastic/tests/test_serial_interface.html
Normal 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">"""Meshtastic unit tests for serial_interface.py"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from ..serial_interface import SerialInterface
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('serial.Serial')
|
||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
|
||||
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
|
||||
"""Test that we can instantiate a SerialInterface with a single port"""
|
||||
iface = SerialInterface(noProto=True)
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.close()
|
||||
mocked_findPorts.assert_called()
|
||||
mocked_serial.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('meshtastic.util.findPorts', return_value=[])
|
||||
def test_SerialInterface_no_ports(mocked_findPorts, capsys):
|
||||
"""Test that we can instantiate a SerialInterface with no ports"""
|
||||
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'Warning: No Meshtastic devices detected', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake1', '/dev/ttyUSBfake2'])
|
||||
def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
|
||||
"""Test that we can instantiate a SerialInterface with two ports"""
|
||||
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'Warning: Multiple serial ports were detected', out, re.MULTILINE)
|
||||
assert err == ''</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('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake1', '/dev/ttyUSBfake2'])
|
||||
def test_SerialInterface_multiple_ports(mocked_findPorts, capsys):
|
||||
"""Test that we can instantiate a SerialInterface with two ports"""
|
||||
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'Warning: Multiple serial ports were detected', out, re.MULTILINE)
|
||||
assert err == ''</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('meshtastic.util.findPorts', return_value=[])
|
||||
def test_SerialInterface_no_ports(mocked_findPorts, capsys):
|
||||
"""Test that we can instantiate a SerialInterface with no ports"""
|
||||
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'Warning: No Meshtastic devices detected', out, re.MULTILINE)
|
||||
assert err == ''</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('serial.Serial')
|
||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
|
||||
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
|
||||
"""Test that we can instantiate a SerialInterface with a single port"""
|
||||
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>
|
||||
1822
docs/meshtastic/tests/test_smoke1.html
Normal file
1822
docs/meshtastic/tests/test_smoke1.html
Normal file
File diff suppressed because it is too large
Load Diff
127
docs/meshtastic/tests/test_smoke2.html
Normal file
127
docs/meshtastic/tests/test_smoke2.html
Normal 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">"""Meshtastic smoke tests with 2 devices connected via USB"""
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.smoke2
|
||||
def test_smoke2_info():
|
||||
"""Test --info with 2 devices connected serially"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
||||
assert re.search(r'Warning: Multiple', out, re.MULTILINE)
|
||||
assert return_value == 1
|
||||
|
||||
|
||||
@pytest.mark.smoke2
|
||||
def test_smoke2_test():
|
||||
"""Test --test"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --test')
|
||||
assert re.search(r'Writing serial debugging', out, re.MULTILINE)
|
||||
assert re.search(r'Ports opened', out, re.MULTILINE)
|
||||
assert re.search(r'Running 5 tests', 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 –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():
|
||||
"""Test --info with 2 devices connected serially"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --info')
|
||||
assert re.search(r'Warning: Multiple', 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 –test</p></div>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.mark.smoke2
|
||||
def test_smoke2_test():
|
||||
"""Test --test"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --test')
|
||||
assert re.search(r'Writing serial debugging', out, re.MULTILINE)
|
||||
assert re.search(r'Ports opened', out, re.MULTILINE)
|
||||
assert re.search(r'Running 5 tests', 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>
|
||||
115
docs/meshtastic/tests/test_smoke_wifi.html
Normal file
115
docs/meshtastic/tests/test_smoke_wifi.html
Normal 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 –set wifi_ssid 'foo' –set wifi_password 'sekret'</p>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">"""Meshtastic smoke tests a device setup with wifi.
|
||||
|
||||
Need to have run the following on an esp32 device:
|
||||
meshtastic --set wifi_ssid 'foo' --set wifi_password 'sekret'
|
||||
"""
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.smokewifi
|
||||
def test_smokewifi_info():
|
||||
"""Test --info"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --info --host meshtastic.local')
|
||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
||||
assert re.search(r'^My info', out, re.MULTILINE)
|
||||
assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
|
||||
assert re.search(r'^Preferences', out, re.MULTILINE)
|
||||
assert re.search(r'^Channels', out, re.MULTILINE)
|
||||
assert re.search(r'^ PRIMARY', out, re.MULTILINE)
|
||||
assert re.search(r'^Primary channel URL', 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 –info</p></div>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">@pytest.mark.smokewifi
|
||||
def test_smokewifi_info():
|
||||
"""Test --info"""
|
||||
return_value, out = subprocess.getstatusoutput('meshtastic --info --host meshtastic.local')
|
||||
assert re.search(r'^Owner', out, re.MULTILINE)
|
||||
assert re.search(r'^My info', out, re.MULTILINE)
|
||||
assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
|
||||
assert re.search(r'^Preferences', out, re.MULTILINE)
|
||||
assert re.search(r'^Channels', out, re.MULTILINE)
|
||||
assert re.search(r'^ PRIMARY', out, re.MULTILINE)
|
||||
assert re.search(r'^Primary channel URL', 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>
|
||||
98
docs/meshtastic/tests/test_stream_interface.html
Normal file
98
docs/meshtastic/tests/test_stream_interface.html
Normal 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">"""Meshtastic unit tests for stream_interface.py"""
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
from ..stream_interface import StreamInterface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_StreamInterface():
|
||||
"""Test that we can instantiate a StreamInterface"""
|
||||
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():
|
||||
"""Test that we can instantiate a StreamInterface"""
|
||||
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>
|
||||
120
docs/meshtastic/tests/test_tcp_interface.html
Normal file
120
docs/meshtastic/tests/test_tcp_interface.html
Normal 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">"""Meshtastic unit tests for tcp_interface.py"""
|
||||
|
||||
import re
|
||||
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from ..tcp_interface import TCPInterface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_TCPInterface(capsys):
|
||||
"""Test that we can instantiate a TCPInterface"""
|
||||
with patch('socket.socket') as mock_socket:
|
||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
|
||||
assert re.search(r'Nodes', out, re.MULTILINE)
|
||||
assert re.search(r'Preferences', out, re.MULTILINE)
|
||||
assert re.search(r'Channels', out, re.MULTILINE)
|
||||
assert re.search(r'Primary channel URL', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
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):
|
||||
"""Test that we can instantiate a TCPInterface"""
|
||||
with patch('socket.socket') as mock_socket:
|
||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'Owner: None \(None\)', out, re.MULTILINE)
|
||||
assert re.search(r'Nodes', out, re.MULTILINE)
|
||||
assert re.search(r'Preferences', out, re.MULTILINE)
|
||||
assert re.search(r'Channels', out, re.MULTILINE)
|
||||
assert re.search(r'Primary channel URL', out, re.MULTILINE)
|
||||
assert err == ''
|
||||
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>
|
||||
455
docs/meshtastic/tests/test_util.html
Normal file
455
docs/meshtastic/tests/test_util.html
Normal 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">"""Meshtastic unit tests for util.py"""
|
||||
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from meshtastic.util import fixme, stripnl, pskToString, our_exit, support_info, genPSK256, fromStr, fromPSK
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_genPSK256():
|
||||
"""Test genPSK256"""
|
||||
assert genPSK256() != ''
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_fromStr():
|
||||
"""Test fromStr"""
|
||||
assert fromStr('') == b''
|
||||
assert fromStr('0x12') == b'\x12'
|
||||
assert fromStr('t')
|
||||
assert fromStr('T')
|
||||
assert fromStr('true')
|
||||
assert fromStr('True')
|
||||
assert fromStr('yes')
|
||||
assert fromStr('Yes')
|
||||
assert fromStr('f') is False
|
||||
assert fromStr('F') is False
|
||||
assert fromStr('false') is False
|
||||
assert fromStr('False') is False
|
||||
assert fromStr('no') is False
|
||||
assert fromStr('No') is False
|
||||
assert fromStr('100.01') == 100.01
|
||||
assert fromStr('123') == 123
|
||||
assert fromStr('abc') == 'abc'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_fromPSK():
|
||||
"""Test fromPSK"""
|
||||
assert fromPSK('random') != ''
|
||||
assert fromPSK('none') == b'\x00'
|
||||
assert fromPSK('default') == b'\x01'
|
||||
assert fromPSK('simple22') == b'\x17'
|
||||
assert fromPSK('trash') == 'trash'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_stripnl():
|
||||
"""Test stripnl"""
|
||||
assert stripnl('') == ''
|
||||
assert stripnl('a\n') == 'a'
|
||||
assert stripnl(' a \n ') == 'a'
|
||||
assert stripnl('a\nb') == 'a b'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_empty_string():
|
||||
"""Test pskToString empty string"""
|
||||
assert pskToString('') == 'unencrypted'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_string():
|
||||
"""Test pskToString string"""
|
||||
assert pskToString('hunter123') == 'secret'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_one_byte_zero_value():
|
||||
"""Test pskToString one byte that is value of 0"""
|
||||
assert pskToString(bytes([0x00])) == 'unencrypted'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_one_byte_non_zero_value():
|
||||
"""Test pskToString one byte that is non-zero"""
|
||||
assert pskToString(bytes([0x01])) == 'default'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_many_bytes():
|
||||
"""Test pskToString many bytes"""
|
||||
assert pskToString(bytes([0x02, 0x01])) == 'secret'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pskToString_simple():
|
||||
"""Test pskToString simple"""
|
||||
assert pskToString(bytes([0x03])) == 'simple2'
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_our_exit_zero_return_value():
|
||||
"""Test our_exit with a zero return value"""
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
our_exit("Warning: Some message", 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():
|
||||
"""Test our_exit with a non-zero return value"""
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
our_exit("Error: Some message", 1)
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_fixme():
|
||||
"""Test fixme"""
|
||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||
fixme("some exception")
|
||||
assert pytest_wrapped_e.type == Exception
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_support_info(capsys):
|
||||
"""Test support_info"""
|
||||
support_info()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'System', out, re.MULTILINE)
|
||||
assert re.search(r'Platform', out, re.MULTILINE)
|
||||
assert re.search(r'Machine', out, re.MULTILINE)
|
||||
assert re.search(r'Executable', out, re.MULTILINE)
|
||||
assert err == ''</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():
|
||||
"""Test fixme"""
|
||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||
fixme("some exception")
|
||||
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():
|
||||
"""Test fromPSK"""
|
||||
assert fromPSK('random') != ''
|
||||
assert fromPSK('none') == b'\x00'
|
||||
assert fromPSK('default') == b'\x01'
|
||||
assert fromPSK('simple22') == b'\x17'
|
||||
assert fromPSK('trash') == 'trash'</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():
|
||||
"""Test fromStr"""
|
||||
assert fromStr('') == b''
|
||||
assert fromStr('0x12') == b'\x12'
|
||||
assert fromStr('t')
|
||||
assert fromStr('T')
|
||||
assert fromStr('true')
|
||||
assert fromStr('True')
|
||||
assert fromStr('yes')
|
||||
assert fromStr('Yes')
|
||||
assert fromStr('f') is False
|
||||
assert fromStr('F') is False
|
||||
assert fromStr('false') is False
|
||||
assert fromStr('False') is False
|
||||
assert fromStr('no') is False
|
||||
assert fromStr('No') is False
|
||||
assert fromStr('100.01') == 100.01
|
||||
assert fromStr('123') == 123
|
||||
assert fromStr('abc') == 'abc'</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():
|
||||
"""Test genPSK256"""
|
||||
assert genPSK256() != ''</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():
|
||||
"""Test our_exit with a non-zero return value"""
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
our_exit("Error: Some message", 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():
|
||||
"""Test our_exit with a zero return value"""
|
||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||
our_exit("Warning: Some message", 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():
|
||||
"""Test pskToString empty string"""
|
||||
assert pskToString('') == 'unencrypted'</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():
|
||||
"""Test pskToString many bytes"""
|
||||
assert pskToString(bytes([0x02, 0x01])) == 'secret'</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():
|
||||
"""Test pskToString one byte that is non-zero"""
|
||||
assert pskToString(bytes([0x01])) == 'default'</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():
|
||||
"""Test pskToString one byte that is value of 0"""
|
||||
assert pskToString(bytes([0x00])) == 'unencrypted'</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():
|
||||
"""Test pskToString simple"""
|
||||
assert pskToString(bytes([0x03])) == 'simple2'</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():
|
||||
"""Test pskToString string"""
|
||||
assert pskToString('hunter123') == 'secret'</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():
|
||||
"""Test stripnl"""
|
||||
assert stripnl('') == ''
|
||||
assert stripnl('a\n') == 'a'
|
||||
assert stripnl(' a \n ') == 'a'
|
||||
assert stripnl('a\nb') == 'a b'</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):
|
||||
"""Test support_info"""
|
||||
support_info()
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r'System', out, re.MULTILINE)
|
||||
assert re.search(r'Platform', out, re.MULTILINE)
|
||||
assert re.search(r'Machine', out, re.MULTILINE)
|
||||
assert re.search(r'Executable', out, re.MULTILINE)
|
||||
assert err == ''</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>
|
||||
@@ -39,7 +39,7 @@
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">""" Code for IP tunnel over a mesh
|
||||
<pre><code class="python">"""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(
|
||||
"Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work). Mesh members:")
|
||||
logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\
|
||||
"feature to work). Mesh members:")
|
||||
|
||||
pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP")
|
||||
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
||||
@@ -139,7 +142,6 @@ class Tunnel:
|
||||
|
||||
logging.debug("creating TUN device with MTU=200")
|
||||
# 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="mesh")
|
||||
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):
|
||||
"""onReceive"""
|
||||
p = packet["decoded"]["payload"]
|
||||
if packet["from"] == self.iface.myInfo.my_node_num:
|
||||
logging.debug("Ignoring message we sent")
|
||||
else:
|
||||
logging.debug(
|
||||
f"Received mesh tunnel message type={type(p)} len={len(p)}")
|
||||
# we don't really need to check for filtering here (sender should have checked), but this provides
|
||||
# useful debug printing on types of packets received
|
||||
# we don't really need to check for filtering here (sender should have checked),
|
||||
# but this provides useful debug printing on types of packets received
|
||||
if not self._shouldFilterPacket(p):
|
||||
self.tun.write(p)
|
||||
|
||||
@@ -175,8 +178,8 @@ class Tunnel:
|
||||
icmpType = p[20]
|
||||
icmpCode = p[21]
|
||||
checksum = p[22:24]
|
||||
logging.debug(
|
||||
f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}")
|
||||
# pylint: disable=line-too-long
|
||||
logging.debug(f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}")
|
||||
# reply to pings (swap src and dest but keep rest of packet unchanged)
|
||||
#pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
|
||||
# tap.write(pingback)
|
||||
@@ -195,14 +198,12 @@ class Tunnel:
|
||||
destport = readnet_u16(p, subheader + 2)
|
||||
if destport in tcpBlacklist:
|
||||
ignore = True
|
||||
logging.log(
|
||||
LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
||||
logging.log(LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
||||
else:
|
||||
logging.debug(
|
||||
f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||
else:
|
||||
logging.warning(
|
||||
f"forwarding unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destAddr)}")
|
||||
logging.warning(f"forwarding unexpected protocol 0x{protocol:02x}, "\
|
||||
"src={ipstr(srcaddr)}, dest={ipstr(destAddr)}")
|
||||
|
||||
return ignore
|
||||
|
||||
@@ -238,15 +239,14 @@ class Tunnel:
|
||||
"""Forward the provided IP packet into the mesh"""
|
||||
nodeId = self._ipToNodeId(destAddr)
|
||||
if nodeId is not None:
|
||||
logging.debug(
|
||||
f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
logging.debug(f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
self.iface.sendData(
|
||||
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
|
||||
else:
|
||||
logging.warning(
|
||||
f"Dropping packet because no node found for destIP={ipstr(destAddr)}")
|
||||
logging.warning(f"Dropping packet because no node found for destIP={ipstr(destAddr)}")
|
||||
|
||||
def close(self):
|
||||
"""Close"""
|
||||
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(
|
||||
"Starting IP to mesh tunnel (you must be root for this *pre-alpha* feature to work). Mesh members:")
|
||||
logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\
|
||||
"feature to work). Mesh members:")
|
||||
|
||||
pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP")
|
||||
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
||||
@@ -383,7 +383,6 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
|
||||
|
||||
logging.debug("creating TUN device with MTU=200")
|
||||
# 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="mesh")
|
||||
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):
|
||||
"""onReceive"""
|
||||
p = packet["decoded"]["payload"]
|
||||
if packet["from"] == self.iface.myInfo.my_node_num:
|
||||
logging.debug("Ignoring message we sent")
|
||||
else:
|
||||
logging.debug(
|
||||
f"Received mesh tunnel message type={type(p)} len={len(p)}")
|
||||
# we don't really need to check for filtering here (sender should have checked), but this provides
|
||||
# useful debug printing on types of packets received
|
||||
# we don't really need to check for filtering here (sender should have checked),
|
||||
# but this provides useful debug printing on types of packets received
|
||||
if not self._shouldFilterPacket(p):
|
||||
self.tun.write(p)
|
||||
|
||||
@@ -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"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}")
|
||||
# pylint: disable=line-too-long
|
||||
logging.debug(f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}")
|
||||
# reply to pings (swap src and dest but keep rest of packet unchanged)
|
||||
#pingback = p[:12]+p[16:20]+p[12:16]+p[20:]
|
||||
# tap.write(pingback)
|
||||
@@ -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"ignoring blacklisted TCP port {destport}")
|
||||
logging.log(LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
||||
else:
|
||||
logging.debug(
|
||||
f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||
else:
|
||||
logging.warning(
|
||||
f"forwarding unexpected protocol 0x{protocol:02x}, src={ipstr(srcaddr)}, dest={ipstr(destAddr)}")
|
||||
logging.warning(f"forwarding unexpected protocol 0x{protocol:02x}, "\
|
||||
"src={ipstr(srcaddr)}, dest={ipstr(destAddr)}")
|
||||
|
||||
return ignore
|
||||
|
||||
@@ -482,15 +480,14 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
|
||||
"""Forward the provided IP packet into the mesh"""
|
||||
nodeId = self._ipToNodeId(destAddr)
|
||||
if nodeId is not None:
|
||||
logging.debug(
|
||||
f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
logging.debug(f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
self.iface.sendData(
|
||||
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
|
||||
else:
|
||||
logging.warning(
|
||||
f"Dropping packet because no node found for destIP={ipstr(destAddr)}")
|
||||
logging.warning(f"Dropping packet because no node found for destIP={ipstr(destAddr)}")
|
||||
|
||||
def close(self):
|
||||
"""Close"""
|
||||
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):
|
||||
"""Close"""
|
||||
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):
|
||||
"""onReceive"""
|
||||
p = packet["decoded"]["payload"]
|
||||
if packet["from"] == self.iface.myInfo.my_node_num:
|
||||
logging.debug("Ignoring message we sent")
|
||||
else:
|
||||
logging.debug(
|
||||
f"Received mesh tunnel message type={type(p)} len={len(p)}")
|
||||
# we don't really need to check for filtering here (sender should have checked), but this provides
|
||||
# useful debug printing on types of packets received
|
||||
# we don't really need to check for filtering here (sender should have checked),
|
||||
# but this provides useful debug printing on types of packets received
|
||||
if not self._shouldFilterPacket(p):
|
||||
self.tun.write(p)</code></pre>
|
||||
</details>
|
||||
@@ -543,13 +542,11 @@ subnet is used to construct our network number (normally 10.115.x.x)</p></div>
|
||||
"""Forward the provided IP packet into the mesh"""
|
||||
nodeId = self._ipToNodeId(destAddr)
|
||||
if nodeId is not None:
|
||||
logging.debug(
|
||||
f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
logging.debug(f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}")
|
||||
self.iface.sendData(
|
||||
p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
|
||||
else:
|
||||
logging.warning(
|
||||
f"Dropping packet because no node found for destIP={ipstr(destAddr)}")</code></pre>
|
||||
logging.warning(f"Dropping packet because no node found for destIP={ipstr(destAddr)}")</code></pre>
|
||||
</details>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">""" Utility functions.
|
||||
<pre><code class="python">"""Utility functions.
|
||||
"""
|
||||
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():
|
||||
"""Generate a random preshared key"""
|
||||
return os.urandom(32)
|
||||
|
||||
|
||||
def fromPSK(valstr):
|
||||
"""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
|
||||
"""
|
||||
if valstr == "random":
|
||||
return genPSK256()
|
||||
elif valstr == "none":
|
||||
return bytes([0]) # Use the 'no encryption' PSK
|
||||
elif valstr == "default":
|
||||
return bytes([1]) # Use default channel psk
|
||||
elif valstr.startswith("simple"):
|
||||
# Use one of the single byte encodings
|
||||
return bytes([int(valstr[6:]) + 1])
|
||||
else:
|
||||
return fromStr(valstr)
|
||||
|
||||
|
||||
def fromStr(valstr):
|
||||
"""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
|
||||
"""
|
||||
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
||||
val = bytes()
|
||||
elif valstr.startswith('0x'):
|
||||
# if needed convert to string with asBytes.decode('utf-8')
|
||||
val = bytes.fromhex(valstr[2:])
|
||||
elif valstr.lower() in {"t", "true", "yes"}:
|
||||
val = True
|
||||
elif valstr.lower() in {"f", "false", "no"}:
|
||||
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):
|
||||
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
|
||||
if len(psk) == 0:
|
||||
@@ -61,13 +112,13 @@ def pskToString(psk: bytes):
|
||||
|
||||
|
||||
def stripnl(s):
|
||||
"""remove newlines from a string (and remove extra whitespace)"""
|
||||
"""Remove newlines from a string (and remove extra whitespace)"""
|
||||
s = str(s).replace("\n", " ")
|
||||
return ' '.join(s.split())
|
||||
|
||||
|
||||
def fixme(message):
|
||||
"""raise an exception for things that needs to be fixed"""
|
||||
"""Raise an exception for things that needs to be fixed"""
|
||||
raise Exception(f"FIXME: {message}")
|
||||
|
||||
|
||||
@@ -153,7 +204,8 @@ def our_exit(message, return_value = 1):
|
||||
|
||||
|
||||
def support_info():
|
||||
"""Print out info that is helping in support of the cli."""
|
||||
"""Print out info that helps troubleshooting of the cli."""
|
||||
print('')
|
||||
print('If having issues with meshtastic cli or python library')
|
||||
print('or wish to make feature requests, visit:')
|
||||
print('https://github.com/meshtastic/Meshtastic-python/issues')
|
||||
@@ -162,13 +214,14 @@ def support_info():
|
||||
print(' Platform: {0}'.format(platform.platform()))
|
||||
print(' Release: {0}'.format(platform.uname().release))
|
||||
print(' Machine: {0}'.format(platform.uname().machine))
|
||||
print(' Encoding (stdin): {0}'.format(sys.stdin.encoding))
|
||||
print(' Encoding (stdout): {0}'.format(sys.stdout.encoding))
|
||||
print(' meshtastic: v{0}'.format(pkg_resources.require('meshtastic')[0].version))
|
||||
print(' Executable: {0}'.format(sys.argv[0]))
|
||||
print(' Python: {0} {1} {2}'.format(platform.python_version(),
|
||||
platform.python_implementation(), platform.python_compiler()))
|
||||
print('')
|
||||
print('Please add the output from the command: meshtastic --info')
|
||||
print('')</code></pre>
|
||||
print('Please add the output from the command: meshtastic --info')</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):
|
||||
"""raise an exception for things that needs to be fixed"""
|
||||
"""Raise an exception for things that needs to be fixed"""
|
||||
raise Exception(f"FIXME: {message}")</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):
|
||||
"""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
|
||||
"""
|
||||
if valstr == "random":
|
||||
return genPSK256()
|
||||
elif valstr == "none":
|
||||
return bytes([0]) # Use the 'no encryption' PSK
|
||||
elif valstr == "default":
|
||||
return bytes([1]) # Use default channel psk
|
||||
elif valstr.startswith("simple"):
|
||||
# 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> : <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):
|
||||
"""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
|
||||
"""
|
||||
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
||||
val = bytes()
|
||||
elif valstr.startswith('0x'):
|
||||
# if needed convert to string with asBytes.decode('utf-8')
|
||||
val = bytes.fromhex(valstr[2:])
|
||||
elif valstr.lower() in {"t", "true", "yes"}:
|
||||
val = True
|
||||
elif valstr.lower() in {"f", "false", "no"}:
|
||||
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():
|
||||
"""Generate a random preshared key"""
|
||||
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):
|
||||
"""remove newlines from a string (and remove extra whitespace)"""
|
||||
"""Remove newlines from a string (and remove extra whitespace)"""
|
||||
s = str(s).replace("\n", " ")
|
||||
return ' '.join(s.split())</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():
|
||||
"""Print out info that is helping in support of the cli."""
|
||||
"""Print out info that helps troubleshooting of the cli."""
|
||||
print('')
|
||||
print('If having issues with meshtastic cli or python library')
|
||||
print('or wish to make feature requests, visit:')
|
||||
print('https://github.com/meshtastic/Meshtastic-python/issues')
|
||||
@@ -310,13 +448,14 @@ return_value defaults to 1 (non-successful)</p></div>
|
||||
print(' Platform: {0}'.format(platform.platform()))
|
||||
print(' Release: {0}'.format(platform.uname().release))
|
||||
print(' Machine: {0}'.format(platform.uname().machine))
|
||||
print(' Encoding (stdin): {0}'.format(sys.stdin.encoding))
|
||||
print(' Encoding (stdout): {0}'.format(sys.stdout.encoding))
|
||||
print(' meshtastic: v{0}'.format(pkg_resources.require('meshtastic')[0].version))
|
||||
print(' Executable: {0}'.format(sys.argv[0]))
|
||||
print(' Python: {0} {1} {2}'.format(platform.python_version(),
|
||||
platform.python_implementation(), platform.python_compiler()))
|
||||
print('')
|
||||
print('Please add the output from the command: meshtastic --info')
|
||||
print('')</code></pre>
|
||||
print('Please add the output from the command: meshtastic --info')</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>
|
||||
|
||||
Reference in New Issue
Block a user