mirror of
https://github.com/meshtastic/python.git
synced 2026-01-02 04:47:54 -05:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a3a840269 | ||
|
|
fe69f05e75 | ||
|
|
5c662822b9 | ||
|
|
c049d3424a | ||
|
|
471535853b | ||
|
|
676148cc14 | ||
|
|
a915b05240 | ||
|
|
a1668e8c66 | ||
|
|
e7664cb40b | ||
|
|
83c18f4008 | ||
|
|
8b6321ce7f | ||
|
|
9fac981ba6 | ||
|
|
ccc71930f7 | ||
|
|
9380f048fa | ||
|
|
0a655ac8df | ||
|
|
0b6676c5b3 | ||
|
|
e5ecba7ec0 | ||
|
|
a1809f5b84 | ||
|
|
9c66447913 | ||
|
|
65960fb982 | ||
|
|
9d0bc09e0f | ||
|
|
475ddcc8dd | ||
|
|
105276f98e | ||
|
|
4ee647403b | ||
|
|
10f48f130f | ||
|
|
bd697864e4 | ||
|
|
ab876c9efd | ||
|
|
aba303c677 | ||
|
|
43d59ca8d8 | ||
|
|
177705aeff | ||
|
|
b92fff0da6 | ||
|
|
6a6b72a2ae | ||
|
|
614a90c0eb | ||
|
|
9adbed4be6 | ||
|
|
809f005f61 | ||
|
|
d366e74e86 | ||
|
|
3f307880f9 | ||
|
|
50523ec1b1 | ||
|
|
684b2885aa | ||
|
|
f5eb8738fb | ||
|
|
14941c742a | ||
|
|
217add3b00 | ||
|
|
4bac85b6a9 | ||
|
|
36bed11959 | ||
|
|
6f9bcfaaff | ||
|
|
f17b66c872 | ||
|
|
040cb9bf34 | ||
|
|
3269b3018f | ||
|
|
aab10b0912 | ||
|
|
e2e9b7d55e | ||
|
|
cecc5c3b25 | ||
|
|
54bb846d00 | ||
|
|
1a5f525632 | ||
|
|
8ba3d26d63 | ||
|
|
b341b6cfdb | ||
|
|
38e7972191 | ||
|
|
5be70328fe | ||
|
|
dfe798dbdf | ||
|
|
d98b23dba0 | ||
|
|
4fbe5c7863 |
@@ -1,2 +1,6 @@
|
|||||||
[run]
|
[run]
|
||||||
omit = meshtastic/*_pb2.py,meshtastic/tests/*.py,meshtastic/test.py
|
omit = meshtastic/*_pb2.py,meshtastic/tests/*.py,meshtastic/test.py
|
||||||
|
|
||||||
|
[report]
|
||||||
|
exclude_lines =
|
||||||
|
if __name__ == .__main__.:
|
||||||
|
|||||||
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@@ -10,12 +10,17 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version:
|
||||||
|
- "3.6"
|
||||||
|
- "3.7"
|
||||||
|
- "3.8"
|
||||||
|
- "3.9"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Python 3
|
- name: Install Python 3
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v1
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
- name: Uninstall meshtastic
|
- name: Uninstall meshtastic
|
||||||
run: |
|
run: |
|
||||||
pip3 uninstall meshtastic
|
pip3 uninstall meshtastic
|
||||||
@@ -32,14 +37,31 @@ jobs:
|
|||||||
run: pylint meshtastic
|
run: pylint meshtastic
|
||||||
- name: Run tests with pytest
|
- name: Run tests with pytest
|
||||||
run: pytest --cov=meshtastic
|
run: pytest --cov=meshtastic
|
||||||
|
- name: Generate coverage report
|
||||||
|
run: |
|
||||||
|
pytest --cov=meshtastic --cov-report=xml
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
file: ./coverage.xml
|
||||||
|
flags: unittests
|
||||||
|
name: codecov-umbrella
|
||||||
|
yml: ./codecov.yml
|
||||||
|
fail_ci_if_error: true
|
||||||
validate:
|
validate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version:
|
||||||
|
- "3.6"
|
||||||
|
- "3.7"
|
||||||
|
- "3.8"
|
||||||
|
- "3.9"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Python 3
|
- name: Install Python 3
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v1
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
- name: Install meshtastic from local
|
- name: Install meshtastic from local
|
||||||
run: |
|
run: |
|
||||||
pip3 install .
|
pip3 install .
|
||||||
|
|||||||
35
.github/workflows/publish_to_pypi.yml
vendored
Normal file
35
.github/workflows/publish_to_pypi.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: Publish PyPI
|
||||||
|
|
||||||
|
on: workflow_dispatch
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
- name: Install pypa/build
|
||||||
|
run: >-
|
||||||
|
python -m
|
||||||
|
pip install
|
||||||
|
build
|
||||||
|
--user
|
||||||
|
- name: Build a binary wheel and a source tarball
|
||||||
|
run: >-
|
||||||
|
python -m
|
||||||
|
build
|
||||||
|
--sdist
|
||||||
|
--wheel
|
||||||
|
--outdir dist/
|
||||||
|
.
|
||||||
|
- name: Publish to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
|
with:
|
||||||
|
username: __token__
|
||||||
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
2
Makefile
2
Makefile
@@ -16,7 +16,7 @@ lint:
|
|||||||
|
|
||||||
# show the slowest unit tests
|
# show the slowest unit tests
|
||||||
slow:
|
slow:
|
||||||
pytest --durations=0
|
pytest --durations=5
|
||||||
|
|
||||||
# run the coverage report and open results in a browser
|
# run the coverage report and open results in a browser
|
||||||
cov:
|
cov:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
[](https://open.vscode.dev/meshtastic/Meshtastic-python)
|
[](https://open.vscode.dev/meshtastic/Meshtastic-python)
|
||||||

|

|
||||||
|
[](https://codecov.io/gh/meshtastic/Meshtastic-python)
|
||||||
|
|
||||||
A python client for using [Meshtastic](https://www.meshtastic.org) devices. This small library (and example application) provides an easy API for sending and receiving messages over mesh radios. It also provides access to any of the operations/data available in the device user interface or the Android application. Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
|
A python client for using [Meshtastic](https://www.meshtastic.org) devices. This small library (and example application) provides an easy API for sending and receiving messages over mesh radios. It also provides access to any of the operations/data available in the device user interface or the Android application. Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
rm dist/*
|
rm dist/*
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
bin/regen-docs.sh
|
|
||||||
pandoc --from=markdown --to=rst --output=README README.md
|
|
||||||
python3 setup.py sdist bdist_wheel
|
python3 setup.py sdist bdist_wheel
|
||||||
python3 -m twine upload dist/*
|
python3 -m twine upload dist/*
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,707 +0,0 @@
|
|||||||
<!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.apponly_pb2 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.apponly_pb2</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python"># -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: apponly.proto
|
|
||||||
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
from . import channel_pb2 as channel__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='apponly.proto',
|
|
||||||
package='',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=b'\n\023com.geeksville.meshB\rAppOnlyProtosH\003Z!github.com/meshtastic/gomeshproto',
|
|
||||||
serialized_pb=b'\n\rapponly.proto\x1a\rchannel.proto\"0\n\nChannelSet\x12\"\n\x08settings\x18\x01 \x03(\x0b\x32\x10.ChannelSettingsBI\n\x13\x63om.geeksville.meshB\rAppOnlyProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
|
||||||
,
|
|
||||||
dependencies=[channel__pb2.DESCRIPTOR,])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_CHANNELSET = _descriptor.Descriptor(
|
|
||||||
name='ChannelSet',
|
|
||||||
full_name='ChannelSet',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='settings', full_name='ChannelSet.settings', index=0,
|
|
||||||
number=1, type=11, cpp_type=10, label=3,
|
|
||||||
has_default_value=False, default_value=[],
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=32,
|
|
||||||
serialized_end=80,
|
|
||||||
)
|
|
||||||
|
|
||||||
_CHANNELSET.fields_by_name['settings'].message_type = channel__pb2._CHANNELSETTINGS
|
|
||||||
DESCRIPTOR.message_types_by_name['ChannelSet'] = _CHANNELSET
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
ChannelSet = _reflection.GeneratedProtocolMessageType('ChannelSet', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _CHANNELSET,
|
|
||||||
'__module__' : 'apponly_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:ChannelSet)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(ChannelSet)
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
# @@protoc_insertion_point(module_scope)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.apponly_pb2.ChannelSet"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">ChannelSet</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.apponly_pb2.ChannelSet.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.apponly_pb2.ChannelSet.SETTINGS_FIELD_NUMBER"><code class="name">var <span class="ident">SETTINGS_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Static methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.settings"><code class="name">var <span class="ident">settings</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for settings.</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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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.apponly_pb2.ChannelSet.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" 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.apponly_pb2.ChannelSet" href="#meshtastic.apponly_pb2.ChannelSet">ChannelSet</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.ByteSize" href="#meshtastic.apponly_pb2.ChannelSet.ByteSize">ByteSize</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.Clear" href="#meshtastic.apponly_pb2.ChannelSet.Clear">Clear</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.ClearField" href="#meshtastic.apponly_pb2.ChannelSet.ClearField">ClearField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.DESCRIPTOR" href="#meshtastic.apponly_pb2.ChannelSet.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.DiscardUnknownFields" href="#meshtastic.apponly_pb2.ChannelSet.DiscardUnknownFields">DiscardUnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.FindInitializationErrors" href="#meshtastic.apponly_pb2.ChannelSet.FindInitializationErrors">FindInitializationErrors</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.FromString" href="#meshtastic.apponly_pb2.ChannelSet.FromString">FromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.HasField" href="#meshtastic.apponly_pb2.ChannelSet.HasField">HasField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.IsInitialized" href="#meshtastic.apponly_pb2.ChannelSet.IsInitialized">IsInitialized</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.ListFields" href="#meshtastic.apponly_pb2.ChannelSet.ListFields">ListFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.MergeFrom" href="#meshtastic.apponly_pb2.ChannelSet.MergeFrom">MergeFrom</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.MergeFromString" href="#meshtastic.apponly_pb2.ChannelSet.MergeFromString">MergeFromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.RegisterExtension" href="#meshtastic.apponly_pb2.ChannelSet.RegisterExtension">RegisterExtension</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.SETTINGS_FIELD_NUMBER" href="#meshtastic.apponly_pb2.ChannelSet.SETTINGS_FIELD_NUMBER">SETTINGS_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.SerializePartialToString" href="#meshtastic.apponly_pb2.ChannelSet.SerializePartialToString">SerializePartialToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.SerializeToString" href="#meshtastic.apponly_pb2.ChannelSet.SerializeToString">SerializeToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.SetInParent" href="#meshtastic.apponly_pb2.ChannelSet.SetInParent">SetInParent</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.UnknownFields" href="#meshtastic.apponly_pb2.ChannelSet.UnknownFields">UnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.WhichOneof" href="#meshtastic.apponly_pb2.ChannelSet.WhichOneof">WhichOneof</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2.ChannelSet.settings" href="#meshtastic.apponly_pb2.ChannelSet.settings">settings</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>
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<!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.ble 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.ble</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
<nav id="sidebar">
|
|
||||||
<h1>Index</h1>
|
|
||||||
<div class="toc">
|
|
||||||
<ul></ul>
|
|
||||||
</div>
|
|
||||||
<ul id="index">
|
|
||||||
<li><h3>Super-module</h3>
|
|
||||||
<ul>
|
|
||||||
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
<footer id="footer">
|
|
||||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
<!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.ble_interface API documentation</title>
|
|
||||||
<meta name="description" content="Bluetooth interface" />
|
|
||||||
<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.ble_interface</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Bluetooth interface</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Bluetooth interface
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
import pygatt
|
|
||||||
|
|
||||||
|
|
||||||
from .mesh_interface import MeshInterface
|
|
||||||
|
|
||||||
# Our standard BLE characteristics
|
|
||||||
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
|
||||||
FROMRADIO_UUID = "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
|
|
||||||
FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453"
|
|
||||||
|
|
||||||
|
|
||||||
class BLEInterface(MeshInterface):
|
|
||||||
"""A not quite ready - FIXME - BLE interface to devices"""
|
|
||||||
|
|
||||||
def __init__(self, address, noProto=False, debugOut=None):
|
|
||||||
self.address = 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, noProto=noProto)
|
|
||||||
|
|
||||||
self._readFromRadio() # read the initial responses
|
|
||||||
|
|
||||||
def handle_data(handle, data):
|
|
||||||
self._handleFromRadio(data)
|
|
||||||
|
|
||||||
if self.device:
|
|
||||||
self.device.subscribe(FROMNUM_UUID, callback=handle_data)
|
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
|
||||||
"""Send a ToRadio protobuf to the device"""
|
|
||||||
#logging.debug(f"Sending: {stripnl(toRadio)}")
|
|
||||||
b = toRadio.SerializeToString()
|
|
||||||
self.device.char_write(TORADIO_UUID, b)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
MeshInterface.close(self)
|
|
||||||
if self.adapter:
|
|
||||||
self.adapter.stop()
|
|
||||||
|
|
||||||
def _readFromRadio(self):
|
|
||||||
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>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.ble_interface.BLEInterface"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">BLEInterface</span></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>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class BLEInterface(MeshInterface):
|
|
||||||
"""A not quite ready - FIXME - BLE interface to devices"""
|
|
||||||
|
|
||||||
def __init__(self, address, noProto=False, debugOut=None):
|
|
||||||
self.address = 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, noProto=noProto)
|
|
||||||
|
|
||||||
self._readFromRadio() # read the initial responses
|
|
||||||
|
|
||||||
def handle_data(handle, data):
|
|
||||||
self._handleFromRadio(data)
|
|
||||||
|
|
||||||
if self.device:
|
|
||||||
self.device.subscribe(FROMNUM_UUID, callback=handle_data)
|
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
|
||||||
"""Send a ToRadio protobuf to the device"""
|
|
||||||
#logging.debug(f"Sending: {stripnl(toRadio)}")
|
|
||||||
b = toRadio.SerializeToString()
|
|
||||||
self.device.char_write(TORADIO_UUID, b)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
MeshInterface.close(self)
|
|
||||||
if self.adapter:
|
|
||||||
self.adapter.stop()
|
|
||||||
|
|
||||||
def _readFromRadio(self):
|
|
||||||
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">
|
|
||||||
<li><a title="meshtastic.mesh_interface.MeshInterface" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface">MeshInterface</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Inherited members</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><b><a title="meshtastic.mesh_interface.MeshInterface" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface">MeshInterface</a></b></code>:
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.close" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.close">close</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getLongName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getLongName">getLongName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getMyNodeInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyNodeInfo">getMyNodeInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getMyUser" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyUser">getMyUser</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getNode" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getNode">getNode</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getShortName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getShortName">getShortName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.sendData" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendData">sendData</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.sendPosition" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendPosition">sendPosition</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.sendText" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendText">sendText</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.showInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showInfo">showInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.showNodes" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showNodes">showNodes</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.waitForConfig" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</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.ble_interface.BLEInterface" href="#meshtastic.ble_interface.BLEInterface">BLEInterface</a></code></h4>
|
|
||||||
</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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,746 +0,0 @@
|
|||||||
<!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.environmental_measurement_pb2 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.environmental_measurement_pb2</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python"># -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: environmental_measurement.proto
|
|
||||||
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='environmental_measurement.proto',
|
|
||||||
package='',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=b'Z!github.com/meshtastic/gomeshproto',
|
|
||||||
serialized_pb=b'\n\x1f\x65nvironmental_measurement.proto\"g\n\x18\x45nvironmentalMeasurement\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x42#Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ENVIRONMENTALMEASUREMENT = _descriptor.Descriptor(
|
|
||||||
name='EnvironmentalMeasurement',
|
|
||||||
full_name='EnvironmentalMeasurement',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='temperature', full_name='EnvironmentalMeasurement.temperature', index=0,
|
|
||||||
number=1, type=2, cpp_type=6, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='relative_humidity', full_name='EnvironmentalMeasurement.relative_humidity', index=1,
|
|
||||||
number=2, type=2, cpp_type=6, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='barometric_pressure', full_name='EnvironmentalMeasurement.barometric_pressure', index=2,
|
|
||||||
number=3, type=2, cpp_type=6, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=35,
|
|
||||||
serialized_end=138,
|
|
||||||
)
|
|
||||||
|
|
||||||
DESCRIPTOR.message_types_by_name['EnvironmentalMeasurement'] = _ENVIRONMENTALMEASUREMENT
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
EnvironmentalMeasurement = _reflection.GeneratedProtocolMessageType('EnvironmentalMeasurement', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _ENVIRONMENTALMEASUREMENT,
|
|
||||||
'__module__' : 'environmental_measurement_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:EnvironmentalMeasurement)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(EnvironmentalMeasurement)
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
# @@protoc_insertion_point(module_scope)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">EnvironmentalMeasurement</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.environmental_measurement_pb2.EnvironmentalMeasurement.BAROMETRIC_PRESSURE_FIELD_NUMBER"><code class="name">var <span class="ident">BAROMETRIC_PRESSURE_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.RELATIVE_HUMIDITY_FIELD_NUMBER"><code class="name">var <span class="ident">RELATIVE_HUMIDITY_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.TEMPERATURE_FIELD_NUMBER"><code class="name">var <span class="ident">TEMPERATURE_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Static methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.barometric_pressure"><code class="name">var <span class="ident">barometric_pressure</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for barometric_pressure.</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.environmental_measurement_pb2.EnvironmentalMeasurement.relative_humidity"><code class="name">var <span class="ident">relative_humidity</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for relative_humidity.</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.environmental_measurement_pb2.EnvironmentalMeasurement.temperature"><code class="name">var <span class="ident">temperature</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for temperature.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def getter(self):
|
|
||||||
# TODO(protobuf-team): This may be broken since there may not be
|
|
||||||
# default_value. Combine with has_default_value somehow.
|
|
||||||
return self._fields.get(field, default_value)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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.environmental_measurement_pb2.EnvironmentalMeasurement.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" 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.environmental_measurement_pb2.EnvironmentalMeasurement" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement">EnvironmentalMeasurement</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.BAROMETRIC_PRESSURE_FIELD_NUMBER" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.BAROMETRIC_PRESSURE_FIELD_NUMBER">BAROMETRIC_PRESSURE_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.ByteSize" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.ByteSize">ByteSize</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.Clear" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.Clear">Clear</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.ClearField" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.ClearField">ClearField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.DESCRIPTOR" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.DiscardUnknownFields" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.DiscardUnknownFields">DiscardUnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.FindInitializationErrors" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.FindInitializationErrors">FindInitializationErrors</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.FromString" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.FromString">FromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.HasField" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.HasField">HasField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.IsInitialized" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.IsInitialized">IsInitialized</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.ListFields" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.ListFields">ListFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.MergeFrom" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.MergeFrom">MergeFrom</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.MergeFromString" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.MergeFromString">MergeFromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.RELATIVE_HUMIDITY_FIELD_NUMBER" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.RELATIVE_HUMIDITY_FIELD_NUMBER">RELATIVE_HUMIDITY_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.RegisterExtension" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.RegisterExtension">RegisterExtension</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.SerializePartialToString" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.SerializePartialToString">SerializePartialToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.SerializeToString" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.SerializeToString">SerializeToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.SetInParent" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.SetInParent">SetInParent</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.TEMPERATURE_FIELD_NUMBER" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.TEMPERATURE_FIELD_NUMBER">TEMPERATURE_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.UnknownFields" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.UnknownFields">UnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.WhichOneof" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.WhichOneof">WhichOneof</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.barometric_pressure" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.barometric_pressure">barometric_pressure</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.relative_humidity" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.relative_humidity">relative_humidity</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.temperature" href="#meshtastic.environmental_measurement_pb2.EnvironmentalMeasurement.temperature">temperature</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>
|
|
||||||
@@ -1,380 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,538 +0,0 @@
|
|||||||
<!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 API documentation</title>
|
|
||||||
<meta name="description" content="an API for Meshtastic devices …" />
|
|
||||||
<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">Package <code>meshtastic</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<h1 id="an-api-for-meshtastic-devices">an API for Meshtastic devices</h1>
|
|
||||||
<p>Primary class: SerialInterface
|
|
||||||
Install with pip: "<a href="https://pypi.org/project/meshtastic/">pip3 install meshtastic</a>"
|
|
||||||
Source code on <a href="https://github.com/meshtastic/Meshtastic-python">github</a></p>
|
|
||||||
<p>properties of SerialInterface:</p>
|
|
||||||
<ul>
|
|
||||||
<li>radioConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
|
|
||||||
the device.</li>
|
|
||||||
<li>nodes - The database of received nodes.
|
|
||||||
Includes always up-to-date location and username information for each
|
|
||||||
node in the mesh.
|
|
||||||
This is a read-only datastructure.</li>
|
|
||||||
<li>nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId</li>
|
|
||||||
<li>myInfo - Contains read-only information about the local radio device (software version, hardware version, etc)</li>
|
|
||||||
</ul>
|
|
||||||
<h1 id="published-pubsub-topics">Published PubSub topics</h1>
|
|
||||||
<p>We use a <a href="https://pypubsub.readthedocs.io/en/v4.0.3/">publish-subscribe</a> model to communicate asynchronous events.
|
|
||||||
Available
|
|
||||||
topics:</p>
|
|
||||||
<ul>
|
|
||||||
<li>meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB</li>
|
|
||||||
<li>meshtastic.connection.lost - published once we've lost our link to the radio</li>
|
|
||||||
<li>meshtastic.receive.text(packet) - delivers a received packet as a dictionary, if you only care about a particular
|
|
||||||
type of packet, you should subscribe to the full topic name.
|
|
||||||
If you want to see all packets, simply subscribe to "meshtastic.receive".</li>
|
|
||||||
<li>meshtastic.receive.position(packet)</li>
|
|
||||||
<li>meshtastic.receive.user(packet)</li>
|
|
||||||
<li>meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)</li>
|
|
||||||
<li>meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc…)</li>
|
|
||||||
</ul>
|
|
||||||
<p>We receive position, user, or data packets from the mesh.
|
|
||||||
You probably only care about meshtastic.receive.data.
|
|
||||||
The first argument for
|
|
||||||
that publish will be the packet.
|
|
||||||
Text or binary data packets (from sendData or sendText) will both arrive this way.
|
|
||||||
If you print packet
|
|
||||||
you'll see the fields in the dictionary.
|
|
||||||
decoded.data.payload will contain the raw bytes that were sent.
|
|
||||||
If the packet was sent with
|
|
||||||
sendText, decoded.data.text will <strong>also</strong> be populated with the decoded string.
|
|
||||||
For ASCII these two strings will be the same, but for
|
|
||||||
unicode scripts they can be different.</p>
|
|
||||||
<h1 id="example-usage">Example Usage</h1>
|
|
||||||
<pre><code>import meshtastic
|
|
||||||
import meshtastic.serial_interface
|
|
||||||
from pubsub import pub
|
|
||||||
|
|
||||||
def onReceive(packet, interface): # called when a packet arrives
|
|
||||||
print(f"Received: {packet}")
|
|
||||||
|
|
||||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect to the radio
|
|
||||||
# defaults to broadcast, specify a destination ID if you wish
|
|
||||||
interface.sendText("hello mesh")
|
|
||||||
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
|
||||||
# By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
|
|
||||||
interface = meshtastic.serial_interface.SerialInterface()
|
|
||||||
|
|
||||||
</code></pre>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""
|
|
||||||
# an API for Meshtastic devices
|
|
||||||
|
|
||||||
Primary class: SerialInterface
|
|
||||||
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
|
||||||
Source code on [github](https://github.com/meshtastic/Meshtastic-python)
|
|
||||||
|
|
||||||
properties of SerialInterface:
|
|
||||||
|
|
||||||
- radioConfig - Current radio configuration and device settings, if you write to this the new settings will be applied to
|
|
||||||
the device.
|
|
||||||
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
|
|
||||||
node in the mesh. This is a read-only datastructure.
|
|
||||||
- nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId
|
|
||||||
- myInfo - Contains read-only information about the local radio device (software version, hardware version, etc)
|
|
||||||
|
|
||||||
# Published PubSub topics
|
|
||||||
|
|
||||||
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
|
|
||||||
topics:
|
|
||||||
|
|
||||||
- meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB
|
|
||||||
- meshtastic.connection.lost - published once we've lost our link to the radio
|
|
||||||
- meshtastic.receive.text(packet) - delivers a received packet as a dictionary, if you only care about a particular
|
|
||||||
type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive".
|
|
||||||
- meshtastic.receive.position(packet)
|
|
||||||
- meshtastic.receive.user(packet)
|
|
||||||
- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
|
|
||||||
- meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc...)
|
|
||||||
|
|
||||||
We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for
|
|
||||||
that publish will be the packet. Text or binary data packets (from sendData or sendText) will both arrive this way. If you print packet
|
|
||||||
you'll see the fields in the dictionary. decoded.data.payload will contain the raw bytes that were sent. If the packet was sent with
|
|
||||||
sendText, decoded.data.text will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for
|
|
||||||
unicode scripts they can be different.
|
|
||||||
|
|
||||||
# Example Usage
|
|
||||||
```
|
|
||||||
import meshtastic
|
|
||||||
import meshtastic.serial_interface
|
|
||||||
from pubsub import pub
|
|
||||||
|
|
||||||
def onReceive(packet, interface): # called when a packet arrives
|
|
||||||
print(f"Received: {packet}")
|
|
||||||
|
|
||||||
def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect to the radio
|
|
||||||
# defaults to broadcast, specify a destination ID if you wish
|
|
||||||
interface.sendText("hello mesh")
|
|
||||||
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
|
||||||
# By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
|
|
||||||
interface = meshtastic.serial_interface.SerialInterface()
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import random
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import stat
|
|
||||||
import threading
|
|
||||||
import traceback
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import *
|
|
||||||
import serial
|
|
||||||
import timeago
|
|
||||||
import google.protobuf.json_format
|
|
||||||
import pygatt
|
|
||||||
from pubsub import pub
|
|
||||||
from dotmap import DotMap
|
|
||||||
from tabulate import tabulate
|
|
||||||
from google.protobuf.json_format import MessageToJson
|
|
||||||
from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
|
||||||
from .node import Node
|
|
||||||
from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util
|
|
||||||
|
|
||||||
# Note: To follow PEP224, comments should be after the module variable.
|
|
||||||
|
|
||||||
LOCAL_ADDR = "^local"
|
|
||||||
"""A special ID that means the local node"""
|
|
||||||
|
|
||||||
BROADCAST_NUM = 0xffffffff
|
|
||||||
"""if using 8 bit nodenums this will be shortend on the target"""
|
|
||||||
|
|
||||||
BROADCAST_ADDR = "^all"
|
|
||||||
"""A special ID that means broadcast"""
|
|
||||||
|
|
||||||
OUR_APP_VERSION = 20200
|
|
||||||
"""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
|
|
||||||
"""
|
|
||||||
|
|
||||||
publishingThread = DeferredExecution("publishing")
|
|
||||||
|
|
||||||
|
|
||||||
class ResponseHandler(NamedTuple):
|
|
||||||
"""A pending response callback, waiting for a response to one of our messages"""
|
|
||||||
# requestId: int - used only as a key
|
|
||||||
callback: Callable
|
|
||||||
# FIXME, add timestamp and age out old requests
|
|
||||||
|
|
||||||
|
|
||||||
class KnownProtocol(NamedTuple):
|
|
||||||
"""Used to automatically decode known protocol payloads"""
|
|
||||||
name: str
|
|
||||||
# portnum: int, now a key
|
|
||||||
# If set, will be called to prase as a protocol buffer
|
|
||||||
protobufFactory: Callable = None
|
|
||||||
# If set, invoked as onReceive(interface, packet)
|
|
||||||
onReceive: Callable = None
|
|
||||||
|
|
||||||
|
|
||||||
def _onTextReceive(iface, asDict):
|
|
||||||
"""Special text auto parsing for received messages"""
|
|
||||||
# We don't throw if the utf8 is invalid in the text message. Instead we just don't populate
|
|
||||||
# the decoded.data.text and we log an error message. This at least allows some delivery to
|
|
||||||
# the app and the app can deal with the missing decoded representation.
|
|
||||||
#
|
|
||||||
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
|
|
||||||
# text.
|
|
||||||
try:
|
|
||||||
asBytes = asDict["decoded"]["payload"]
|
|
||||||
asDict["decoded"]["text"] = asBytes.decode("utf-8")
|
|
||||||
except Exception as ex:
|
|
||||||
logging.error(f"Malformatted utf8 in text message: {ex}")
|
|
||||||
_receiveInfoUpdate(iface, asDict)
|
|
||||||
|
|
||||||
|
|
||||||
def _onPositionReceive(iface, asDict):
|
|
||||||
"""Special auto parsing for received messages"""
|
|
||||||
p = asDict["decoded"]["position"]
|
|
||||||
iface._fixupPosition(p)
|
|
||||||
# update node DB as needed
|
|
||||||
iface._getOrCreateByNum(asDict["from"])["position"] = p
|
|
||||||
|
|
||||||
|
|
||||||
def _onNodeInfoReceive(iface, asDict):
|
|
||||||
"""Special auto parsing for received messages"""
|
|
||||||
p = asDict["decoded"]["user"]
|
|
||||||
# decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
|
|
||||||
# update node DB as needed
|
|
||||||
n = iface._getOrCreateByNum(asDict["from"])
|
|
||||||
n["user"] = p
|
|
||||||
# We now have a node ID, make sure it is uptodate in that table
|
|
||||||
iface.nodes[p["id"]] = n
|
|
||||||
_receiveInfoUpdate(iface, asDict)
|
|
||||||
|
|
||||||
|
|
||||||
def _receiveInfoUpdate(iface, asDict):
|
|
||||||
if "from" in asDict:
|
|
||||||
iface._getOrCreateByNum(asDict["from"])["lastReceived"] = asDict
|
|
||||||
iface._getOrCreateByNum(asDict["from"])["lastHeard"] = asDict.get("rxTime")
|
|
||||||
iface._getOrCreateByNum(asDict["from"])["snr"] = asDict.get("rxSnr")
|
|
||||||
iface._getOrCreateByNum(asDict["from"])["hopLimit"] = asDict.get("hopLimit")
|
|
||||||
|
|
||||||
|
|
||||||
"""Well known message payloads can register decoders for automatic protobuf parsing"""
|
|
||||||
protocols = {
|
|
||||||
portnums_pb2.PortNum.TEXT_MESSAGE_APP: KnownProtocol("text", onReceive=_onTextReceive),
|
|
||||||
portnums_pb2.PortNum.POSITION_APP: KnownProtocol("position", mesh_pb2.Position, _onPositionReceive),
|
|
||||||
portnums_pb2.PortNum.NODEINFO_APP: KnownProtocol("user", mesh_pb2.User, _onNodeInfoReceive),
|
|
||||||
portnums_pb2.PortNum.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage),
|
|
||||||
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
|
|
||||||
portnums_pb2.PortNum.ENVIRONMENTAL_MEASUREMENT_APP: KnownProtocol("environmental", environmental_measurement_pb2.EnvironmentalMeasurement),
|
|
||||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
|
|
||||||
"remotehw", remote_hardware_pb2.HardwareMessage)
|
|
||||||
}</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
|
|
||||||
<dl>
|
|
||||||
<dt><code class="name"><a title="meshtastic.admin_pb2" href="admin_pb2.html">meshtastic.admin_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.apponly_pb2" href="apponly_pb2.html">meshtastic.apponly_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.ble" href="ble.html">meshtastic.ble</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.ble_interface" href="ble_interface.html">meshtastic.ble_interface</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Bluetooth interface</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.channel_pb2" href="channel_pb2.html">meshtastic.channel_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.deviceonly_pb2" href="deviceonly_pb2.html">meshtastic.deviceonly_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.environmental_measurement_pb2" href="environmental_measurement_pb2.html">meshtastic.environmental_measurement_pb2</a></code></dt>
|
|
||||||
<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>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.mesh_pb2" href="mesh_pb2.html">meshtastic.mesh_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.mqtt_pb2" href="mqtt_pb2.html">meshtastic.mqtt_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.node" href="node.html">meshtastic.node</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Node class</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.portnums_pb2" href="portnums_pb2.html">meshtastic.portnums_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.radioconfig_pb2" href="radioconfig_pb2.html">meshtastic.radioconfig_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.remote_hardware" href="remote_hardware.html">meshtastic.remote_hardware</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Remote hardware</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.remote_hardware_pb2" href="remote_hardware_pb2.html">meshtastic.remote_hardware_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.serial_interface" href="serial_interface.html">meshtastic.serial_interface</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Serial interface class</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.storeforward_pb2" href="storeforward_pb2.html">meshtastic.storeforward_pb2</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.stream_interface" href="stream_interface.html">meshtastic.stream_interface</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Stream Interface base class</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.tcp_interface" href="tcp_interface.html">meshtastic.tcp_interface</a></code></dt>
|
|
||||||
<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.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>
|
|
||||||
<dt><code class="name"><a title="meshtastic.tunnel" href="tunnel.html">meshtastic.tunnel</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Code for IP tunnel over a mesh …</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt><code class="name"><a title="meshtastic.util" href="util.html">meshtastic.util</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Utility functions.</p></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-variables">Global variables</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.BROADCAST_ADDR"><code class="name">var <span class="ident">BROADCAST_ADDR</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A special ID that means broadcast</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.BROADCAST_NUM"><code class="name">var <span class="ident">BROADCAST_NUM</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>if using 8 bit nodenums this will be shortend on the target</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.LOCAL_ADDR"><code class="name">var <span class="ident">LOCAL_ADDR</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A special ID that means the local node</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.OUR_APP_VERSION"><code class="name">var <span class="ident">OUR_APP_VERSION</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>
|
|
||||||
<p>format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20</p></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.KnownProtocol"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">KnownProtocol</span></span>
|
|
||||||
<span>(</span><span>name: str, protobufFactory: Callable = None, onReceive: Callable = None)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Used to automatically decode known protocol payloads</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class KnownProtocol(NamedTuple):
|
|
||||||
"""Used to automatically decode known protocol payloads"""
|
|
||||||
name: str
|
|
||||||
# portnum: int, now a key
|
|
||||||
# If set, will be called to prase as a protocol buffer
|
|
||||||
protobufFactory: Callable = None
|
|
||||||
# If set, invoked as onReceive(interface, packet)
|
|
||||||
onReceive: Callable = None</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li>builtins.tuple</li>
|
|
||||||
</ul>
|
|
||||||
<h3>Instance variables</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.KnownProtocol.name"><code class="name">var <span class="ident">name</span> : str</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Alias for field number 0</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.KnownProtocol.onReceive"><code class="name">var <span class="ident">onReceive</span> : Callable</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Alias for field number 2</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.KnownProtocol.protobufFactory"><code class="name">var <span class="ident">protobufFactory</span> : Callable</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Alias for field number 1</p></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.ResponseHandler"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">ResponseHandler</span></span>
|
|
||||||
<span>(</span><span>callback: Callable)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A pending response callback, waiting for a response to one of our messages</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class ResponseHandler(NamedTuple):
|
|
||||||
"""A pending response callback, waiting for a response to one of our messages"""
|
|
||||||
# requestId: int - used only as a key
|
|
||||||
callback: Callable
|
|
||||||
# FIXME, add timestamp and age out old requests</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li>builtins.tuple</li>
|
|
||||||
</ul>
|
|
||||||
<h3>Instance variables</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.ResponseHandler.callback"><code class="name">var <span class="ident">callback</span> : Callable</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Alias for field number 0</p></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
<nav id="sidebar">
|
|
||||||
<h1>Index</h1>
|
|
||||||
<div class="toc">
|
|
||||||
<ul>
|
|
||||||
<li><a href="#an-api-for-meshtastic-devices">an API for Meshtastic devices</a></li>
|
|
||||||
<li><a href="#published-pubsub-topics">Published PubSub topics</a></li>
|
|
||||||
<li><a href="#example-usage">Example Usage</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<ul id="index">
|
|
||||||
<li><h3><a href="#header-submodules">Sub-modules</a></h3>
|
|
||||||
<ul>
|
|
||||||
<li><code><a title="meshtastic.admin_pb2" href="admin_pb2.html">meshtastic.admin_pb2</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.apponly_pb2" href="apponly_pb2.html">meshtastic.apponly_pb2</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.ble" href="ble.html">meshtastic.ble</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.ble_interface" href="ble_interface.html">meshtastic.ble_interface</a></code></li>
|
|
||||||
<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>
|
|
||||||
<li><code><a title="meshtastic.node" href="node.html">meshtastic.node</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.portnums_pb2" href="portnums_pb2.html">meshtastic.portnums_pb2</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.radioconfig_pb2" href="radioconfig_pb2.html">meshtastic.radioconfig_pb2</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware" href="remote_hardware.html">meshtastic.remote_hardware</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2" href="remote_hardware_pb2.html">meshtastic.remote_hardware_pb2</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.serial_interface" href="serial_interface.html">meshtastic.serial_interface</a></code></li>
|
|
||||||
<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.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>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-variables">Global variables</a></h3>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.BROADCAST_ADDR" href="#meshtastic.BROADCAST_ADDR">BROADCAST_ADDR</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.BROADCAST_NUM" href="#meshtastic.BROADCAST_NUM">BROADCAST_NUM</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.LOCAL_ADDR" href="#meshtastic.LOCAL_ADDR">LOCAL_ADDR</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.OUR_APP_VERSION" href="#meshtastic.OUR_APP_VERSION">OUR_APP_VERSION</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.KnownProtocol" href="#meshtastic.KnownProtocol">KnownProtocol</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.KnownProtocol.name" href="#meshtastic.KnownProtocol.name">name</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.KnownProtocol.onReceive" href="#meshtastic.KnownProtocol.onReceive">onReceive</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.KnownProtocol.protobufFactory" href="#meshtastic.KnownProtocol.protobufFactory">protobufFactory</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.ResponseHandler" href="#meshtastic.ResponseHandler">ResponseHandler</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.ResponseHandler.callback" href="#meshtastic.ResponseHandler.callback">callback</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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,759 +0,0 @@
|
|||||||
<!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.mqtt_pb2 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.mqtt_pb2</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python"># -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: mqtt.proto
|
|
||||||
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
from . import mesh_pb2 as mesh__pb2
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='mqtt.proto',
|
|
||||||
package='',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=b'\n\023com.geeksville.meshB\nMQTTProtosH\003Z!github.com/meshtastic/gomeshproto',
|
|
||||||
serialized_pb=b'\n\nmqtt.proto\x1a\nmesh.proto\"V\n\x0fServiceEnvelope\x12\x1b\n\x06packet\x18\x01 \x01(\x0b\x32\x0b.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\tBF\n\x13\x63om.geeksville.meshB\nMQTTProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
|
||||||
,
|
|
||||||
dependencies=[mesh__pb2.DESCRIPTOR,])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_SERVICEENVELOPE = _descriptor.Descriptor(
|
|
||||||
name='ServiceEnvelope',
|
|
||||||
full_name='ServiceEnvelope',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='packet', full_name='ServiceEnvelope.packet', index=0,
|
|
||||||
number=1, type=11, cpp_type=10, label=1,
|
|
||||||
has_default_value=False, default_value=None,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='channel_id', full_name='ServiceEnvelope.channel_id', index=1,
|
|
||||||
number=2, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='gateway_id', full_name='ServiceEnvelope.gateway_id', index=2,
|
|
||||||
number=3, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=26,
|
|
||||||
serialized_end=112,
|
|
||||||
)
|
|
||||||
|
|
||||||
_SERVICEENVELOPE.fields_by_name['packet'].message_type = mesh__pb2._MESHPACKET
|
|
||||||
DESCRIPTOR.message_types_by_name['ServiceEnvelope'] = _SERVICEENVELOPE
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
ServiceEnvelope = _reflection.GeneratedProtocolMessageType('ServiceEnvelope', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _SERVICEENVELOPE,
|
|
||||||
'__module__' : 'mqtt_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:ServiceEnvelope)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(ServiceEnvelope)
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
# @@protoc_insertion_point(module_scope)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.mqtt_pb2.ServiceEnvelope"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">ServiceEnvelope</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.mqtt_pb2.ServiceEnvelope.CHANNEL_ID_FIELD_NUMBER"><code class="name">var <span class="ident">CHANNEL_ID_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.mqtt_pb2.ServiceEnvelope.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.mqtt_pb2.ServiceEnvelope.GATEWAY_ID_FIELD_NUMBER"><code class="name">var <span class="ident">GATEWAY_ID_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.mqtt_pb2.ServiceEnvelope.PACKET_FIELD_NUMBER"><code class="name">var <span class="ident">PACKET_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Static methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.channel_id"><code class="name">var <span class="ident">channel_id</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for channel_id.</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.mqtt_pb2.ServiceEnvelope.gateway_id"><code class="name">var <span class="ident">gateway_id</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for gateway_id.</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.mqtt_pb2.ServiceEnvelope.packet"><code class="name">var <span class="ident">packet</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for packet.</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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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.mqtt_pb2.ServiceEnvelope.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" 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.mqtt_pb2.ServiceEnvelope" href="#meshtastic.mqtt_pb2.ServiceEnvelope">ServiceEnvelope</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.ByteSize" href="#meshtastic.mqtt_pb2.ServiceEnvelope.ByteSize">ByteSize</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.CHANNEL_ID_FIELD_NUMBER" href="#meshtastic.mqtt_pb2.ServiceEnvelope.CHANNEL_ID_FIELD_NUMBER">CHANNEL_ID_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.Clear" href="#meshtastic.mqtt_pb2.ServiceEnvelope.Clear">Clear</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.ClearField" href="#meshtastic.mqtt_pb2.ServiceEnvelope.ClearField">ClearField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.DESCRIPTOR" href="#meshtastic.mqtt_pb2.ServiceEnvelope.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.DiscardUnknownFields" href="#meshtastic.mqtt_pb2.ServiceEnvelope.DiscardUnknownFields">DiscardUnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.FindInitializationErrors" href="#meshtastic.mqtt_pb2.ServiceEnvelope.FindInitializationErrors">FindInitializationErrors</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.FromString" href="#meshtastic.mqtt_pb2.ServiceEnvelope.FromString">FromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.GATEWAY_ID_FIELD_NUMBER" href="#meshtastic.mqtt_pb2.ServiceEnvelope.GATEWAY_ID_FIELD_NUMBER">GATEWAY_ID_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.HasField" href="#meshtastic.mqtt_pb2.ServiceEnvelope.HasField">HasField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.IsInitialized" href="#meshtastic.mqtt_pb2.ServiceEnvelope.IsInitialized">IsInitialized</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.ListFields" href="#meshtastic.mqtt_pb2.ServiceEnvelope.ListFields">ListFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.MergeFrom" href="#meshtastic.mqtt_pb2.ServiceEnvelope.MergeFrom">MergeFrom</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.MergeFromString" href="#meshtastic.mqtt_pb2.ServiceEnvelope.MergeFromString">MergeFromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.PACKET_FIELD_NUMBER" href="#meshtastic.mqtt_pb2.ServiceEnvelope.PACKET_FIELD_NUMBER">PACKET_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.RegisterExtension" href="#meshtastic.mqtt_pb2.ServiceEnvelope.RegisterExtension">RegisterExtension</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.SerializePartialToString" href="#meshtastic.mqtt_pb2.ServiceEnvelope.SerializePartialToString">SerializePartialToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.SerializeToString" href="#meshtastic.mqtt_pb2.ServiceEnvelope.SerializeToString">SerializeToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.SetInParent" href="#meshtastic.mqtt_pb2.ServiceEnvelope.SetInParent">SetInParent</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.UnknownFields" href="#meshtastic.mqtt_pb2.ServiceEnvelope.UnknownFields">UnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.WhichOneof" href="#meshtastic.mqtt_pb2.ServiceEnvelope.WhichOneof">WhichOneof</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.channel_id" href="#meshtastic.mqtt_pb2.ServiceEnvelope.channel_id">channel_id</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.gateway_id" href="#meshtastic.mqtt_pb2.ServiceEnvelope.gateway_id">gateway_id</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mqtt_pb2.ServiceEnvelope.packet" href="#meshtastic.mqtt_pb2.ServiceEnvelope.packet">packet</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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,190 +0,0 @@
|
|||||||
<!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.portnums_pb2 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.portnums_pb2</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python"># -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: portnums.proto
|
|
||||||
|
|
||||||
from google.protobuf.internal import enum_type_wrapper
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='portnums.proto',
|
|
||||||
package='',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=b'\n\023com.geeksville.meshB\010PortnumsH\003Z!github.com/meshtastic/gomeshproto',
|
|
||||||
serialized_pb=b'\n\x0eportnums.proto*\xcb\x02\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12!\n\x1d\x45NVIRONMENTAL_MEASUREMENT_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
|
||||||
)
|
|
||||||
|
|
||||||
_PORTNUM = _descriptor.EnumDescriptor(
|
|
||||||
name='PortNum',
|
|
||||||
full_name='PortNum',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
values=[
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='UNKNOWN_APP', index=0, number=0,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='TEXT_MESSAGE_APP', index=1, number=1,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='REMOTE_HARDWARE_APP', index=2, number=2,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='POSITION_APP', index=3, number=3,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='NODEINFO_APP', index=4, number=4,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='ROUTING_APP', index=5, number=5,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='ADMIN_APP', index=6, number=6,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='REPLY_APP', index=7, number=32,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='IP_TUNNEL_APP', index=8, number=33,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='SERIAL_APP', index=9, number=64,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='STORE_FORWARD_APP', index=10, number=65,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='RANGE_TEST_APP', index=11, number=66,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='ENVIRONMENTAL_MEASUREMENT_APP', index=12, number=67,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='ZPS_APP', index=13, number=68,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='PRIVATE_APP', index=14, number=256,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='ATAK_FORWARDER', index=15, number=257,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='MAX', index=16, number=511,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
],
|
|
||||||
containing_type=None,
|
|
||||||
serialized_options=None,
|
|
||||||
serialized_start=19,
|
|
||||||
serialized_end=350,
|
|
||||||
)
|
|
||||||
_sym_db.RegisterEnumDescriptor(_PORTNUM)
|
|
||||||
|
|
||||||
PortNum = enum_type_wrapper.EnumTypeWrapper(_PORTNUM)
|
|
||||||
UNKNOWN_APP = 0
|
|
||||||
TEXT_MESSAGE_APP = 1
|
|
||||||
REMOTE_HARDWARE_APP = 2
|
|
||||||
POSITION_APP = 3
|
|
||||||
NODEINFO_APP = 4
|
|
||||||
ROUTING_APP = 5
|
|
||||||
ADMIN_APP = 6
|
|
||||||
REPLY_APP = 32
|
|
||||||
IP_TUNNEL_APP = 33
|
|
||||||
SERIAL_APP = 64
|
|
||||||
STORE_FORWARD_APP = 65
|
|
||||||
RANGE_TEST_APP = 66
|
|
||||||
ENVIRONMENTAL_MEASUREMENT_APP = 67
|
|
||||||
ZPS_APP = 68
|
|
||||||
PRIVATE_APP = 256
|
|
||||||
ATAK_FORWARDER = 257
|
|
||||||
MAX = 511
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR.enum_types_by_name['PortNum'] = _PORTNUM
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
# @@protoc_insertion_point(module_scope)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
<nav id="sidebar">
|
|
||||||
<h1>Index</h1>
|
|
||||||
<div class="toc">
|
|
||||||
<ul></ul>
|
|
||||||
</div>
|
|
||||||
<ul id="index">
|
|
||||||
<li><h3>Super-module</h3>
|
|
||||||
<ul>
|
|
||||||
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
<footer id="footer">
|
|
||||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,324 +0,0 @@
|
|||||||
<!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.remote_hardware API documentation</title>
|
|
||||||
<meta name="description" content="Remote hardware" />
|
|
||||||
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
|
|
||||||
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
|
|
||||||
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
|
|
||||||
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
|
|
||||||
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
|
|
||||||
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
|
|
||||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
|
|
||||||
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<article id="content">
|
|
||||||
<header>
|
|
||||||
<h1 class="title">Module <code>meshtastic.remote_hardware</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Remote hardware</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Remote hardware
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
from pubsub import pub
|
|
||||||
from . import portnums_pb2, remote_hardware_pb2
|
|
||||||
from .util import our_exit
|
|
||||||
|
|
||||||
|
|
||||||
def onGPIOreceive(packet, interface):
|
|
||||||
"""Callback for received GPIO responses
|
|
||||||
"""
|
|
||||||
logging.debug(f"packet:{packet} interface:{interface}")
|
|
||||||
hw = packet["decoded"]["remotehw"]
|
|
||||||
gpioValue = hw["gpioValue"]
|
|
||||||
#print(f'mask:{interface.mask}')
|
|
||||||
value = int(gpioValue) & int(interface.mask)
|
|
||||||
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
|
|
||||||
interface.gotResponse = True
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteHardwareClient:
|
|
||||||
"""
|
|
||||||
This is the client code to control/monitor simple hardware built into the
|
|
||||||
meshtastic devices. It is intended to be both a useful API/service and example
|
|
||||||
code for how you can connect to your own custom meshtastic services
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, iface):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
|
|
||||||
iface is the already open MeshInterface instance
|
|
||||||
"""
|
|
||||||
self.iface = iface
|
|
||||||
ch = iface.localNode.getChannelByName("gpio")
|
|
||||||
if not ch:
|
|
||||||
our_exit(
|
|
||||||
"Warning: No channel named 'gpio' was found.\n"\
|
|
||||||
"On the sending and receive nodes create a channel named 'gpio'.\n"\
|
|
||||||
"For example, run '--ch-add gpio' on one device, then '--seturl' on\n"\
|
|
||||||
"the other devices using the url from the device where the channel was added.")
|
|
||||||
self.channelIndex = ch.index
|
|
||||||
|
|
||||||
pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw")
|
|
||||||
|
|
||||||
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
|
|
||||||
if not nodeid:
|
|
||||||
our_exit(r"Warning: Must use 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)
|
|
||||||
|
|
||||||
def writeGPIOs(self, nodeid, mask, vals):
|
|
||||||
"""
|
|
||||||
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
|
||||||
are 1 will be changed
|
|
||||||
"""
|
|
||||||
logging.debug(f'writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
r.gpio_value = vals
|
|
||||||
return self._sendHardware(nodeid, r)
|
|
||||||
|
|
||||||
def readGPIOs(self, nodeid, mask, onResponse = None):
|
|
||||||
"""Read the specified bits from GPIO inputs on the device"""
|
|
||||||
logging.debug(f'readGPIOs nodeid:{nodeid} mask:{mask}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse)
|
|
||||||
|
|
||||||
def watchGPIOs(self, nodeid, mask):
|
|
||||||
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
|
||||||
logging.debug(f'watchGPIOs nodeid:{nodeid} mask:{mask}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
self.iface.mask = mask
|
|
||||||
return self._sendHardware(nodeid, r)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware.onGPIOreceive"><code class="name flex">
|
|
||||||
<span>def <span class="ident">onGPIOreceive</span></span>(<span>packet, interface)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Callback for received GPIO responses</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def onGPIOreceive(packet, interface):
|
|
||||||
"""Callback for received GPIO responses
|
|
||||||
"""
|
|
||||||
logging.debug(f"packet:{packet} interface:{interface}")
|
|
||||||
hw = packet["decoded"]["remotehw"]
|
|
||||||
gpioValue = hw["gpioValue"]
|
|
||||||
#print(f'mask:{interface.mask}')
|
|
||||||
value = int(gpioValue) & int(interface.mask)
|
|
||||||
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')
|
|
||||||
interface.gotResponse = True</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware.RemoteHardwareClient"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">RemoteHardwareClient</span></span>
|
|
||||||
<span>(</span><span>iface)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>This is the client code to control/monitor simple hardware built into the
|
|
||||||
meshtastic devices.
|
|
||||||
It is intended to be both a useful API/service and example
|
|
||||||
code for how you can connect to your own custom meshtastic services</p>
|
|
||||||
<p>Constructor</p>
|
|
||||||
<p>iface is the already open MeshInterface instance</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class RemoteHardwareClient:
|
|
||||||
"""
|
|
||||||
This is the client code to control/monitor simple hardware built into the
|
|
||||||
meshtastic devices. It is intended to be both a useful API/service and example
|
|
||||||
code for how you can connect to your own custom meshtastic services
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, iface):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
|
|
||||||
iface is the already open MeshInterface instance
|
|
||||||
"""
|
|
||||||
self.iface = iface
|
|
||||||
ch = iface.localNode.getChannelByName("gpio")
|
|
||||||
if not ch:
|
|
||||||
our_exit(
|
|
||||||
"Warning: No channel named 'gpio' was found.\n"\
|
|
||||||
"On the sending and receive nodes create a channel named 'gpio'.\n"\
|
|
||||||
"For example, run '--ch-add gpio' on one device, then '--seturl' on\n"\
|
|
||||||
"the other devices using the url from the device where the channel was added.")
|
|
||||||
self.channelIndex = ch.index
|
|
||||||
|
|
||||||
pub.subscribe(onGPIOreceive, "meshtastic.receive.remotehw")
|
|
||||||
|
|
||||||
def _sendHardware(self, nodeid, r, wantResponse=False, onResponse=None):
|
|
||||||
if not nodeid:
|
|
||||||
our_exit(r"Warning: Must use 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)
|
|
||||||
|
|
||||||
def writeGPIOs(self, nodeid, mask, vals):
|
|
||||||
"""
|
|
||||||
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
|
||||||
are 1 will be changed
|
|
||||||
"""
|
|
||||||
logging.debug(f'writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
r.gpio_value = vals
|
|
||||||
return self._sendHardware(nodeid, r)
|
|
||||||
|
|
||||||
def readGPIOs(self, nodeid, mask, onResponse = None):
|
|
||||||
"""Read the specified bits from GPIO inputs on the device"""
|
|
||||||
logging.debug(f'readGPIOs nodeid:{nodeid} mask:{mask}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse)
|
|
||||||
|
|
||||||
def watchGPIOs(self, nodeid, mask):
|
|
||||||
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
|
||||||
logging.debug(f'watchGPIOs nodeid:{nodeid} mask:{mask}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
self.iface.mask = mask
|
|
||||||
return self._sendHardware(nodeid, r)</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware.RemoteHardwareClient.readGPIOs"><code class="name flex">
|
|
||||||
<span>def <span class="ident">readGPIOs</span></span>(<span>self, nodeid, mask, onResponse=None)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Read the specified bits from GPIO inputs on the device</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def readGPIOs(self, nodeid, mask, onResponse = None):
|
|
||||||
"""Read the specified bits from GPIO inputs on the device"""
|
|
||||||
logging.debug(f'readGPIOs nodeid:{nodeid} mask:{mask}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
return self._sendHardware(nodeid, r, wantResponse=True, onResponse=onResponse)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware.RemoteHardwareClient.watchGPIOs"><code class="name flex">
|
|
||||||
<span>def <span class="ident">watchGPIOs</span></span>(<span>self, nodeid, mask)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Watch the specified bits from GPIO inputs on the device for changes</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def watchGPIOs(self, nodeid, mask):
|
|
||||||
"""Watch the specified bits from GPIO inputs on the device for changes"""
|
|
||||||
logging.debug(f'watchGPIOs nodeid:{nodeid} mask:{mask}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
self.iface.mask = mask
|
|
||||||
return self._sendHardware(nodeid, r)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware.RemoteHardwareClient.writeGPIOs"><code class="name flex">
|
|
||||||
<span>def <span class="ident">writeGPIOs</span></span>(<span>self, nodeid, mask, vals)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Write the specified vals bits to the device GPIOs.
|
|
||||||
Only bits in mask that
|
|
||||||
are 1 will be changed</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def writeGPIOs(self, nodeid, mask, vals):
|
|
||||||
"""
|
|
||||||
Write the specified vals bits to the device GPIOs. Only bits in mask that
|
|
||||||
are 1 will be changed
|
|
||||||
"""
|
|
||||||
logging.debug(f'writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}')
|
|
||||||
r = remote_hardware_pb2.HardwareMessage()
|
|
||||||
r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
|
|
||||||
r.gpio_mask = mask
|
|
||||||
r.gpio_value = vals
|
|
||||||
return self._sendHardware(nodeid, r)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
<nav id="sidebar">
|
|
||||||
<h1>Index</h1>
|
|
||||||
<div class="toc">
|
|
||||||
<ul></ul>
|
|
||||||
</div>
|
|
||||||
<ul id="index">
|
|
||||||
<li><h3>Super-module</h3>
|
|
||||||
<ul>
|
|
||||||
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.remote_hardware.onGPIOreceive" href="#meshtastic.remote_hardware.onGPIOreceive">onGPIOreceive</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.remote_hardware.RemoteHardwareClient" href="#meshtastic.remote_hardware.RemoteHardwareClient">RemoteHardwareClient</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.remote_hardware.RemoteHardwareClient.readGPIOs" href="#meshtastic.remote_hardware.RemoteHardwareClient.readGPIOs">readGPIOs</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware.RemoteHardwareClient.watchGPIOs" href="#meshtastic.remote_hardware.RemoteHardwareClient.watchGPIOs">watchGPIOs</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware.RemoteHardwareClient.writeGPIOs" href="#meshtastic.remote_hardware.RemoteHardwareClient.writeGPIOs">writeGPIOs</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
<footer id="footer">
|
|
||||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,822 +0,0 @@
|
|||||||
<!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.remote_hardware_pb2 API documentation</title>
|
|
||||||
<meta name="description" content="" />
|
|
||||||
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
|
|
||||||
<link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
|
|
||||||
<link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
|
|
||||||
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
|
|
||||||
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
|
|
||||||
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
|
|
||||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
|
|
||||||
<script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<article id="content">
|
|
||||||
<header>
|
|
||||||
<h1 class="title">Module <code>meshtastic.remote_hardware_pb2</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python"># -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# source: remote_hardware.proto
|
|
||||||
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='remote_hardware.proto',
|
|
||||||
package='',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=b'\n\023com.geeksville.meshB\016RemoteHardwareH\003Z!github.com/meshtastic/gomeshproto',
|
|
||||||
serialized_pb=b'\n\x15remote_hardware.proto\"\xca\x01\n\x0fHardwareMessage\x12\"\n\x03typ\x18\x01 \x01(\x0e\x32\x15.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42J\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_HARDWAREMESSAGE_TYPE = _descriptor.EnumDescriptor(
|
|
||||||
name='Type',
|
|
||||||
full_name='HardwareMessage.Type',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
values=[
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='UNSET', index=0, number=0,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='WRITE_GPIOS', index=1, number=1,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='WATCH_GPIOS', index=2, number=2,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='GPIOS_CHANGED', index=3, number=3,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='READ_GPIOS', index=4, number=4,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
_descriptor.EnumValueDescriptor(
|
|
||||||
name='READ_GPIOS_REPLY', index=5, number=5,
|
|
||||||
serialized_options=None,
|
|
||||||
type=None),
|
|
||||||
],
|
|
||||||
containing_type=None,
|
|
||||||
serialized_options=None,
|
|
||||||
serialized_start=120,
|
|
||||||
serialized_end=228,
|
|
||||||
)
|
|
||||||
_sym_db.RegisterEnumDescriptor(_HARDWAREMESSAGE_TYPE)
|
|
||||||
|
|
||||||
|
|
||||||
_HARDWAREMESSAGE = _descriptor.Descriptor(
|
|
||||||
name='HardwareMessage',
|
|
||||||
full_name='HardwareMessage',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='typ', full_name='HardwareMessage.typ', index=0,
|
|
||||||
number=1, type=14, cpp_type=8, label=1,
|
|
||||||
has_default_value=False, default_value=0,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='gpio_mask', full_name='HardwareMessage.gpio_mask', index=1,
|
|
||||||
number=2, type=4, cpp_type=4, label=1,
|
|
||||||
has_default_value=False, default_value=0,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='gpio_value', full_name='HardwareMessage.gpio_value', index=2,
|
|
||||||
number=3, type=4, cpp_type=4, label=1,
|
|
||||||
has_default_value=False, default_value=0,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
_HARDWAREMESSAGE_TYPE,
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=26,
|
|
||||||
serialized_end=228,
|
|
||||||
)
|
|
||||||
|
|
||||||
_HARDWAREMESSAGE.fields_by_name['typ'].enum_type = _HARDWAREMESSAGE_TYPE
|
|
||||||
_HARDWAREMESSAGE_TYPE.containing_type = _HARDWAREMESSAGE
|
|
||||||
DESCRIPTOR.message_types_by_name['HardwareMessage'] = _HARDWAREMESSAGE
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
HardwareMessage = _reflection.GeneratedProtocolMessageType('HardwareMessage', (_message.Message,), {
|
|
||||||
'DESCRIPTOR' : _HARDWAREMESSAGE,
|
|
||||||
'__module__' : 'remote_hardware_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:HardwareMessage)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(HardwareMessage)
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
|
||||||
# @@protoc_insertion_point(module_scope)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">HardwareMessage</span></span>
|
|
||||||
<span>(</span><span>**kwargs)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Abstract base class for protocol messages.</p>
|
|
||||||
<p>Protocol message classes are almost always generated by the protocol
|
|
||||||
compiler.
|
|
||||||
These generated types subclass Message and implement the methods
|
|
||||||
shown below.</p></div>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li>google.protobuf.message.Message</li>
|
|
||||||
</ul>
|
|
||||||
<h3>Class variables</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.DESCRIPTOR"><code class="name">var <span class="ident">DESCRIPTOR</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.GPIOS_CHANGED"><code class="name">var <span class="ident">GPIOS_CHANGED</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_MASK_FIELD_NUMBER"><code class="name">var <span class="ident">GPIO_MASK_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_VALUE_FIELD_NUMBER"><code class="name">var <span class="ident">GPIO_VALUE_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS"><code class="name">var <span class="ident">READ_GPIOS</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS_REPLY"><code class="name">var <span class="ident">READ_GPIOS_REPLY</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.TYP_FIELD_NUMBER"><code class="name">var <span class="ident">TYP_FIELD_NUMBER</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.Type"><code class="name">var <span class="ident">Type</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.UNSET"><code class="name">var <span class="ident">UNSET</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.WATCH_GPIOS"><code class="name">var <span class="ident">WATCH_GPIOS</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.WRITE_GPIOS"><code class="name">var <span class="ident">WRITE_GPIOS</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Static methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.FromString"><code class="name flex">
|
|
||||||
<span>def <span class="ident">FromString</span></span>(<span>s)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def FromString(s):
|
|
||||||
message = cls()
|
|
||||||
message.MergeFromString(s)
|
|
||||||
return message</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.RegisterExtension"><code class="name flex">
|
|
||||||
<span>def <span class="ident">RegisterExtension</span></span>(<span>extension_handle)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def RegisterExtension(extension_handle):
|
|
||||||
extension_handle.containing_type = cls.DESCRIPTOR
|
|
||||||
# TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available.
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
cls.DESCRIPTOR.file.pool._AddExtensionDescriptor(extension_handle)
|
|
||||||
_AttachFieldHelpers(cls, extension_handle)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Instance variables</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_mask"><code class="name">var <span class="ident">gpio_mask</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for gpio_mask.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def getter(self):
|
|
||||||
# TODO(protobuf-team): This may be broken since there may not be
|
|
||||||
# default_value. Combine with has_default_value somehow.
|
|
||||||
return self._fields.get(field, default_value)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_value"><code class="name">var <span class="ident">gpio_value</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for gpio_value.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def getter(self):
|
|
||||||
# TODO(protobuf-team): This may be broken since there may not be
|
|
||||||
# default_value. Combine with has_default_value somehow.
|
|
||||||
return self._fields.get(field, default_value)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.typ"><code class="name">var <span class="ident">typ</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Getter for typ.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def getter(self):
|
|
||||||
# TODO(protobuf-team): This may be broken since there may not be
|
|
||||||
# default_value. Combine with has_default_value somehow.
|
|
||||||
return self._fields.get(field, default_value)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.ByteSize"><code class="name flex">
|
|
||||||
<span>def <span class="ident">ByteSize</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def ByteSize(self):
|
|
||||||
if not self._cached_byte_size_dirty:
|
|
||||||
return self._cached_byte_size
|
|
||||||
|
|
||||||
size = 0
|
|
||||||
descriptor = self.DESCRIPTOR
|
|
||||||
if descriptor.GetOptions().map_entry:
|
|
||||||
# Fields of map entry should always be serialized.
|
|
||||||
size = descriptor.fields_by_name['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.remote_hardware_pb2.HardwareMessage.Clear"><code class="name flex">
|
|
||||||
<span>def <span class="ident">Clear</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def _Clear(self):
|
|
||||||
# Clear fields.
|
|
||||||
self._fields = {}
|
|
||||||
self._unknown_fields = ()
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
if self._unknown_field_set is not None:
|
|
||||||
self._unknown_field_set._clear()
|
|
||||||
self._unknown_field_set = None
|
|
||||||
|
|
||||||
self._oneofs = {}
|
|
||||||
self._Modified()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.ClearField"><code class="name flex">
|
|
||||||
<span>def <span class="ident">ClearField</span></span>(<span>self, field_name)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def ClearField(self, field_name):
|
|
||||||
try:
|
|
||||||
field = message_descriptor.fields_by_name[field_name]
|
|
||||||
except KeyError:
|
|
||||||
try:
|
|
||||||
field = message_descriptor.oneofs_by_name[field_name]
|
|
||||||
if field in self._oneofs:
|
|
||||||
field = self._oneofs[field]
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
except KeyError:
|
|
||||||
raise ValueError('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.remote_hardware_pb2.HardwareMessage.DiscardUnknownFields"><code class="name flex">
|
|
||||||
<span>def <span class="ident">DiscardUnknownFields</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def _DiscardUnknownFields(self):
|
|
||||||
self._unknown_fields = []
|
|
||||||
self._unknown_field_set = None # pylint: disable=protected-access
|
|
||||||
for field, value in self.ListFields():
|
|
||||||
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
||||||
if _IsMapField(field):
|
|
||||||
if _IsMessageMapField(field):
|
|
||||||
for key in value:
|
|
||||||
value[key].DiscardUnknownFields()
|
|
||||||
elif field.label == _FieldDescriptor.LABEL_REPEATED:
|
|
||||||
for sub_message in value:
|
|
||||||
sub_message.DiscardUnknownFields()
|
|
||||||
else:
|
|
||||||
value.DiscardUnknownFields()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.FindInitializationErrors"><code class="name flex">
|
|
||||||
<span>def <span class="ident">FindInitializationErrors</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Finds required fields which are not initialized.</p>
|
|
||||||
<h2 id="returns">Returns</h2>
|
|
||||||
<p>A list of strings.
|
|
||||||
Each string is a path to an uninitialized field from
|
|
||||||
the top-level message, e.g. "foo.bar[5].baz".</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def FindInitializationErrors(self):
|
|
||||||
"""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.remote_hardware_pb2.HardwareMessage.HasField"><code class="name flex">
|
|
||||||
<span>def <span class="ident">HasField</span></span>(<span>self, field_name)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def HasField(self, field_name):
|
|
||||||
try:
|
|
||||||
field = hassable_fields[field_name]
|
|
||||||
except KeyError:
|
|
||||||
raise ValueError(error_msg % (message_descriptor.full_name, field_name))
|
|
||||||
|
|
||||||
if isinstance(field, descriptor_mod.OneofDescriptor):
|
|
||||||
try:
|
|
||||||
return HasField(self, self._oneofs[field].name)
|
|
||||||
except KeyError:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
|
|
||||||
value = self._fields.get(field)
|
|
||||||
return value is not None and value._is_present_in_parent
|
|
||||||
else:
|
|
||||||
return field in self._fields</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.IsInitialized"><code class="name flex">
|
|
||||||
<span>def <span class="ident">IsInitialized</span></span>(<span>self, errors=None)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Checks if all required fields of a message are set.</p>
|
|
||||||
<h2 id="args">Args</h2>
|
|
||||||
<dl>
|
|
||||||
<dt><strong><code>errors</code></strong></dt>
|
|
||||||
<dd>A list which, if provided, will be populated with the field
|
|
||||||
paths of all missing required fields.</dd>
|
|
||||||
</dl>
|
|
||||||
<h2 id="returns">Returns</h2>
|
|
||||||
<p>True iff the specified message has all required fields set.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def IsInitialized(self, errors=None):
|
|
||||||
"""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.remote_hardware_pb2.HardwareMessage.ListFields"><code class="name flex">
|
|
||||||
<span>def <span class="ident">ListFields</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def ListFields(self):
|
|
||||||
all_fields = [item for item in self._fields.items() if _IsPresent(item)]
|
|
||||||
all_fields.sort(key = lambda item: item[0].number)
|
|
||||||
return all_fields</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFrom"><code class="name flex">
|
|
||||||
<span>def <span class="ident">MergeFrom</span></span>(<span>self, msg)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def MergeFrom(self, msg):
|
|
||||||
if not isinstance(msg, cls):
|
|
||||||
raise TypeError(
|
|
||||||
'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.remote_hardware_pb2.HardwareMessage.MergeFromString"><code class="name flex">
|
|
||||||
<span>def <span class="ident">MergeFromString</span></span>(<span>self, serialized)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def MergeFromString(self, serialized):
|
|
||||||
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.remote_hardware_pb2.HardwareMessage.SerializePartialToString"><code class="name flex">
|
|
||||||
<span>def <span class="ident">SerializePartialToString</span></span>(<span>self, **kwargs)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def SerializePartialToString(self, **kwargs):
|
|
||||||
out = BytesIO()
|
|
||||||
self._InternalSerialize(out.write, **kwargs)
|
|
||||||
return out.getvalue()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.SerializeToString"><code class="name flex">
|
|
||||||
<span>def <span class="ident">SerializeToString</span></span>(<span>self, **kwargs)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def SerializeToString(self, **kwargs):
|
|
||||||
# Check if the message has all of its required fields set.
|
|
||||||
if not self.IsInitialized():
|
|
||||||
raise message_mod.EncodeError(
|
|
||||||
'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.remote_hardware_pb2.HardwareMessage.SetInParent"><code class="name flex">
|
|
||||||
<span>def <span class="ident">SetInParent</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Sets the _cached_byte_size_dirty bit to true,
|
|
||||||
and propagates this to our listener iff this was a state change.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def Modified(self):
|
|
||||||
"""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.remote_hardware_pb2.HardwareMessage.UnknownFields"><code class="name flex">
|
|
||||||
<span>def <span class="ident">UnknownFields</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def _UnknownFields(self):
|
|
||||||
if self._unknown_field_set is None: # pylint: disable=protected-access
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
self._unknown_field_set = containers.UnknownFieldSet()
|
|
||||||
return self._unknown_field_set # pylint: disable=protected-access</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.remote_hardware_pb2.HardwareMessage.WhichOneof"><code class="name flex">
|
|
||||||
<span>def <span class="ident">WhichOneof</span></span>(<span>self, oneof_name)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Returns the name of the currently set field inside a oneof, or None.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def WhichOneof(self, oneof_name):
|
|
||||||
"""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" href="index.html">meshtastic</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage" href="#meshtastic.remote_hardware_pb2.HardwareMessage">HardwareMessage</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.ByteSize" href="#meshtastic.remote_hardware_pb2.HardwareMessage.ByteSize">ByteSize</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.Clear" href="#meshtastic.remote_hardware_pb2.HardwareMessage.Clear">Clear</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.ClearField" href="#meshtastic.remote_hardware_pb2.HardwareMessage.ClearField">ClearField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.DESCRIPTOR" href="#meshtastic.remote_hardware_pb2.HardwareMessage.DESCRIPTOR">DESCRIPTOR</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.DiscardUnknownFields" href="#meshtastic.remote_hardware_pb2.HardwareMessage.DiscardUnknownFields">DiscardUnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.FindInitializationErrors" href="#meshtastic.remote_hardware_pb2.HardwareMessage.FindInitializationErrors">FindInitializationErrors</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.FromString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.FromString">FromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.GPIOS_CHANGED" href="#meshtastic.remote_hardware_pb2.HardwareMessage.GPIOS_CHANGED">GPIOS_CHANGED</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_MASK_FIELD_NUMBER" href="#meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_MASK_FIELD_NUMBER">GPIO_MASK_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_VALUE_FIELD_NUMBER" href="#meshtastic.remote_hardware_pb2.HardwareMessage.GPIO_VALUE_FIELD_NUMBER">GPIO_VALUE_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.HasField" href="#meshtastic.remote_hardware_pb2.HardwareMessage.HasField">HasField</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.IsInitialized" href="#meshtastic.remote_hardware_pb2.HardwareMessage.IsInitialized">IsInitialized</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.ListFields" href="#meshtastic.remote_hardware_pb2.HardwareMessage.ListFields">ListFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFrom" href="#meshtastic.remote_hardware_pb2.HardwareMessage.MergeFrom">MergeFrom</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.MergeFromString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.MergeFromString">MergeFromString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS" href="#meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS">READ_GPIOS</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS_REPLY" href="#meshtastic.remote_hardware_pb2.HardwareMessage.READ_GPIOS_REPLY">READ_GPIOS_REPLY</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.RegisterExtension" href="#meshtastic.remote_hardware_pb2.HardwareMessage.RegisterExtension">RegisterExtension</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.SerializePartialToString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.SerializePartialToString">SerializePartialToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.SerializeToString" href="#meshtastic.remote_hardware_pb2.HardwareMessage.SerializeToString">SerializeToString</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.SetInParent" href="#meshtastic.remote_hardware_pb2.HardwareMessage.SetInParent">SetInParent</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.TYP_FIELD_NUMBER" href="#meshtastic.remote_hardware_pb2.HardwareMessage.TYP_FIELD_NUMBER">TYP_FIELD_NUMBER</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.Type" href="#meshtastic.remote_hardware_pb2.HardwareMessage.Type">Type</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.UNSET" href="#meshtastic.remote_hardware_pb2.HardwareMessage.UNSET">UNSET</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.UnknownFields" href="#meshtastic.remote_hardware_pb2.HardwareMessage.UnknownFields">UnknownFields</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.WATCH_GPIOS" href="#meshtastic.remote_hardware_pb2.HardwareMessage.WATCH_GPIOS">WATCH_GPIOS</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.WRITE_GPIOS" href="#meshtastic.remote_hardware_pb2.HardwareMessage.WRITE_GPIOS">WRITE_GPIOS</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.WhichOneof" href="#meshtastic.remote_hardware_pb2.HardwareMessage.WhichOneof">WhichOneof</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_mask" href="#meshtastic.remote_hardware_pb2.HardwareMessage.gpio_mask">gpio_mask</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.gpio_value" href="#meshtastic.remote_hardware_pb2.HardwareMessage.gpio_value">gpio_value</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.remote_hardware_pb2.HardwareMessage.typ" href="#meshtastic.remote_hardware_pb2.HardwareMessage.typ">typ</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
<footer id="footer">
|
|
||||||
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
<!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.serial_interface API documentation</title>
|
|
||||||
<meta name="description" content="Serial interface 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.serial_interface</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Serial interface class</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">""" Serial interface class
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
import platform
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import serial
|
|
||||||
|
|
||||||
import meshtastic.util
|
|
||||||
from .stream_interface import StreamInterface
|
|
||||||
|
|
||||||
class SerialInterface(StreamInterface):
|
|
||||||
"""Interface class for meshtastic devices over a serial link"""
|
|
||||||
|
|
||||||
def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
|
|
||||||
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
|
||||||
find one Meshtastic device by probing
|
|
||||||
|
|
||||||
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})
|
|
||||||
"""
|
|
||||||
|
|
||||||
if devPath is None:
|
|
||||||
ports = meshtastic.util.findPorts()
|
|
||||||
logging.debug(f"ports:{ports}")
|
|
||||||
if len(ports) == 0:
|
|
||||||
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}"
|
|
||||||
meshtastic.util.our_exit(message)
|
|
||||||
else:
|
|
||||||
devPath = ports[0]
|
|
||||||
|
|
||||||
logging.debug(f"Connecting to {devPath}")
|
|
||||||
|
|
||||||
# Note: we provide None for port here, because we will be opening it later
|
|
||||||
self.stream = serial.Serial(
|
|
||||||
None, 921600, exclusive=True, timeout=0.5, write_timeout=0)
|
|
||||||
|
|
||||||
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
|
|
||||||
self.stream.port = devPath
|
|
||||||
|
|
||||||
# HACK: If the platform driving the serial port is unable to leave the RTS pin in high-impedance
|
|
||||||
# mode, set RTS to false so that the device platform won't be reset spuriously.
|
|
||||||
# Linux does this properly, so don't apply this hack on Linux (because it makes the reset button not work).
|
|
||||||
if self._hostPlatformAlwaysDrivesUartRts():
|
|
||||||
self.stream.rts = False
|
|
||||||
self.stream.open()
|
|
||||||
|
|
||||||
StreamInterface.__init__(
|
|
||||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
|
|
||||||
|
|
||||||
"""true if platform driving the serial port is Windows Subsystem for Linux 1."""
|
|
||||||
def _isWsl1(self):
|
|
||||||
# WSL1 identifies itself as Linux, but has a special char device at /dev/lxss for use with session control,
|
|
||||||
# e.g. /init. We should treat WSL1 as Windows for the RTS-driving hack because the underlying platfrom
|
|
||||||
# serial driver for the CP21xx still exhibits the buggy behavior.
|
|
||||||
# WSL2 is not covered here, as it does not (as of 2021-May-25) support the appropriate functionality to
|
|
||||||
# share or pass-through serial ports.
|
|
||||||
try:
|
|
||||||
# Claims to be Linux, but has /dev/lxss; must be WSL 1
|
|
||||||
return platform.system() == 'Linux' and stat.S_ISCHR(os.stat('/dev/lxss').st_mode)
|
|
||||||
except:
|
|
||||||
# Couldn't stat /dev/lxss special device; not WSL1
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _hostPlatformAlwaysDrivesUartRts(self):
|
|
||||||
# OS-X/Windows seems to have a bug in its CP21xx serial drivers. It ignores that we asked for no RTSCTS
|
|
||||||
# control and will always drive RTS either high or low (rather than letting the CP102 leave
|
|
||||||
# it as an open-collector floating pin).
|
|
||||||
# TODO: When WSL2 supports USB passthrough, this will get messier. If/when WSL2 gets virtual serial
|
|
||||||
# ports that "share" the Windows serial port (and thus the Windows drivers), this code will need to be
|
|
||||||
# updated to reflect that as well -- or if T-Beams get made with an alternate USB to UART bridge that has
|
|
||||||
# a less buggy driver.
|
|
||||||
return platform.system() != 'Linux' or self._isWsl1()</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.serial_interface.SerialInterface"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">SerialInterface</span></span>
|
|
||||||
<span>(</span><span>devPath=None, debugOut=None, noProto=False, connectNow=True)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Interface class for meshtastic devices over a serial link</p>
|
|
||||||
<p>Constructor, opens a connection to a specified serial port, or if unspecified try to
|
|
||||||
find one Meshtastic device by probing</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></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class SerialInterface(StreamInterface):
|
|
||||||
"""Interface class for meshtastic devices over a serial link"""
|
|
||||||
|
|
||||||
def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
|
|
||||||
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
|
||||||
find one Meshtastic device by probing
|
|
||||||
|
|
||||||
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})
|
|
||||||
"""
|
|
||||||
|
|
||||||
if devPath is None:
|
|
||||||
ports = meshtastic.util.findPorts()
|
|
||||||
logging.debug(f"ports:{ports}")
|
|
||||||
if len(ports) == 0:
|
|
||||||
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}"
|
|
||||||
meshtastic.util.our_exit(message)
|
|
||||||
else:
|
|
||||||
devPath = ports[0]
|
|
||||||
|
|
||||||
logging.debug(f"Connecting to {devPath}")
|
|
||||||
|
|
||||||
# Note: we provide None for port here, because we will be opening it later
|
|
||||||
self.stream = serial.Serial(
|
|
||||||
None, 921600, exclusive=True, timeout=0.5, write_timeout=0)
|
|
||||||
|
|
||||||
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
|
|
||||||
self.stream.port = devPath
|
|
||||||
|
|
||||||
# HACK: If the platform driving the serial port is unable to leave the RTS pin in high-impedance
|
|
||||||
# mode, set RTS to false so that the device platform won't be reset spuriously.
|
|
||||||
# Linux does this properly, so don't apply this hack on Linux (because it makes the reset button not work).
|
|
||||||
if self._hostPlatformAlwaysDrivesUartRts():
|
|
||||||
self.stream.rts = False
|
|
||||||
self.stream.open()
|
|
||||||
|
|
||||||
StreamInterface.__init__(
|
|
||||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
|
|
||||||
|
|
||||||
"""true if platform driving the serial port is Windows Subsystem for Linux 1."""
|
|
||||||
def _isWsl1(self):
|
|
||||||
# WSL1 identifies itself as Linux, but has a special char device at /dev/lxss for use with session control,
|
|
||||||
# e.g. /init. We should treat WSL1 as Windows for the RTS-driving hack because the underlying platfrom
|
|
||||||
# serial driver for the CP21xx still exhibits the buggy behavior.
|
|
||||||
# WSL2 is not covered here, as it does not (as of 2021-May-25) support the appropriate functionality to
|
|
||||||
# share or pass-through serial ports.
|
|
||||||
try:
|
|
||||||
# Claims to be Linux, but has /dev/lxss; must be WSL 1
|
|
||||||
return platform.system() == 'Linux' and stat.S_ISCHR(os.stat('/dev/lxss').st_mode)
|
|
||||||
except:
|
|
||||||
# Couldn't stat /dev/lxss special device; not WSL1
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _hostPlatformAlwaysDrivesUartRts(self):
|
|
||||||
# OS-X/Windows seems to have a bug in its CP21xx serial drivers. It ignores that we asked for no RTSCTS
|
|
||||||
# control and will always drive RTS either high or low (rather than letting the CP102 leave
|
|
||||||
# it as an open-collector floating pin).
|
|
||||||
# TODO: When WSL2 supports USB passthrough, this will get messier. If/when WSL2 gets virtual serial
|
|
||||||
# ports that "share" the Windows serial port (and thus the Windows drivers), this code will need to be
|
|
||||||
# updated to reflect that as well -- or if T-Beams get made with an alternate USB to UART bridge that has
|
|
||||||
# a less buggy driver.
|
|
||||||
return platform.system() != 'Linux' or self._isWsl1()</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><a title="meshtastic.stream_interface.StreamInterface" href="stream_interface.html#meshtastic.stream_interface.StreamInterface">StreamInterface</a></li>
|
|
||||||
<li><a title="meshtastic.mesh_interface.MeshInterface" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface">MeshInterface</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Inherited members</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><b><a title="meshtastic.stream_interface.StreamInterface" href="stream_interface.html#meshtastic.stream_interface.StreamInterface">StreamInterface</a></b></code>:
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.close" href="stream_interface.html#meshtastic.stream_interface.StreamInterface.close">close</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.connect" href="stream_interface.html#meshtastic.stream_interface.StreamInterface.connect">connect</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getLongName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getLongName">getLongName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getMyNodeInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyNodeInfo">getMyNodeInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getMyUser" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyUser">getMyUser</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getNode" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getNode">getNode</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getShortName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getShortName">getShortName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.sendData" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendData">sendData</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.sendPosition" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendPosition">sendPosition</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.sendText" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendText">sendText</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.showInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showInfo">showInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.showNodes" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showNodes">showNodes</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.waitForConfig" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</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.serial_interface.SerialInterface" href="#meshtastic.serial_interface.SerialInterface">SerialInterface</a></code></h4>
|
|
||||||
</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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,519 +0,0 @@
|
|||||||
<!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.stream_interface API documentation</title>
|
|
||||||
<meta name="description" content="Stream Interface base 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.stream_interface</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Stream Interface base class</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Stream Interface base class
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
import serial
|
|
||||||
|
|
||||||
|
|
||||||
from .mesh_interface import MeshInterface
|
|
||||||
from .util import stripnl
|
|
||||||
|
|
||||||
|
|
||||||
START1 = 0x94
|
|
||||||
START2 = 0xc3
|
|
||||||
HEADER_LEN = 4
|
|
||||||
MAX_TO_FROM_RADIO_SIZE = 512
|
|
||||||
|
|
||||||
|
|
||||||
class StreamInterface(MeshInterface):
|
|
||||||
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
|
||||||
|
|
||||||
def __init__(self, debugOut=None, noProto=False, connectNow=True):
|
|
||||||
"""Constructor, opens a connection to self.stream
|
|
||||||
|
|
||||||
Keyword Arguments:
|
|
||||||
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]
|
|
||||||
Exception: [description]
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not hasattr(self, 'stream') and not noProto:
|
|
||||||
raise Exception(
|
|
||||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)")
|
|
||||||
self._rxBuf = bytes() # empty
|
|
||||||
self._wantExit = False
|
|
||||||
|
|
||||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
|
||||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
|
||||||
|
|
||||||
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
|
|
||||||
|
|
||||||
# Start the reader thread after superclass constructor completes init
|
|
||||||
if connectNow:
|
|
||||||
self.connect()
|
|
||||||
if not noProto:
|
|
||||||
self.waitForConfig()
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
self._rxThread.start()
|
|
||||||
|
|
||||||
self._startConfig()
|
|
||||||
|
|
||||||
if not self.noProto: # Wait for the db download if using the protocol
|
|
||||||
self._waitConnected()
|
|
||||||
|
|
||||||
def _disconnected(self):
|
|
||||||
"""We override the superclass implementation to close our port"""
|
|
||||||
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):
|
|
||||||
"""Write an array of bytes to our stream and flush"""
|
|
||||||
if self.stream: # ignore writes when stream is closed
|
|
||||||
self.stream.write(b)
|
|
||||||
self.stream.flush()
|
|
||||||
|
|
||||||
def _readBytes(self, length):
|
|
||||||
"""Read an array of bytes from our stream"""
|
|
||||||
if self.stream:
|
|
||||||
return self.stream.read(length)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
|
||||||
"""Send a ToRadio protobuf to the device"""
|
|
||||||
logging.debug(f"Sending: {stripnl(toRadio)}")
|
|
||||||
b = toRadio.SerializeToString()
|
|
||||||
bufLen = len(b)
|
|
||||||
# We convert into a string, because the TCP code doesn't work with byte arrays
|
|
||||||
header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
|
||||||
logging.debug(f'sending header:{header} b:{b}')
|
|
||||||
self._writeBytes(header + b)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""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
|
|
||||||
self._wantExit = True
|
|
||||||
if self._rxThread != threading.current_thread():
|
|
||||||
self._rxThread.join() # wait for it to exit
|
|
||||||
|
|
||||||
def __reader(self):
|
|
||||||
"""The reader thread that reads bytes from our stream"""
|
|
||||||
logging.debug('in __reader()')
|
|
||||||
empty = bytes()
|
|
||||||
|
|
||||||
try:
|
|
||||||
while not self._wantExit:
|
|
||||||
logging.debug("reading character")
|
|
||||||
b = self._readBytes(1)
|
|
||||||
logging.debug("In reader loop")
|
|
||||||
#logging.debug(f"read returned {b}")
|
|
||||||
if len(b) > 0:
|
|
||||||
c = b[0]
|
|
||||||
#logging.debug(f'c:{c}')
|
|
||||||
ptr = len(self._rxBuf)
|
|
||||||
|
|
||||||
# Assume we want to append this byte, fixme use bytearray instead
|
|
||||||
self._rxBuf = self._rxBuf + b
|
|
||||||
|
|
||||||
if ptr == 0: # looking for START1
|
|
||||||
if c != START1:
|
|
||||||
self._rxBuf = empty # failed to find start
|
|
||||||
if self.debugOut is not None:
|
|
||||||
try:
|
|
||||||
self.debugOut.write(b.decode("utf-8"))
|
|
||||||
except:
|
|
||||||
self.debugOut.write('?')
|
|
||||||
|
|
||||||
elif ptr == 1: # looking for START2
|
|
||||||
if c != START2:
|
|
||||||
self._rxBuf = empty # failed to find start2
|
|
||||||
elif ptr >= HEADER_LEN - 1: # we've at least got a header
|
|
||||||
#logging.debug('at least we received a header')
|
|
||||||
# big endian length follows header
|
|
||||||
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
|
|
||||||
|
|
||||||
if ptr == HEADER_LEN - 1: # we _just_ finished reading the header, validate length
|
|
||||||
if packetlen > MAX_TO_FROM_RADIO_SIZE:
|
|
||||||
self._rxBuf = empty # length was out out bounds, restart
|
|
||||||
|
|
||||||
if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
|
|
||||||
try:
|
|
||||||
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
|
||||||
except Exception as ex:
|
|
||||||
logging.error(f"Error while handling message from radio {ex}")
|
|
||||||
traceback.print_exc()
|
|
||||||
self._rxBuf = empty
|
|
||||||
else:
|
|
||||||
# logging.debug(f"timeout")
|
|
||||||
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}")
|
|
||||||
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}")
|
|
||||||
except Exception as ex:
|
|
||||||
logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
|
|
||||||
finally:
|
|
||||||
logging.debug("reader is exiting")
|
|
||||||
self._disconnected()</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.stream_interface.StreamInterface"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">StreamInterface</span></span>
|
|
||||||
<span>(</span><span>debugOut=None, noProto=False, connectNow=True)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Interface class for meshtastic devices over a stream link (serial, TCP, etc)</p>
|
|
||||||
<p>Constructor, opens a connection to self.stream</p>
|
|
||||||
<p>Keyword Arguments:
|
|
||||||
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>
|
|
||||||
<dd>[description]</dd>
|
|
||||||
<dt><code>Exception</code></dt>
|
|
||||||
<dd>[description]</dd>
|
|
||||||
</dl></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class StreamInterface(MeshInterface):
|
|
||||||
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
|
||||||
|
|
||||||
def __init__(self, debugOut=None, noProto=False, connectNow=True):
|
|
||||||
"""Constructor, opens a connection to self.stream
|
|
||||||
|
|
||||||
Keyword Arguments:
|
|
||||||
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]
|
|
||||||
Exception: [description]
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not hasattr(self, 'stream') and not noProto:
|
|
||||||
raise Exception(
|
|
||||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)")
|
|
||||||
self._rxBuf = bytes() # empty
|
|
||||||
self._wantExit = False
|
|
||||||
|
|
||||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
|
||||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
|
||||||
|
|
||||||
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
|
|
||||||
|
|
||||||
# Start the reader thread after superclass constructor completes init
|
|
||||||
if connectNow:
|
|
||||||
self.connect()
|
|
||||||
if not noProto:
|
|
||||||
self.waitForConfig()
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
self._rxThread.start()
|
|
||||||
|
|
||||||
self._startConfig()
|
|
||||||
|
|
||||||
if not self.noProto: # Wait for the db download if using the protocol
|
|
||||||
self._waitConnected()
|
|
||||||
|
|
||||||
def _disconnected(self):
|
|
||||||
"""We override the superclass implementation to close our port"""
|
|
||||||
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):
|
|
||||||
"""Write an array of bytes to our stream and flush"""
|
|
||||||
if self.stream: # ignore writes when stream is closed
|
|
||||||
self.stream.write(b)
|
|
||||||
self.stream.flush()
|
|
||||||
|
|
||||||
def _readBytes(self, length):
|
|
||||||
"""Read an array of bytes from our stream"""
|
|
||||||
if self.stream:
|
|
||||||
return self.stream.read(length)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
|
||||||
"""Send a ToRadio protobuf to the device"""
|
|
||||||
logging.debug(f"Sending: {stripnl(toRadio)}")
|
|
||||||
b = toRadio.SerializeToString()
|
|
||||||
bufLen = len(b)
|
|
||||||
# We convert into a string, because the TCP code doesn't work with byte arrays
|
|
||||||
header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
|
||||||
logging.debug(f'sending header:{header} b:{b}')
|
|
||||||
self._writeBytes(header + b)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""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
|
|
||||||
self._wantExit = True
|
|
||||||
if self._rxThread != threading.current_thread():
|
|
||||||
self._rxThread.join() # wait for it to exit
|
|
||||||
|
|
||||||
def __reader(self):
|
|
||||||
"""The reader thread that reads bytes from our stream"""
|
|
||||||
logging.debug('in __reader()')
|
|
||||||
empty = bytes()
|
|
||||||
|
|
||||||
try:
|
|
||||||
while not self._wantExit:
|
|
||||||
logging.debug("reading character")
|
|
||||||
b = self._readBytes(1)
|
|
||||||
logging.debug("In reader loop")
|
|
||||||
#logging.debug(f"read returned {b}")
|
|
||||||
if len(b) > 0:
|
|
||||||
c = b[0]
|
|
||||||
#logging.debug(f'c:{c}')
|
|
||||||
ptr = len(self._rxBuf)
|
|
||||||
|
|
||||||
# Assume we want to append this byte, fixme use bytearray instead
|
|
||||||
self._rxBuf = self._rxBuf + b
|
|
||||||
|
|
||||||
if ptr == 0: # looking for START1
|
|
||||||
if c != START1:
|
|
||||||
self._rxBuf = empty # failed to find start
|
|
||||||
if self.debugOut is not None:
|
|
||||||
try:
|
|
||||||
self.debugOut.write(b.decode("utf-8"))
|
|
||||||
except:
|
|
||||||
self.debugOut.write('?')
|
|
||||||
|
|
||||||
elif ptr == 1: # looking for START2
|
|
||||||
if c != START2:
|
|
||||||
self._rxBuf = empty # failed to find start2
|
|
||||||
elif ptr >= HEADER_LEN - 1: # we've at least got a header
|
|
||||||
#logging.debug('at least we received a header')
|
|
||||||
# big endian length follows header
|
|
||||||
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
|
|
||||||
|
|
||||||
if ptr == HEADER_LEN - 1: # we _just_ finished reading the header, validate length
|
|
||||||
if packetlen > MAX_TO_FROM_RADIO_SIZE:
|
|
||||||
self._rxBuf = empty # length was out out bounds, restart
|
|
||||||
|
|
||||||
if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
|
|
||||||
try:
|
|
||||||
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
|
|
||||||
except Exception as ex:
|
|
||||||
logging.error(f"Error while handling message from radio {ex}")
|
|
||||||
traceback.print_exc()
|
|
||||||
self._rxBuf = empty
|
|
||||||
else:
|
|
||||||
# logging.debug(f"timeout")
|
|
||||||
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}")
|
|
||||||
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}")
|
|
||||||
except Exception as ex:
|
|
||||||
logging.error(f"Unexpected exception, terminating meshtastic reader... {ex}")
|
|
||||||
finally:
|
|
||||||
logging.debug("reader is exiting")
|
|
||||||
self._disconnected()</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><a title="meshtastic.mesh_interface.MeshInterface" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface">MeshInterface</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Subclasses</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><a title="meshtastic.serial_interface.SerialInterface" href="serial_interface.html#meshtastic.serial_interface.SerialInterface">SerialInterface</a></li>
|
|
||||||
<li><a title="meshtastic.tcp_interface.TCPInterface" href="tcp_interface.html#meshtastic.tcp_interface.TCPInterface">TCPInterface</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.stream_interface.StreamInterface.close"><code class="name flex">
|
|
||||||
<span>def <span class="ident">close</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Close a connection to the device</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def close(self):
|
|
||||||
"""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
|
|
||||||
self._wantExit = True
|
|
||||||
if self._rxThread != threading.current_thread():
|
|
||||||
self._rxThread.join() # wait for it to exit</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.stream_interface.StreamInterface.connect"><code class="name flex">
|
|
||||||
<span>def <span class="ident">connect</span></span>(<span>self)</span>
|
|
||||||
</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>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
self._rxThread.start()
|
|
||||||
|
|
||||||
self._startConfig()
|
|
||||||
|
|
||||||
if not self.noProto: # Wait for the db download if using the protocol
|
|
||||||
self._waitConnected()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Inherited members</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><b><a title="meshtastic.mesh_interface.MeshInterface" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface">MeshInterface</a></b></code>:
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getLongName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getLongName">getLongName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getMyNodeInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyNodeInfo">getMyNodeInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getMyUser" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyUser">getMyUser</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getNode" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getNode">getNode</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.getShortName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getShortName">getShortName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.sendData" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendData">sendData</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.sendPosition" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendPosition">sendPosition</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.sendText" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendText">sendText</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.showInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showInfo">showInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.showNodes" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showNodes">showNodes</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.mesh_interface.MeshInterface.waitForConfig" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</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.stream_interface.StreamInterface" href="#meshtastic.stream_interface.StreamInterface">StreamInterface</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.close" href="#meshtastic.stream_interface.StreamInterface.close">close</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.connect" href="#meshtastic.stream_interface.StreamInterface.connect">connect</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>
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
<!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.tcp_interface API documentation</title>
|
|
||||||
<meta name="description" content="TCPInterface class for interfacing with http endpoint" />
|
|
||||||
<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.tcp_interface</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>TCPInterface class for interfacing with http endpoint</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""TCPInterface class for interfacing with http endpoint
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
import socket
|
|
||||||
from typing import AnyStr
|
|
||||||
|
|
||||||
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):
|
|
||||||
"""Constructor, opens a connection to a specified IP address/hostname
|
|
||||||
|
|
||||||
Keyword Arguments:
|
|
||||||
hostname {string} -- Hostname/IP address of the device to connect to
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Instead of wrapping as a stream, we use the native socket API
|
|
||||||
# self.stream = sock.makefile('rw')
|
|
||||||
self.stream = None
|
|
||||||
|
|
||||||
self.hostname = hostname
|
|
||||||
self.portNumber = portNumber
|
|
||||||
|
|
||||||
if connectNow:
|
|
||||||
logging.debug(f"Connecting to {hostname}")
|
|
||||||
server_address = (hostname, portNumber)
|
|
||||||
sock = socket.create_connection(server_address)
|
|
||||||
self.socket = sock
|
|
||||||
else:
|
|
||||||
self.socket = None
|
|
||||||
|
|
||||||
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
|
|
||||||
connectNow=connectNow)
|
|
||||||
|
|
||||||
def myConnect(self):
|
|
||||||
"""Connect to socket"""
|
|
||||||
server_address = (self.hostname, self.portNumber)
|
|
||||||
sock = socket.create_connection(server_address)
|
|
||||||
self.socket = sock
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""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
|
|
||||||
self._wantExit = True
|
|
||||||
if not self.socket is None:
|
|
||||||
try:
|
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
|
||||||
except:
|
|
||||||
pass # Ignore errors in shutdown, because we might have a race with the server
|
|
||||||
self.socket.close()
|
|
||||||
|
|
||||||
def _writeBytes(self, b):
|
|
||||||
"""Write an array of bytes to our stream and flush"""
|
|
||||||
self.socket.send(b)
|
|
||||||
|
|
||||||
def _readBytes(self, length):
|
|
||||||
"""Read an array of bytes from our stream"""
|
|
||||||
return self.socket.recv(length)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tcp_interface.TCPInterface"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">TCPInterface</span></span>
|
|
||||||
<span>(</span><span>hostname: ~AnyStr, debugOut=None, noProto=False, connectNow=True, portNumber=4403)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Interface class for meshtastic devices over a TCP link</p>
|
|
||||||
<p>Constructor, opens a connection to a specified IP address/hostname</p>
|
|
||||||
<p>Keyword Arguments:
|
|
||||||
hostname {string} – Hostname/IP address of the device to connect to</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<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):
|
|
||||||
"""Constructor, opens a connection to a specified IP address/hostname
|
|
||||||
|
|
||||||
Keyword Arguments:
|
|
||||||
hostname {string} -- Hostname/IP address of the device to connect to
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Instead of wrapping as a stream, we use the native socket API
|
|
||||||
# self.stream = sock.makefile('rw')
|
|
||||||
self.stream = None
|
|
||||||
|
|
||||||
self.hostname = hostname
|
|
||||||
self.portNumber = portNumber
|
|
||||||
|
|
||||||
if connectNow:
|
|
||||||
logging.debug(f"Connecting to {hostname}")
|
|
||||||
server_address = (hostname, portNumber)
|
|
||||||
sock = socket.create_connection(server_address)
|
|
||||||
self.socket = sock
|
|
||||||
else:
|
|
||||||
self.socket = None
|
|
||||||
|
|
||||||
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
|
|
||||||
connectNow=connectNow)
|
|
||||||
|
|
||||||
def myConnect(self):
|
|
||||||
"""Connect to socket"""
|
|
||||||
server_address = (self.hostname, self.portNumber)
|
|
||||||
sock = socket.create_connection(server_address)
|
|
||||||
self.socket = sock
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""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
|
|
||||||
self._wantExit = True
|
|
||||||
if not self.socket is None:
|
|
||||||
try:
|
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
|
||||||
except:
|
|
||||||
pass # Ignore errors in shutdown, because we might have a race with the server
|
|
||||||
self.socket.close()
|
|
||||||
|
|
||||||
def _writeBytes(self, b):
|
|
||||||
"""Write an array of bytes to our stream and flush"""
|
|
||||||
self.socket.send(b)
|
|
||||||
|
|
||||||
def _readBytes(self, length):
|
|
||||||
"""Read an array of bytes from our stream"""
|
|
||||||
return self.socket.recv(length)</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><a title="meshtastic.stream_interface.StreamInterface" href="stream_interface.html#meshtastic.stream_interface.StreamInterface">StreamInterface</a></li>
|
|
||||||
<li><a title="meshtastic.mesh_interface.MeshInterface" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface">MeshInterface</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tcp_interface.TCPInterface.myConnect"><code class="name flex">
|
|
||||||
<span>def <span class="ident">myConnect</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Connect to socket</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def myConnect(self):
|
|
||||||
"""Connect to socket"""
|
|
||||||
server_address = (self.hostname, self.portNumber)
|
|
||||||
sock = socket.create_connection(server_address)
|
|
||||||
self.socket = sock</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h3>Inherited members</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><b><a title="meshtastic.stream_interface.StreamInterface" href="stream_interface.html#meshtastic.stream_interface.StreamInterface">StreamInterface</a></b></code>:
|
|
||||||
<ul class="hlist">
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.close" href="stream_interface.html#meshtastic.stream_interface.StreamInterface.close">close</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.connect" href="stream_interface.html#meshtastic.stream_interface.StreamInterface.connect">connect</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getLongName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getLongName">getLongName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getMyNodeInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyNodeInfo">getMyNodeInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getMyUser" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getMyUser">getMyUser</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getNode" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getNode">getNode</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.getShortName" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.getShortName">getShortName</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.sendData" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendData">sendData</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.sendPosition" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendPosition">sendPosition</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.sendText" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.sendText">sendText</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.showInfo" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showInfo">showInfo</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.showNodes" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.showNodes">showNodes</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.stream_interface.StreamInterface.waitForConfig" href="mesh_interface.html#meshtastic.mesh_interface.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</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.tcp_interface.TCPInterface" href="#meshtastic.tcp_interface.TCPInterface">TCPInterface</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.tcp_interface.TCPInterface.myConnect" href="#meshtastic.tcp_interface.TCPInterface.myConnect">myConnect</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>
|
|
||||||
@@ -1,544 +0,0 @@
|
|||||||
<!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.test API documentation</title>
|
|
||||||
<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>
|
|
||||||
<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.test</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<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">"""With two radios connected serially, send and receive test
|
|
||||||
messages and report back if successful.
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
from dotmap import DotMap
|
|
||||||
from pubsub import pub
|
|
||||||
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
|
|
||||||
|
|
||||||
"""A list of all packets we received while the current test was running"""
|
|
||||||
receivedPackets = None
|
|
||||||
|
|
||||||
testsRunning = False
|
|
||||||
|
|
||||||
testNumber = 0
|
|
||||||
|
|
||||||
sendingInterface = None
|
|
||||||
|
|
||||||
|
|
||||||
def onReceive(packet, interface):
|
|
||||||
"""Callback invoked when a packet arrives"""
|
|
||||||
if sendingInterface == interface:
|
|
||||||
pass
|
|
||||||
# print("Ignoring sending interface")
|
|
||||||
else:
|
|
||||||
# print(f"From {interface.stream.port}: {packet}")
|
|
||||||
p = DotMap(packet)
|
|
||||||
|
|
||||||
if p.decoded.portnum == "TEXT_MESSAGE_APP":
|
|
||||||
# We only care a about clear text packets
|
|
||||||
if receivedPackets is not None:
|
|
||||||
receivedPackets.append(p)
|
|
||||||
|
|
||||||
|
|
||||||
def onNode(node):
|
|
||||||
"""Callback invoked when the node DB changes"""
|
|
||||||
print(f"Node changed: {node}")
|
|
||||||
|
|
||||||
|
|
||||||
def subscribe():
|
|
||||||
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
|
|
||||||
|
|
||||||
pub.subscribe(onNode, "meshtastic.node")
|
|
||||||
|
|
||||||
|
|
||||||
def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False):
|
|
||||||
"""
|
|
||||||
Sends one test packet between two nodes and then returns success or failure
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
fromInterface {[type]} -- [description]
|
|
||||||
toInterface {[type]} -- [description]
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
boolean -- True for success
|
|
||||||
"""
|
|
||||||
global receivedPackets
|
|
||||||
receivedPackets = []
|
|
||||||
fromNode = fromInterface.myInfo.my_node_num
|
|
||||||
|
|
||||||
if isBroadcast:
|
|
||||||
toNode = BROADCAST_NUM
|
|
||||||
else:
|
|
||||||
toNode = toInterface.myInfo.my_node_num
|
|
||||||
|
|
||||||
logging.debug(
|
|
||||||
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
|
||||||
global sendingInterface
|
|
||||||
sendingInterface = fromInterface
|
|
||||||
if not asBinary:
|
|
||||||
fromInterface.sendText(f"Test {testNumber}", toNode, wantAck=wantAck)
|
|
||||||
else:
|
|
||||||
fromInterface.sendData((f"Binary {testNumber}").encode(
|
|
||||||
"utf-8"), toNode, wantAck=wantAck)
|
|
||||||
for _ in range(60): # max of 60 secs before we timeout
|
|
||||||
time.sleep(1)
|
|
||||||
if len(receivedPackets) >= 1:
|
|
||||||
return True
|
|
||||||
return False # Failed to send
|
|
||||||
|
|
||||||
|
|
||||||
def runTests(numTests=50, wantAck=False, maxFailures=0):
|
|
||||||
"""Run the tests."""
|
|
||||||
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
|
|
||||||
numFail = 0
|
|
||||||
numSuccess = 0
|
|
||||||
for _ in range(numTests):
|
|
||||||
global testNumber
|
|
||||||
testNumber = testNumber + 1
|
|
||||||
isBroadcast = True
|
|
||||||
# asBinary=(i % 2 == 0)
|
|
||||||
success = testSend(
|
|
||||||
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck)
|
|
||||||
if not success:
|
|
||||||
numFail = numFail + 1
|
|
||||||
logging.error(
|
|
||||||
f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)")
|
|
||||||
else:
|
|
||||||
numSuccess = numSuccess + 1
|
|
||||||
logging.info(
|
|
||||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far")
|
|
||||||
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
if numFail > maxFailures:
|
|
||||||
logging.error("Too many failures! Test failed!")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def testThread(numTests=50):
|
|
||||||
"""Test thread"""
|
|
||||||
logging.info("Found devices, starting tests...")
|
|
||||||
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):
|
|
||||||
"""Callback invoked when we connect/disconnect from a radio"""
|
|
||||||
print(f"Connection changed: {topic.getName()}")
|
|
||||||
|
|
||||||
|
|
||||||
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, encoding='utf8')
|
|
||||||
|
|
||||||
|
|
||||||
def testAll(numTests=5):
|
|
||||||
"""
|
|
||||||
Run a series of tests using devices we can find.
|
|
||||||
This is called from the cli with the "--test" option.
|
|
||||||
|
|
||||||
"""
|
|
||||||
ports = meshtastic.util.findPorts()
|
|
||||||
if len(ports) < 2:
|
|
||||||
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
|
|
||||||
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection")
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
|
||||||
global interfaces
|
|
||||||
interfaces = list(map(lambda port: SerialInterface(
|
|
||||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
|
||||||
|
|
||||||
logging.info("Ports opened, starting test")
|
|
||||||
result = testThread(numTests)
|
|
||||||
|
|
||||||
for i in interfaces:
|
|
||||||
i.close()
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def testSimulator():
|
|
||||||
"""
|
|
||||||
Assume that someone has launched meshtastic-native as a simulated node.
|
|
||||||
Talk to that node over TCP, do some operations and if they are successful
|
|
||||||
exit the process with a success code, else exit with a non zero exit code.
|
|
||||||
|
|
||||||
Run with
|
|
||||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
|
||||||
"""
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
logging.info("Connecting to simulator on localhost!")
|
|
||||||
try:
|
|
||||||
iface = TCPInterface("localhost")
|
|
||||||
iface.showInfo()
|
|
||||||
iface.localNode.showInfo()
|
|
||||||
iface.localNode.exitSimulator()
|
|
||||||
iface.close()
|
|
||||||
logging.info("Integration test successful!")
|
|
||||||
except:
|
|
||||||
print("Error while testing simulator:", sys.exc_info()[0])
|
|
||||||
traceback.print_exc()
|
|
||||||
sys.exit(1)
|
|
||||||
sys.exit(0)</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-variables">Global variables</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.test.interfaces"><code class="name">var <span class="ident">interfaces</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A list of all packets we received while the current test was running</p></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.test.onConnection"><code class="name flex">
|
|
||||||
<span>def <span class="ident">onConnection</span></span>(<span>topic=pubsub.core.callables.AUTO_TOPIC)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Callback invoked when we connect/disconnect from a radio</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def onConnection(topic=pub.AUTO_TOPIC):
|
|
||||||
"""Callback invoked when we connect/disconnect from a radio"""
|
|
||||||
print(f"Connection changed: {topic.getName()}")</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.onNode"><code class="name flex">
|
|
||||||
<span>def <span class="ident">onNode</span></span>(<span>node)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Callback invoked when the node DB changes</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def onNode(node):
|
|
||||||
"""Callback invoked when the node DB changes"""
|
|
||||||
print(f"Node changed: {node}")</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.onReceive"><code class="name flex">
|
|
||||||
<span>def <span class="ident">onReceive</span></span>(<span>packet, interface)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Callback invoked when a packet arrives</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def onReceive(packet, interface):
|
|
||||||
"""Callback invoked when a packet arrives"""
|
|
||||||
if sendingInterface == interface:
|
|
||||||
pass
|
|
||||||
# print("Ignoring sending interface")
|
|
||||||
else:
|
|
||||||
# print(f"From {interface.stream.port}: {packet}")
|
|
||||||
p = DotMap(packet)
|
|
||||||
|
|
||||||
if p.decoded.portnum == "TEXT_MESSAGE_APP":
|
|
||||||
# We only care a about clear text packets
|
|
||||||
if receivedPackets is not None:
|
|
||||||
receivedPackets.append(p)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.openDebugLog"><code class="name flex">
|
|
||||||
<span>def <span class="ident">openDebugLog</span></span>(<span>portName)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Open the debug log file</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">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, encoding='utf8')</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.runTests"><code class="name flex">
|
|
||||||
<span>def <span class="ident">runTests</span></span>(<span>numTests=50, wantAck=False, maxFailures=0)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Run the tests.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def runTests(numTests=50, wantAck=False, maxFailures=0):
|
|
||||||
"""Run the tests."""
|
|
||||||
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
|
|
||||||
numFail = 0
|
|
||||||
numSuccess = 0
|
|
||||||
for _ in range(numTests):
|
|
||||||
global testNumber
|
|
||||||
testNumber = testNumber + 1
|
|
||||||
isBroadcast = True
|
|
||||||
# asBinary=(i % 2 == 0)
|
|
||||||
success = testSend(
|
|
||||||
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck)
|
|
||||||
if not success:
|
|
||||||
numFail = numFail + 1
|
|
||||||
logging.error(
|
|
||||||
f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)")
|
|
||||||
else:
|
|
||||||
numSuccess = numSuccess + 1
|
|
||||||
logging.info(
|
|
||||||
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far")
|
|
||||||
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
if numFail > maxFailures:
|
|
||||||
logging.error("Too many failures! Test failed!")
|
|
||||||
return False
|
|
||||||
return True</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.subscribe"><code class="name flex">
|
|
||||||
<span>def <span class="ident">subscribe</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Subscribe to the topics the user probably wants to see, prints output to stdout</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def subscribe():
|
|
||||||
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
|
|
||||||
|
|
||||||
pub.subscribe(onNode, "meshtastic.node")</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.testAll"><code class="name flex">
|
|
||||||
<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.
|
|
||||||
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(numTests=5):
|
|
||||||
"""
|
|
||||||
Run a series of tests using devices we can find.
|
|
||||||
This is called from the cli with the "--test" option.
|
|
||||||
|
|
||||||
"""
|
|
||||||
ports = meshtastic.util.findPorts()
|
|
||||||
if len(ports) < 2:
|
|
||||||
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
|
|
||||||
|
|
||||||
pub.subscribe(onConnection, "meshtastic.connection")
|
|
||||||
pub.subscribe(onReceive, "meshtastic.receive")
|
|
||||||
global interfaces
|
|
||||||
interfaces = list(map(lambda port: SerialInterface(
|
|
||||||
port, debugOut=openDebugLog(port), connectNow=True), ports))
|
|
||||||
|
|
||||||
logging.info("Ports opened, starting test")
|
|
||||||
result = testThread(numTests)
|
|
||||||
|
|
||||||
for i in interfaces:
|
|
||||||
i.close()
|
|
||||||
|
|
||||||
return result</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.testSend"><code class="name flex">
|
|
||||||
<span>def <span class="ident">testSend</span></span>(<span>fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Sends one test packet between two nodes and then returns success or failure</p>
|
|
||||||
<h2 id="arguments">Arguments</h2>
|
|
||||||
<p>fromInterface {[type]} – [description]
|
|
||||||
toInterface {[type]} – [description]</p>
|
|
||||||
<h2 id="returns">Returns</h2>
|
|
||||||
<p>boolean – True for success</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False):
|
|
||||||
"""
|
|
||||||
Sends one test packet between two nodes and then returns success or failure
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
fromInterface {[type]} -- [description]
|
|
||||||
toInterface {[type]} -- [description]
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
boolean -- True for success
|
|
||||||
"""
|
|
||||||
global receivedPackets
|
|
||||||
receivedPackets = []
|
|
||||||
fromNode = fromInterface.myInfo.my_node_num
|
|
||||||
|
|
||||||
if isBroadcast:
|
|
||||||
toNode = BROADCAST_NUM
|
|
||||||
else:
|
|
||||||
toNode = toInterface.myInfo.my_node_num
|
|
||||||
|
|
||||||
logging.debug(
|
|
||||||
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
|
|
||||||
global sendingInterface
|
|
||||||
sendingInterface = fromInterface
|
|
||||||
if not asBinary:
|
|
||||||
fromInterface.sendText(f"Test {testNumber}", toNode, wantAck=wantAck)
|
|
||||||
else:
|
|
||||||
fromInterface.sendData((f"Binary {testNumber}").encode(
|
|
||||||
"utf-8"), toNode, wantAck=wantAck)
|
|
||||||
for _ in range(60): # max of 60 secs before we timeout
|
|
||||||
time.sleep(1)
|
|
||||||
if len(receivedPackets) >= 1:
|
|
||||||
return True
|
|
||||||
return False # Failed to send</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.testSimulator"><code class="name flex">
|
|
||||||
<span>def <span class="ident">testSimulator</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Assume that someone has launched meshtastic-native as a simulated node.
|
|
||||||
Talk to that node over TCP, do some operations and if they are successful
|
|
||||||
exit the process with a success code, else exit with a non zero exit code.</p>
|
|
||||||
<p>Run with
|
|
||||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def testSimulator():
|
|
||||||
"""
|
|
||||||
Assume that someone has launched meshtastic-native as a simulated node.
|
|
||||||
Talk to that node over TCP, do some operations and if they are successful
|
|
||||||
exit the process with a success code, else exit with a non zero exit code.
|
|
||||||
|
|
||||||
Run with
|
|
||||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
|
||||||
"""
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
logging.info("Connecting to simulator on localhost!")
|
|
||||||
try:
|
|
||||||
iface = TCPInterface("localhost")
|
|
||||||
iface.showInfo()
|
|
||||||
iface.localNode.showInfo()
|
|
||||||
iface.localNode.exitSimulator()
|
|
||||||
iface.close()
|
|
||||||
logging.info("Integration test successful!")
|
|
||||||
except:
|
|
||||||
print("Error while testing simulator:", sys.exc_info()[0])
|
|
||||||
traceback.print_exc()
|
|
||||||
sys.exit(1)
|
|
||||||
sys.exit(0)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.test.testThread"><code class="name flex">
|
|
||||||
<span>def <span class="ident">testThread</span></span>(<span>numTests=50)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test thread</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def testThread(numTests=50):
|
|
||||||
"""Test thread"""
|
|
||||||
logging.info("Found devices, starting tests...")
|
|
||||||
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>
|
|
||||||
</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-variables">Global variables</a></h3>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.test.interfaces" href="#meshtastic.test.interfaces">interfaces</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
|
||||||
<ul class="two-column">
|
|
||||||
<li><code><a title="meshtastic.test.onConnection" href="#meshtastic.test.onConnection">onConnection</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.onNode" href="#meshtastic.test.onNode">onNode</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.onReceive" href="#meshtastic.test.onReceive">onReceive</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.openDebugLog" href="#meshtastic.test.openDebugLog">openDebugLog</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.runTests" href="#meshtastic.test.runTests">runTests</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.subscribe" href="#meshtastic.test.subscribe">subscribe</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.testAll" href="#meshtastic.test.testAll">testAll</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.testSend" href="#meshtastic.test.testSend">testSend</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.testSimulator" href="#meshtastic.test.testSimulator">testSimulator</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.test.testThread" href="#meshtastic.test.testThread">testThread</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>
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
<!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
|
|
||||||
|
|
||||||
from unittest.mock import MagicMock
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from meshtastic.__main__ import Globals
|
|
||||||
from ..mesh_interface import MeshInterface
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def reset_globals():
|
|
||||||
"""Fixture to reset globals."""
|
|
||||||
parser = None
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
Globals.getInstance().reset()
|
|
||||||
Globals.getInstance().set_parser(parser)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def iface_with_nodes():
|
|
||||||
"""Fixture to setup some nodes."""
|
|
||||||
nodesById = {
|
|
||||||
'!9388f81c': {
|
|
||||||
'num': 2475227164,
|
|
||||||
'user': {
|
|
||||||
'id': '!9388f81c',
|
|
||||||
'longName': 'Unknown f81c',
|
|
||||||
'shortName': '?1C',
|
|
||||||
'macaddr': 'RBeTiPgc',
|
|
||||||
'hwModel': 'TBEAM'
|
|
||||||
},
|
|
||||||
'position': {},
|
|
||||||
'lastHeard': 1640204888
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesByNum = {
|
|
||||||
2475227164: {
|
|
||||||
'num': 2475227164,
|
|
||||||
'user': {
|
|
||||||
'id': '!9388f81c',
|
|
||||||
'longName': 'Unknown f81c',
|
|
||||||
'shortName': '?1C',
|
|
||||||
'macaddr': 'RBeTiPgc',
|
|
||||||
'hwModel': 'TBEAM'
|
|
||||||
},
|
|
||||||
'position': {
|
|
||||||
'time': 1640206266
|
|
||||||
},
|
|
||||||
'lastHeard': 1640206266
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iface = MeshInterface(noProto=True)
|
|
||||||
iface.nodes = nodesById
|
|
||||||
iface.nodesByNum = nodesByNum
|
|
||||||
myInfo = MagicMock()
|
|
||||||
iface.myInfo = myInfo
|
|
||||||
iface.myInfo.my_node_num = 2475227164
|
|
||||||
return iface</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tests.conftest.iface_with_nodes"><code class="name flex">
|
|
||||||
<span>def <span class="ident">iface_with_nodes</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Fixture to setup some nodes.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.fixture
|
|
||||||
def iface_with_nodes():
|
|
||||||
"""Fixture to setup some nodes."""
|
|
||||||
nodesById = {
|
|
||||||
'!9388f81c': {
|
|
||||||
'num': 2475227164,
|
|
||||||
'user': {
|
|
||||||
'id': '!9388f81c',
|
|
||||||
'longName': 'Unknown f81c',
|
|
||||||
'shortName': '?1C',
|
|
||||||
'macaddr': 'RBeTiPgc',
|
|
||||||
'hwModel': 'TBEAM'
|
|
||||||
},
|
|
||||||
'position': {},
|
|
||||||
'lastHeard': 1640204888
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesByNum = {
|
|
||||||
2475227164: {
|
|
||||||
'num': 2475227164,
|
|
||||||
'user': {
|
|
||||||
'id': '!9388f81c',
|
|
||||||
'longName': 'Unknown f81c',
|
|
||||||
'shortName': '?1C',
|
|
||||||
'macaddr': 'RBeTiPgc',
|
|
||||||
'hwModel': 'TBEAM'
|
|
||||||
},
|
|
||||||
'position': {
|
|
||||||
'time': 1640206266
|
|
||||||
},
|
|
||||||
'lastHeard': 1640206266
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iface = MeshInterface(noProto=True)
|
|
||||||
iface.nodes = nodesById
|
|
||||||
iface.nodesByNum = nodesByNum
|
|
||||||
myInfo = MagicMock()
|
|
||||||
iface.myInfo = myInfo
|
|
||||||
iface.myInfo.my_node_num = 2475227164
|
|
||||||
return iface</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<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.iface_with_nodes" href="#meshtastic.tests.conftest.iface_with_nodes">iface_with_nodes</a></code></li>
|
|
||||||
<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>
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<!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_examples" href="test_examples.html">meshtastic.tests.test_examples</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Meshtastic test that the examples run as expected.
|
|
||||||
We assume you have a python virtual environment in current directory.
|
|
||||||
If not, you need to run: …</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_remote_hardware" href="test_remote_hardware.html">meshtastic.tests.test_remote_hardware</a></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Meshtastic unit tests for remote_hardware.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_examples" href="test_examples.html">meshtastic.tests.test_examples</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_remote_hardware" href="test_remote_hardware.html">meshtastic.tests.test_remote_hardware</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>
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
<!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_examples API documentation</title>
|
|
||||||
<meta name="description" content="Meshtastic test that the examples run as expected.
|
|
||||||
We assume you have a python virtual environment in current directory.
|
|
||||||
If not, you need to run: …" />
|
|
||||||
<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_examples</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Meshtastic test that the examples run as expected.
|
|
||||||
We assume you have a python virtual environment in current directory.
|
|
||||||
If not, you need to run: "python3 -m venv venv", "source venv/bin/activate", "pip install ."</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Meshtastic test that the examples run as expected.
|
|
||||||
We assume you have a python virtual environment in current directory.
|
|
||||||
If not, you need to run: "python3 -m venv venv", "source venv/bin/activate", "pip install ."
|
|
||||||
"""
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.mark.examples
|
|
||||||
def test_examples_hello_world_serial_no_arg():
|
|
||||||
"""Test hello_world_serial without any args"""
|
|
||||||
return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py')
|
|
||||||
assert return_value == 3
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.examples
|
|
||||||
def test_examples_hello_world_serial_with_arg(capsys):
|
|
||||||
"""Test hello_world_serial with arg"""
|
|
||||||
return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py hello')
|
|
||||||
assert return_value == 1
|
|
||||||
_, err = capsys.readouterr()
|
|
||||||
assert err == ''
|
|
||||||
# TODO: Why does this not work?
|
|
||||||
# assert out == 'Warning: No Meshtastic devices detected.'</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tests.test_examples.test_examples_hello_world_serial_no_arg"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_examples_hello_world_serial_no_arg</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test hello_world_serial without any args</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.examples
|
|
||||||
def test_examples_hello_world_serial_no_arg():
|
|
||||||
"""Test hello_world_serial without any args"""
|
|
||||||
return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py')
|
|
||||||
assert return_value == 3</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_examples.test_examples_hello_world_serial_with_arg"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_examples_hello_world_serial_with_arg</span></span>(<span>capsys)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test hello_world_serial with arg</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.examples
|
|
||||||
def test_examples_hello_world_serial_with_arg(capsys):
|
|
||||||
"""Test hello_world_serial with arg"""
|
|
||||||
return_value, _ = subprocess.getstatusoutput('source venv/bin/activate; python3 examples/hello_world_serial.py hello')
|
|
||||||
assert return_value == 1
|
|
||||||
_, err = capsys.readouterr()
|
|
||||||
assert err == ''
|
|
||||||
# TODO: Why does this not work?
|
|
||||||
# assert out == 'Warning: No Meshtastic devices detected.'</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_examples.test_examples_hello_world_serial_no_arg" href="#meshtastic.tests.test_examples.test_examples_hello_world_serial_no_arg">test_examples_hello_world_serial_no_arg</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_examples.test_examples_hello_world_serial_with_arg" href="#meshtastic.tests.test_examples.test_examples_hello_world_serial_with_arg">test_examples_hello_world_serial_with_arg</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>
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
<!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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,304 +0,0 @@
|
|||||||
<!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_remote_hardware API documentation</title>
|
|
||||||
<meta name="description" content="Meshtastic unit tests for remote_hardware.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_remote_hardware</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Meshtastic unit tests for remote_hardware.py</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Meshtastic unit tests for remote_hardware.py"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import re
|
|
||||||
|
|
||||||
from unittest.mock import patch, MagicMock
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from ..remote_hardware import RemoteHardwareClient, onGPIOreceive
|
|
||||||
from ..serial_interface import SerialInterface
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_RemoteHardwareClient():
|
|
||||||
"""Test that we can instantiate a RemoteHardwareClient instance"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
assert rhw.iface == iface
|
|
||||||
iface.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_onGPIOreceive(capsys):
|
|
||||||
"""Test onGPIOreceive"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
packet = {'decoded': {'remotehw': {'typ': 'foo', 'gpioValue': '4096' }}}
|
|
||||||
onGPIOreceive(packet, iface)
|
|
||||||
out, err = capsys.readouterr()
|
|
||||||
assert re.search(r'Received RemoteHardware', out)
|
|
||||||
assert err == ''
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_RemoteHardwareClient_no_gpio_channel(capsys):
|
|
||||||
"""Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
|
||||||
mo.localNode.getChannelByName.return_value = None
|
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
|
||||||
RemoteHardwareClient(mo)
|
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
|
||||||
assert pytest_wrapped_e.value.code == 1
|
|
||||||
out, err = capsys.readouterr()
|
|
||||||
assert re.search(r'Warning: No channel named', out)
|
|
||||||
assert err == ""
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_readGPIOs(caplog):
|
|
||||||
"""Test readGPIOs"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
rhw.readGPIOs('0x10', 123)
|
|
||||||
assert re.search(r'readGPIOs', caplog.text, re.MULTILINE)
|
|
||||||
iface.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_writeGPIOs(caplog):
|
|
||||||
"""Test writeGPIOs"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
rhw.writeGPIOs('0x10', 123, 1)
|
|
||||||
assert re.search(r'writeGPIOs', caplog.text, re.MULTILINE)
|
|
||||||
iface.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_watchGPIOs(caplog):
|
|
||||||
"""Test watchGPIOs"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
rhw.watchGPIOs('0x10', 123)
|
|
||||||
assert re.search(r'watchGPIOs', caplog.text, re.MULTILINE)
|
|
||||||
iface.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_sendHardware_no_nodeid():
|
|
||||||
"""Test sending no nodeid to _sendHardware()"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
|
||||||
rhw = RemoteHardwareClient(mo)
|
|
||||||
rhw._sendHardware(None, None)
|
|
||||||
assert pytest_wrapped_e.type == SystemExit</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_RemoteHardwareClient"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_RemoteHardwareClient</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test that we can instantiate a RemoteHardwareClient instance</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_RemoteHardwareClient():
|
|
||||||
"""Test that we can instantiate a RemoteHardwareClient instance"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
assert rhw.iface == iface
|
|
||||||
iface.close()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_RemoteHardwareClient_no_gpio_channel"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_RemoteHardwareClient_no_gpio_channel</span></span>(<span>capsys)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_RemoteHardwareClient_no_gpio_channel(capsys):
|
|
||||||
"""Test that we can instantiate a RemoteHardwareClient instance but there is no channel named channel 'gpio'"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
|
||||||
mo.localNode.getChannelByName.return_value = None
|
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
|
||||||
RemoteHardwareClient(mo)
|
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
|
||||||
assert pytest_wrapped_e.value.code == 1
|
|
||||||
out, err = capsys.readouterr()
|
|
||||||
assert re.search(r'Warning: No channel named', out)
|
|
||||||
assert err == ""</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_onGPIOreceive"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_onGPIOreceive</span></span>(<span>capsys)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test onGPIOreceive</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_onGPIOreceive(capsys):
|
|
||||||
"""Test onGPIOreceive"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
packet = {'decoded': {'remotehw': {'typ': 'foo', 'gpioValue': '4096' }}}
|
|
||||||
onGPIOreceive(packet, iface)
|
|
||||||
out, err = capsys.readouterr()
|
|
||||||
assert re.search(r'Received RemoteHardware', out)
|
|
||||||
assert err == ''</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_readGPIOs"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_readGPIOs</span></span>(<span>caplog)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test readGPIOs</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_readGPIOs(caplog):
|
|
||||||
"""Test readGPIOs"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
rhw.readGPIOs('0x10', 123)
|
|
||||||
assert re.search(r'readGPIOs', caplog.text, re.MULTILINE)
|
|
||||||
iface.close()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_sendHardware_no_nodeid"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_sendHardware_no_nodeid</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test sending no nodeid to _sendHardware()</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_sendHardware_no_nodeid():
|
|
||||||
"""Test sending no nodeid to _sendHardware()"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
|
||||||
rhw = RemoteHardwareClient(mo)
|
|
||||||
rhw._sendHardware(None, None)
|
|
||||||
assert pytest_wrapped_e.type == SystemExit</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_watchGPIOs"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_watchGPIOs</span></span>(<span>caplog)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test watchGPIOs</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_watchGPIOs(caplog):
|
|
||||||
"""Test watchGPIOs"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
rhw.watchGPIOs('0x10', 123)
|
|
||||||
assert re.search(r'watchGPIOs', caplog.text, re.MULTILINE)
|
|
||||||
iface.close()</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_remote_hardware.test_writeGPIOs"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_writeGPIOs</span></span>(<span>caplog)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test writeGPIOs</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_writeGPIOs(caplog):
|
|
||||||
"""Test writeGPIOs"""
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
|
||||||
rhw = RemoteHardwareClient(iface)
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
rhw.writeGPIOs('0x10', 123, 1)
|
|
||||||
assert re.search(r'writeGPIOs', caplog.text, re.MULTILINE)
|
|
||||||
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_remote_hardware.test_RemoteHardwareClient" href="#meshtastic.tests.test_remote_hardware.test_RemoteHardwareClient">test_RemoteHardwareClient</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_remote_hardware.test_RemoteHardwareClient_no_gpio_channel" href="#meshtastic.tests.test_remote_hardware.test_RemoteHardwareClient_no_gpio_channel">test_RemoteHardwareClient_no_gpio_channel</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_remote_hardware.test_onGPIOreceive" href="#meshtastic.tests.test_remote_hardware.test_onGPIOreceive">test_onGPIOreceive</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_remote_hardware.test_readGPIOs" href="#meshtastic.tests.test_remote_hardware.test_readGPIOs">test_readGPIOs</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_remote_hardware.test_sendHardware_no_nodeid" href="#meshtastic.tests.test_remote_hardware.test_sendHardware_no_nodeid">test_sendHardware_no_nodeid</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_remote_hardware.test_watchGPIOs" href="#meshtastic.tests.test_remote_hardware.test_watchGPIOs">test_watchGPIOs</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_remote_hardware.test_writeGPIOs" href="#meshtastic.tests.test_remote_hardware.test_writeGPIOs">test_writeGPIOs</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>
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
<!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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,127 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
<!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 logging
|
|
||||||
import re
|
|
||||||
|
|
||||||
from unittest.mock import MagicMock
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from ..stream_interface import StreamInterface
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_StreamInterface():
|
|
||||||
"""Test that we cannot instantiate a StreamInterface based on noProto"""
|
|
||||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
|
||||||
StreamInterface()
|
|
||||||
assert pytest_wrapped_e.type == Exception
|
|
||||||
|
|
||||||
|
|
||||||
# Note: This takes a bit, so moving from unit to slow
|
|
||||||
@pytest.mark.unitslow
|
|
||||||
def test_StreamInterface_with_noProto(caplog, reset_globals):
|
|
||||||
"""Test that we can instantiate a StreamInterface based on nonProto
|
|
||||||
and we can read/write bytes from a mocked stream
|
|
||||||
"""
|
|
||||||
stream = MagicMock()
|
|
||||||
test_data = b'hello'
|
|
||||||
stream.read.return_value = test_data
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
iface = StreamInterface(noProto=True, connectNow=False)
|
|
||||||
iface.stream = stream
|
|
||||||
iface._writeBytes(test_data)
|
|
||||||
data = iface._readBytes(len(test_data))
|
|
||||||
assert data == test_data
|
|
||||||
|
|
||||||
|
|
||||||
# Note: This takes a bit, so moving from unit to slow
|
|
||||||
# Tip: If you want to see the print output, run with '-s' flag:
|
|
||||||
# pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
|
||||||
@pytest.mark.unitslow
|
|
||||||
def test_sendToRadioImpl(caplog, reset_globals):
|
|
||||||
"""Test _sendToRadioImpl()"""
|
|
||||||
|
|
||||||
# def add_header(b):
|
|
||||||
# """Add header stuffs for radio"""
|
|
||||||
# bufLen = len(b)
|
|
||||||
# header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
|
||||||
# return header + b
|
|
||||||
|
|
||||||
# captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
|
|
||||||
raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
|
|
||||||
raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
|
|
||||||
raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
raw_blank = b''
|
|
||||||
|
|
||||||
test_data = b'hello'
|
|
||||||
stream = MagicMock()
|
|
||||||
#stream.read.return_value = add_header(test_data)
|
|
||||||
stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
|
|
||||||
raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
|
|
||||||
raw_blank, raw_blank]
|
|
||||||
toRadio = MagicMock()
|
|
||||||
toRadio.SerializeToString.return_value = test_data
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
iface = StreamInterface(noProto=True, connectNow=False)
|
|
||||||
iface.stream = stream
|
|
||||||
iface.connect()
|
|
||||||
iface._sendToRadioImpl(toRadio)
|
|
||||||
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
|
||||||
assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
|
||||||
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
|
||||||
print(caplog.text)</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 cannot instantiate a StreamInterface based on noProto</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 cannot instantiate a StreamInterface based on noProto"""
|
|
||||||
with pytest.raises(Exception) as pytest_wrapped_e:
|
|
||||||
StreamInterface()
|
|
||||||
assert pytest_wrapped_e.type == Exception</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_stream_interface.test_StreamInterface_with_noProto"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_StreamInterface_with_noProto</span></span>(<span>caplog, reset_globals)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test that we can instantiate a StreamInterface based on nonProto
|
|
||||||
and we can read/write bytes from a mocked stream</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unitslow
|
|
||||||
def test_StreamInterface_with_noProto(caplog, reset_globals):
|
|
||||||
"""Test that we can instantiate a StreamInterface based on nonProto
|
|
||||||
and we can read/write bytes from a mocked stream
|
|
||||||
"""
|
|
||||||
stream = MagicMock()
|
|
||||||
test_data = b'hello'
|
|
||||||
stream.read.return_value = test_data
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
iface = StreamInterface(noProto=True, connectNow=False)
|
|
||||||
iface.stream = stream
|
|
||||||
iface._writeBytes(test_data)
|
|
||||||
data = iface._readBytes(len(test_data))
|
|
||||||
assert data == test_data</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tests.test_stream_interface.test_sendToRadioImpl"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_sendToRadioImpl</span></span>(<span>caplog, reset_globals)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test _sendToRadioImpl()</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unitslow
|
|
||||||
def test_sendToRadioImpl(caplog, reset_globals):
|
|
||||||
"""Test _sendToRadioImpl()"""
|
|
||||||
|
|
||||||
# def add_header(b):
|
|
||||||
# """Add header stuffs for radio"""
|
|
||||||
# bufLen = len(b)
|
|
||||||
# header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
|
||||||
# return header + b
|
|
||||||
|
|
||||||
# captured raw bytes of a Heltec2.1 radio with 2 channels (primary and a secondary channel named "gpio")
|
|
||||||
raw_1_my_info = b'\x1a,\x08\xdc\x8c\xd5\xc5\x02\x18\r2\x0e1.2.49.5354c49P\x15]\xe1%\x17Eh\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
|
|
||||||
raw_2_node_info = b'"9\x08\xdc\x8c\xd5\xc5\x02\x12(\n\t!28b5465c\x12\x0cUnknown 465c\x1a\x03?5C"\x06$o(\xb5F\\0\n\x1a\x02 1%M<\xc6a'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_3_node_info = b'"C\x08\xa4\x8c\xd5\xc5\x02\x12(\n\t!28b54624\x12\x0cUnknown 4624\x1a\x03?24"\x06$o(\xb5F$0\n\x1a\x07 5MH<\xc6a%G<\xc6a=\x00\x00\xc0@'
|
|
||||||
raw_4_complete = b'@\xcf\xe5\xd1\x8c\x0e'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_5_prefs = b'Z6\r\\F\xb5(\x15\\F\xb5("\x1c\x08\x06\x12\x13*\x11\n\x0f0\x84\x07P\xac\x02\x88\x01\x01\xb0\t#\xb8\t\x015]$\xddk5\xd5\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_6_channel0 = b'Z.\r\\F\xb5(\x15\\F\xb5("\x14\x08\x06\x12\x0b:\t\x12\x05\x18\x01"\x01\x01\x18\x015^$\xddk5\xd6\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
# pylint: disable=C0301
|
|
||||||
raw_7_channel1 = b'ZS\r\\F\xb5(\x15\\F\xb5("9\x08\x06\x120:.\x08\x01\x12(" \xb4&\xb3\xc7\x06\xd8\xe39%\xba\xa5\xee\x8eH\x06\xf6\xf4H\xe8\xd5\xc1[ao\xb5Y\\\xb4"\xafmi*\x04gpio\x18\x025_$\xddk5\xd7\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
raw_8_channel2 = b'Z)\r\\F\xb5(\x15\\F\xb5("\x0f\x08\x06\x12\x06:\x04\x08\x02\x12\x005`$\xddk5\xd8\x7f!b=M<\xc6aP\x03`F'
|
|
||||||
raw_blank = b''
|
|
||||||
|
|
||||||
test_data = b'hello'
|
|
||||||
stream = MagicMock()
|
|
||||||
#stream.read.return_value = add_header(test_data)
|
|
||||||
stream.read.side_effect = [ raw_1_my_info, raw_2_node_info, raw_3_node_info, raw_4_complete,
|
|
||||||
raw_5_prefs, raw_6_channel0, raw_7_channel1, raw_8_channel2,
|
|
||||||
raw_blank, raw_blank]
|
|
||||||
toRadio = MagicMock()
|
|
||||||
toRadio.SerializeToString.return_value = test_data
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
iface = StreamInterface(noProto=True, connectNow=False)
|
|
||||||
iface.stream = stream
|
|
||||||
iface.connect()
|
|
||||||
iface._sendToRadioImpl(toRadio)
|
|
||||||
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
|
||||||
assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
|
||||||
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
|
||||||
print(caplog.text)</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>
|
|
||||||
<li><code><a title="meshtastic.tests.test_stream_interface.test_StreamInterface_with_noProto" href="#meshtastic.tests.test_stream_interface.test_StreamInterface_with_noProto">test_StreamInterface_with_noProto</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tests.test_stream_interface.test_sendToRadioImpl" href="#meshtastic.tests.test_stream_interface.test_sendToRadioImpl">test_sendToRadioImpl</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>
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,519 +0,0 @@
|
|||||||
<!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 logging
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
|
|
||||||
support_info, genPSK256, fromStr, fromPSK,
|
|
||||||
quoteBooleans, catchAndIgnore)
|
|
||||||
|
|
||||||
|
|
||||||
@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_quoteBooleans():
|
|
||||||
"""Test quoteBooleans"""
|
|
||||||
assert quoteBooleans('') == ''
|
|
||||||
assert quoteBooleans('foo') == 'foo'
|
|
||||||
assert quoteBooleans('true') == 'true'
|
|
||||||
assert quoteBooleans('false') == 'false'
|
|
||||||
assert quoteBooleans(': true') == ": 'true'"
|
|
||||||
assert quoteBooleans(': false') == ": 'false'"
|
|
||||||
|
|
||||||
@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 == ''
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
|
||||||
def test_catchAndIgnore(caplog):
|
|
||||||
"""Test catchAndIgnore() does not actually throw an exception, but just logs"""
|
|
||||||
def some_closure():
|
|
||||||
raise Exception('foo')
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
catchAndIgnore("something", some_closure)
|
|
||||||
assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)</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_catchAndIgnore"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_catchAndIgnore</span></span>(<span>caplog)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test catchAndIgnore() does not actually throw an exception, but just logs</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_catchAndIgnore(caplog):
|
|
||||||
"""Test catchAndIgnore() does not actually throw an exception, but just logs"""
|
|
||||||
def some_closure():
|
|
||||||
raise Exception('foo')
|
|
||||||
with caplog.at_level(logging.DEBUG):
|
|
||||||
catchAndIgnore("something", some_closure)
|
|
||||||
assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<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_quoteBooleans"><code class="name flex">
|
|
||||||
<span>def <span class="ident">test_quoteBooleans</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Test quoteBooleans</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">@pytest.mark.unit
|
|
||||||
def test_quoteBooleans():
|
|
||||||
"""Test quoteBooleans"""
|
|
||||||
assert quoteBooleans('') == ''
|
|
||||||
assert quoteBooleans('foo') == 'foo'
|
|
||||||
assert quoteBooleans('true') == 'true'
|
|
||||||
assert quoteBooleans('false') == 'false'
|
|
||||||
assert quoteBooleans(': true') == ": 'true'"
|
|
||||||
assert quoteBooleans(': false') == ": 'false'"</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_catchAndIgnore" href="#meshtastic.tests.test_util.test_catchAndIgnore">test_catchAndIgnore</a></code></li>
|
|
||||||
<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_quoteBooleans" href="#meshtastic.tests.test_util.test_quoteBooleans">test_quoteBooleans</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>
|
|
||||||
@@ -1,615 +0,0 @@
|
|||||||
<!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.tunnel API documentation</title>
|
|
||||||
<meta name="description" content="Code for IP tunnel over a mesh …" />
|
|
||||||
<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.tunnel</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Code for IP tunnel over a mesh</p>
|
|
||||||
<h1 id="note-python-pytuntap-was-too-buggy">Note python-pytuntap was too buggy</h1>
|
|
||||||
<h1 id="using-pip3-install-pytap2">using pip3 install pytap2</h1>
|
|
||||||
<h1 id="make-sure-to-sudo-setcap-cap_net_admineip-usrbinpython38-so-python-can-access-tun-device-without-being-root">make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root</h1>
|
|
||||||
<h1 id="sudo-ip-tuntap-del-mode-tun-tun0">sudo ip tuntap del mode tun tun0</h1>
|
|
||||||
<h1 id="sudo-binrunsh-port-devttyusb0-setch-shortfast">sudo bin/run.sh –port /dev/ttyUSB0 –setch-shortfast</h1>
|
|
||||||
<h1 id="sudo-binrunsh-port-devttyusb0-tunnel-debug">sudo bin/run.sh –port /dev/ttyUSB0 –tunnel –debug</h1>
|
|
||||||
<h1 id="ssh-y-root19216810151-or-dietpi-default-password-p">ssh -Y root@192.168.10.151 (or dietpi), default password p</h1>
|
|
||||||
<h1 id="ncat-e-bincat-k-u-l-1235">ncat -e /bin/cat -k -u -l 1235</h1>
|
|
||||||
<h1 id="ncat-u-1011564152-1235">ncat -u 10.115.64.152 1235</h1>
|
|
||||||
<h1 id="ping-c-1-w-20-1011564152">ping -c 1 -W 20 10.115.64.152</h1>
|
|
||||||
<h1 id="ping-i-30-w-30-1011564152">ping -i 30 -W 30 10.115.64.152</h1>
|
|
||||||
<h1 id="fixme-use-a-more-optimal-mtu">FIXME: use a more optimal MTU</h1>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Code for IP tunnel over a mesh
|
|
||||||
|
|
||||||
# Note python-pytuntap was too buggy
|
|
||||||
# using pip3 install pytap2
|
|
||||||
# make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root
|
|
||||||
# sudo ip tuntap del mode tun tun0
|
|
||||||
# sudo bin/run.sh --port /dev/ttyUSB0 --setch-shortfast
|
|
||||||
# sudo bin/run.sh --port /dev/ttyUSB0 --tunnel --debug
|
|
||||||
# ssh -Y root@192.168.10.151 (or dietpi), default password p
|
|
||||||
# ncat -e /bin/cat -k -u -l 1235
|
|
||||||
# ncat -u 10.115.64.152 1235
|
|
||||||
# ping -c 1 -W 20 10.115.64.152
|
|
||||||
# ping -i 30 -W 30 10.115.64.152
|
|
||||||
|
|
||||||
# FIXME: use a more optimal MTU
|
|
||||||
"""
|
|
||||||
|
|
||||||
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
|
|
||||||
LOG_TRACE = 5
|
|
||||||
|
|
||||||
# fixme - find a way to move onTunnelReceive inside of the class
|
|
||||||
tunnelInstance = None
|
|
||||||
|
|
||||||
"""A list of chatty UDP services we should never accidentally
|
|
||||||
forward to our slow network"""
|
|
||||||
udpBlacklist = {
|
|
||||||
1900, # SSDP
|
|
||||||
5353, # multicast DNS
|
|
||||||
}
|
|
||||||
|
|
||||||
"""A list of TCP services to block"""
|
|
||||||
tcpBlacklist = {}
|
|
||||||
|
|
||||||
"""A list of protocols we ignore"""
|
|
||||||
protocolBlacklist = {
|
|
||||||
0x02, # IGMP
|
|
||||||
0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def hexstr(barray):
|
|
||||||
"""Print a string of hex digits"""
|
|
||||||
return ":".join('{:02x}'.format(x) for x in barray)
|
|
||||||
|
|
||||||
|
|
||||||
def ipstr(barray):
|
|
||||||
"""Print a string of ip digits"""
|
|
||||||
return ".".join('{}'.format(x) for x in barray)
|
|
||||||
|
|
||||||
|
|
||||||
def readnet_u16(p, offset):
|
|
||||||
"""Read big endian u16 (network byte order)"""
|
|
||||||
return p[offset] * 256 + p[offset + 1]
|
|
||||||
|
|
||||||
|
|
||||||
def onTunnelReceive(packet, interface):
|
|
||||||
"""Callback for received tunneled messages from mesh
|
|
||||||
|
|
||||||
FIXME figure out how to do closures with methods in python"""
|
|
||||||
tunnelInstance.onReceive(packet)
|
|
||||||
|
|
||||||
|
|
||||||
class Tunnel:
|
|
||||||
"""A TUN based IP tunnel over meshtastic"""
|
|
||||||
|
|
||||||
def __init__(self, iface, subnet=None, netmask="255.255.0.0"):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
|
|
||||||
iface is the already open MeshInterface instance
|
|
||||||
subnet is used to construct our network number (normally 10.115.x.x)
|
|
||||||
"""
|
|
||||||
|
|
||||||
if subnet is None:
|
|
||||||
subnet = "10.115"
|
|
||||||
|
|
||||||
self.iface = iface
|
|
||||||
self.subnetPrefix = subnet
|
|
||||||
|
|
||||||
global tunnelInstance
|
|
||||||
tunnelInstance = self
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
for node in self.iface.nodes.values():
|
|
||||||
nodeId = node["user"]["id"]
|
|
||||||
ip = self._nodeNumToIp(node["num"])
|
|
||||||
logging.info(f"Node { nodeId } has IP address { ip }")
|
|
||||||
|
|
||||||
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
|
|
||||||
self.tun = TapDevice(name="mesh")
|
|
||||||
self.tun.up()
|
|
||||||
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
|
|
||||||
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
|
||||||
self._rxThread = threading.Thread(
|
|
||||||
target=self.__tunReader, args=(), daemon=True)
|
|
||||||
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
|
|
||||||
if not self._shouldFilterPacket(p):
|
|
||||||
self.tun.write(p)
|
|
||||||
|
|
||||||
def _shouldFilterPacket(self, p):
|
|
||||||
"""Given a packet, decode it and return true if it should be ignored"""
|
|
||||||
protocol = p[8 + 1]
|
|
||||||
srcaddr = p[12:16]
|
|
||||||
destAddr = p[16:20]
|
|
||||||
subheader = 20
|
|
||||||
ignore = False # Assume we will be forwarding the packet
|
|
||||||
if protocol in protocolBlacklist:
|
|
||||||
ignore = True
|
|
||||||
logging.log(
|
|
||||||
LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
|
||||||
elif protocol == 0x01: # ICMP
|
|
||||||
icmpType = p[20]
|
|
||||||
icmpCode = p[21]
|
|
||||||
checksum = p[22:24]
|
|
||||||
# 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)
|
|
||||||
elif protocol == 0x11: # UDP
|
|
||||||
srcport = readnet_u16(p, subheader)
|
|
||||||
destport = readnet_u16(p, subheader + 2)
|
|
||||||
if destport in udpBlacklist:
|
|
||||||
ignore = True
|
|
||||||
logging.log(
|
|
||||||
LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
|
|
||||||
else:
|
|
||||||
logging.debug(
|
|
||||||
f"forwarding udp srcport={srcport}, destport={destport}")
|
|
||||||
elif protocol == 0x06: # TCP
|
|
||||||
srcport = readnet_u16(p, subheader)
|
|
||||||
destport = readnet_u16(p, subheader + 2)
|
|
||||||
if destport in tcpBlacklist:
|
|
||||||
ignore = True
|
|
||||||
logging.log(LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
|
||||||
else:
|
|
||||||
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)}")
|
|
||||||
|
|
||||||
return ignore
|
|
||||||
|
|
||||||
def __tunReader(self):
|
|
||||||
tap = self.tun
|
|
||||||
logging.debug("TUN reader running")
|
|
||||||
while True:
|
|
||||||
p = tap.read()
|
|
||||||
#logging.debug(f"IP packet received on TUN interface, type={type(p)}")
|
|
||||||
destAddr = p[16:20]
|
|
||||||
|
|
||||||
if not self._shouldFilterPacket(p):
|
|
||||||
self.sendPacket(destAddr, p)
|
|
||||||
|
|
||||||
def _ipToNodeId(self, ipAddr):
|
|
||||||
# We only consider the last 16 bits of the nodenum for IP address matching
|
|
||||||
ipBits = ipAddr[2] * 256 + ipAddr[3]
|
|
||||||
|
|
||||||
if ipBits == 0xffff:
|
|
||||||
return "^all"
|
|
||||||
|
|
||||||
for node in self.iface.nodes.values():
|
|
||||||
nodeNum = node["num"] & 0xffff
|
|
||||||
# logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
|
|
||||||
if (nodeNum) == ipBits:
|
|
||||||
return node["user"]["id"]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _nodeNumToIp(self, nodeNum):
|
|
||||||
return f"{self.subnetPrefix}.{(nodeNum >> 8) & 0xff}.{nodeNum & 0xff}"
|
|
||||||
|
|
||||||
def sendPacket(self, destAddr, p):
|
|
||||||
"""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}")
|
|
||||||
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)}")
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""Close"""
|
|
||||||
self.tun.close()</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-variables">Global variables</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tunnel.tcpBlacklist"><code class="name">var <span class="ident">tcpBlacklist</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A list of protocols we ignore</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tunnel.tunnelInstance"><code class="name">var <span class="ident">tunnelInstance</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A list of chatty UDP services we should never accidentally
|
|
||||||
forward to our slow network</p></div>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tunnel.udpBlacklist"><code class="name">var <span class="ident">udpBlacklist</span></code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A list of TCP services to block</p></div>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tunnel.hexstr"><code class="name flex">
|
|
||||||
<span>def <span class="ident">hexstr</span></span>(<span>barray)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Print a string of hex digits</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def hexstr(barray):
|
|
||||||
"""Print a string of hex digits"""
|
|
||||||
return ":".join('{:02x}'.format(x) for x in barray)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tunnel.ipstr"><code class="name flex">
|
|
||||||
<span>def <span class="ident">ipstr</span></span>(<span>barray)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Print a string of ip digits</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def ipstr(barray):
|
|
||||||
"""Print a string of ip digits"""
|
|
||||||
return ".".join('{}'.format(x) for x in barray)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tunnel.onTunnelReceive"><code class="name flex">
|
|
||||||
<span>def <span class="ident">onTunnelReceive</span></span>(<span>packet, interface)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Callback for received tunneled messages from mesh</p>
|
|
||||||
<p>FIXME figure out how to do closures with methods in python</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def onTunnelReceive(packet, interface):
|
|
||||||
"""Callback for received tunneled messages from mesh
|
|
||||||
|
|
||||||
FIXME figure out how to do closures with methods in python"""
|
|
||||||
tunnelInstance.onReceive(packet)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tunnel.readnet_u16"><code class="name flex">
|
|
||||||
<span>def <span class="ident">readnet_u16</span></span>(<span>p, offset)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Read big endian u16 (network byte order)</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def readnet_u16(p, offset):
|
|
||||||
"""Read big endian u16 (network byte order)"""
|
|
||||||
return p[offset] * 256 + p[offset + 1]</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tunnel.Tunnel"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">Tunnel</span></span>
|
|
||||||
<span>(</span><span>iface, subnet=None, netmask='255.255.0.0')</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A TUN based IP tunnel over meshtastic</p>
|
|
||||||
<p>Constructor</p>
|
|
||||||
<p>iface is the already open MeshInterface instance
|
|
||||||
subnet is used to construct our network number (normally 10.115.x.x)</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class Tunnel:
|
|
||||||
"""A TUN based IP tunnel over meshtastic"""
|
|
||||||
|
|
||||||
def __init__(self, iface, subnet=None, netmask="255.255.0.0"):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
|
|
||||||
iface is the already open MeshInterface instance
|
|
||||||
subnet is used to construct our network number (normally 10.115.x.x)
|
|
||||||
"""
|
|
||||||
|
|
||||||
if subnet is None:
|
|
||||||
subnet = "10.115"
|
|
||||||
|
|
||||||
self.iface = iface
|
|
||||||
self.subnetPrefix = subnet
|
|
||||||
|
|
||||||
global tunnelInstance
|
|
||||||
tunnelInstance = self
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
for node in self.iface.nodes.values():
|
|
||||||
nodeId = node["user"]["id"]
|
|
||||||
ip = self._nodeNumToIp(node["num"])
|
|
||||||
logging.info(f"Node { nodeId } has IP address { ip }")
|
|
||||||
|
|
||||||
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
|
|
||||||
self.tun = TapDevice(name="mesh")
|
|
||||||
self.tun.up()
|
|
||||||
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
|
|
||||||
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
|
||||||
self._rxThread = threading.Thread(
|
|
||||||
target=self.__tunReader, args=(), daemon=True)
|
|
||||||
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
|
|
||||||
if not self._shouldFilterPacket(p):
|
|
||||||
self.tun.write(p)
|
|
||||||
|
|
||||||
def _shouldFilterPacket(self, p):
|
|
||||||
"""Given a packet, decode it and return true if it should be ignored"""
|
|
||||||
protocol = p[8 + 1]
|
|
||||||
srcaddr = p[12:16]
|
|
||||||
destAddr = p[16:20]
|
|
||||||
subheader = 20
|
|
||||||
ignore = False # Assume we will be forwarding the packet
|
|
||||||
if protocol in protocolBlacklist:
|
|
||||||
ignore = True
|
|
||||||
logging.log(
|
|
||||||
LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
|
||||||
elif protocol == 0x01: # ICMP
|
|
||||||
icmpType = p[20]
|
|
||||||
icmpCode = p[21]
|
|
||||||
checksum = p[22:24]
|
|
||||||
# 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)
|
|
||||||
elif protocol == 0x11: # UDP
|
|
||||||
srcport = readnet_u16(p, subheader)
|
|
||||||
destport = readnet_u16(p, subheader + 2)
|
|
||||||
if destport in udpBlacklist:
|
|
||||||
ignore = True
|
|
||||||
logging.log(
|
|
||||||
LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
|
|
||||||
else:
|
|
||||||
logging.debug(
|
|
||||||
f"forwarding udp srcport={srcport}, destport={destport}")
|
|
||||||
elif protocol == 0x06: # TCP
|
|
||||||
srcport = readnet_u16(p, subheader)
|
|
||||||
destport = readnet_u16(p, subheader + 2)
|
|
||||||
if destport in tcpBlacklist:
|
|
||||||
ignore = True
|
|
||||||
logging.log(LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
|
||||||
else:
|
|
||||||
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)}")
|
|
||||||
|
|
||||||
return ignore
|
|
||||||
|
|
||||||
def __tunReader(self):
|
|
||||||
tap = self.tun
|
|
||||||
logging.debug("TUN reader running")
|
|
||||||
while True:
|
|
||||||
p = tap.read()
|
|
||||||
#logging.debug(f"IP packet received on TUN interface, type={type(p)}")
|
|
||||||
destAddr = p[16:20]
|
|
||||||
|
|
||||||
if not self._shouldFilterPacket(p):
|
|
||||||
self.sendPacket(destAddr, p)
|
|
||||||
|
|
||||||
def _ipToNodeId(self, ipAddr):
|
|
||||||
# We only consider the last 16 bits of the nodenum for IP address matching
|
|
||||||
ipBits = ipAddr[2] * 256 + ipAddr[3]
|
|
||||||
|
|
||||||
if ipBits == 0xffff:
|
|
||||||
return "^all"
|
|
||||||
|
|
||||||
for node in self.iface.nodes.values():
|
|
||||||
nodeNum = node["num"] & 0xffff
|
|
||||||
# logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
|
|
||||||
if (nodeNum) == ipBits:
|
|
||||||
return node["user"]["id"]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _nodeNumToIp(self, nodeNum):
|
|
||||||
return f"{self.subnetPrefix}.{(nodeNum >> 8) & 0xff}.{nodeNum & 0xff}"
|
|
||||||
|
|
||||||
def sendPacket(self, destAddr, p):
|
|
||||||
"""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}")
|
|
||||||
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)}")
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""Close"""
|
|
||||||
self.tun.close()</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.tunnel.Tunnel.close"><code class="name flex">
|
|
||||||
<span>def <span class="ident">close</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<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>
|
|
||||||
<dt id="meshtastic.tunnel.Tunnel.onReceive"><code class="name flex">
|
|
||||||
<span>def <span class="ident">onReceive</span></span>(<span>self, packet)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<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
|
|
||||||
if not self._shouldFilterPacket(p):
|
|
||||||
self.tun.write(p)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.tunnel.Tunnel.sendPacket"><code class="name flex">
|
|
||||||
<span>def <span class="ident">sendPacket</span></span>(<span>self, destAddr, p)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Forward the provided IP packet into the mesh</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def sendPacket(self, destAddr, p):
|
|
||||||
"""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}")
|
|
||||||
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>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
<nav id="sidebar">
|
|
||||||
<h1>Index</h1>
|
|
||||||
<div class="toc">
|
|
||||||
<ul>
|
|
||||||
<li><a href="#note-python-pytuntap-was-too-buggy">Note python-pytuntap was too buggy</a></li>
|
|
||||||
<li><a href="#using-pip3-install-pytap2">using pip3 install pytap2</a></li>
|
|
||||||
<li><a href="#make-sure-to-sudo-setcap-cap_net_admineip-usrbinpython38-so-python-can-access-tun-device-without-being-root">make sure to "sudo setcap cap_net_admin+eip /usr/bin/python3.8" so python can access tun device without being root</a></li>
|
|
||||||
<li><a href="#sudo-ip-tuntap-del-mode-tun-tun0">sudo ip tuntap del mode tun tun0</a></li>
|
|
||||||
<li><a href="#sudo-binrunsh-port-devttyusb0-setch-shortfast">sudo bin/run.sh –port /dev/ttyUSB0 –setch-shortfast</a></li>
|
|
||||||
<li><a href="#sudo-binrunsh-port-devttyusb0-tunnel-debug">sudo bin/run.sh –port /dev/ttyUSB0 –tunnel –debug</a></li>
|
|
||||||
<li><a href="#ssh-y-root19216810151-or-dietpi-default-password-p">ssh -Y root@192.168.10.151 (or dietpi), default password p</a></li>
|
|
||||||
<li><a href="#ncat-e-bincat-k-u-l-1235">ncat -e /bin/cat -k -u -l 1235</a></li>
|
|
||||||
<li><a href="#ncat-u-1011564152-1235">ncat -u 10.115.64.152 1235</a></li>
|
|
||||||
<li><a href="#ping-c-1-w-20-1011564152">ping -c 1 -W 20 10.115.64.152</a></li>
|
|
||||||
<li><a href="#ping-i-30-w-30-1011564152">ping -i 30 -W 30 10.115.64.152</a></li>
|
|
||||||
<li><a href="#fixme-use-a-more-optimal-mtu">FIXME: use a more optimal MTU</a></li>
|
|
||||||
</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-variables">Global variables</a></h3>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.tunnel.tcpBlacklist" href="#meshtastic.tunnel.tcpBlacklist">tcpBlacklist</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.tunnelInstance" href="#meshtastic.tunnel.tunnelInstance">tunnelInstance</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.udpBlacklist" href="#meshtastic.tunnel.udpBlacklist">udpBlacklist</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.tunnel.hexstr" href="#meshtastic.tunnel.hexstr">hexstr</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.ipstr" href="#meshtastic.tunnel.ipstr">ipstr</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.onTunnelReceive" href="#meshtastic.tunnel.onTunnelReceive">onTunnelReceive</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.readnet_u16" href="#meshtastic.tunnel.readnet_u16">readnet_u16</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.tunnel.Tunnel" href="#meshtastic.tunnel.Tunnel">Tunnel</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.tunnel.Tunnel.close" href="#meshtastic.tunnel.Tunnel.close">close</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.Tunnel.onReceive" href="#meshtastic.tunnel.Tunnel.onReceive">onReceive</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.tunnel.Tunnel.sendPacket" href="#meshtastic.tunnel.Tunnel.sendPacket">sendPacket</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>
|
|
||||||
@@ -1,688 +0,0 @@
|
|||||||
<!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.util API documentation</title>
|
|
||||||
<meta name="description" content="Utility functions." />
|
|
||||||
<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.util</code></h1>
|
|
||||||
</header>
|
|
||||||
<section id="section-intro">
|
|
||||||
<p>Utility functions.</p>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">"""Utility functions.
|
|
||||||
"""
|
|
||||||
import traceback
|
|
||||||
from queue import Queue
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import platform
|
|
||||||
import logging
|
|
||||||
import threading
|
|
||||||
import serial
|
|
||||||
import serial.tools.list_ports
|
|
||||||
import pkg_resources
|
|
||||||
|
|
||||||
"""Some devices such as a seger jlink we never want to accidentally open"""
|
|
||||||
blacklistVids = dict.fromkeys([0x1366])
|
|
||||||
|
|
||||||
|
|
||||||
def quoteBooleans(a_string):
|
|
||||||
"""Quote booleans
|
|
||||||
given a string that contains ": true", replace with ": 'true'" (or false)
|
|
||||||
"""
|
|
||||||
tmp = a_string.replace(": true", ": 'true'")
|
|
||||||
tmp = tmp.replace(": false", ": 'false'")
|
|
||||||
return tmp
|
|
||||||
|
|
||||||
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:
|
|
||||||
return "unencrypted"
|
|
||||||
elif len(psk) == 1:
|
|
||||||
b = psk[0]
|
|
||||||
if b == 0:
|
|
||||||
return "unencrypted"
|
|
||||||
elif b == 1:
|
|
||||||
return "default"
|
|
||||||
else:
|
|
||||||
return f"simple{b - 1}"
|
|
||||||
else:
|
|
||||||
return "secret"
|
|
||||||
|
|
||||||
|
|
||||||
def stripnl(s):
|
|
||||||
"""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 Exception(f"FIXME: {message}")
|
|
||||||
|
|
||||||
|
|
||||||
def catchAndIgnore(reason, closure):
|
|
||||||
"""Call a closure but if it throws an exception print it and continue"""
|
|
||||||
try:
|
|
||||||
closure()
|
|
||||||
except BaseException as ex:
|
|
||||||
logging.error(f"Exception thrown in {reason}: {ex}")
|
|
||||||
|
|
||||||
|
|
||||||
def findPorts():
|
|
||||||
"""Find all ports that might have meshtastic devices
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list -- a list of device paths
|
|
||||||
"""
|
|
||||||
l = list(map(lambda port: port.device,
|
|
||||||
filter(lambda port: port.vid is not None and port.vid not in blacklistVids,
|
|
||||||
serial.tools.list_ports.comports())))
|
|
||||||
l.sort()
|
|
||||||
return l
|
|
||||||
|
|
||||||
|
|
||||||
class dotdict(dict):
|
|
||||||
"""dot.notation access to dictionary attributes"""
|
|
||||||
__getattr__ = dict.get
|
|
||||||
__setattr__ = dict.__setitem__
|
|
||||||
__delattr__ = dict.__delitem__
|
|
||||||
|
|
||||||
|
|
||||||
class Timeout:
|
|
||||||
"""Timeout class"""
|
|
||||||
def __init__(self, maxSecs=20):
|
|
||||||
self.expireTime = 0
|
|
||||||
self.sleepInterval = 0.1
|
|
||||||
self.expireTimeout = maxSecs
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""Restart the waitForSet timer"""
|
|
||||||
self.expireTime = time.time() + self.expireTimeout
|
|
||||||
|
|
||||||
def waitForSet(self, target, attrs=()):
|
|
||||||
"""Block until the specified attributes are set. Returns True if config has been received."""
|
|
||||||
self.reset()
|
|
||||||
while time.time() < self.expireTime:
|
|
||||||
if all(map(lambda a: getattr(target, a, None), attrs)):
|
|
||||||
return True
|
|
||||||
time.sleep(self.sleepInterval)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class DeferredExecution():
|
|
||||||
"""A thread that accepts closures to run, and runs them as they are received"""
|
|
||||||
|
|
||||||
def __init__(self, name=None):
|
|
||||||
self.queue = Queue()
|
|
||||||
self.thread = threading.Thread(target=self._run, args=(), name=name)
|
|
||||||
self.thread.daemon = True
|
|
||||||
self.thread.start()
|
|
||||||
|
|
||||||
def queueWork(self, runnable):
|
|
||||||
""" Queue up the work"""
|
|
||||||
self.queue.put(runnable)
|
|
||||||
|
|
||||||
def _run(self):
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
o = self.queue.get()
|
|
||||||
o()
|
|
||||||
except:
|
|
||||||
logging.error(
|
|
||||||
f"Unexpected error in deferred execution {sys.exc_info()[0]}")
|
|
||||||
print(traceback.format_exc())
|
|
||||||
|
|
||||||
|
|
||||||
def our_exit(message, return_value = 1):
|
|
||||||
"""Print the message and return a value.
|
|
||||||
return_value defaults to 1 (non-successful)
|
|
||||||
"""
|
|
||||||
print(message)
|
|
||||||
sys.exit(return_value)
|
|
||||||
|
|
||||||
|
|
||||||
def support_info():
|
|
||||||
"""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')
|
|
||||||
print('When adding an issue, be sure to include the following info:')
|
|
||||||
print(' System: {0}'.format(platform.system()))
|
|
||||||
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')</code></pre>
|
|
||||||
</details>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-functions">Functions</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.util.catchAndIgnore"><code class="name flex">
|
|
||||||
<span>def <span class="ident">catchAndIgnore</span></span>(<span>reason, closure)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Call a closure but if it throws an exception print it and continue</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def catchAndIgnore(reason, closure):
|
|
||||||
"""Call a closure but if it throws an exception print it and continue"""
|
|
||||||
try:
|
|
||||||
closure()
|
|
||||||
except BaseException as ex:
|
|
||||||
logging.error(f"Exception thrown in {reason}: {ex}")</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.findPorts"><code class="name flex">
|
|
||||||
<span>def <span class="ident">findPorts</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Find all ports that might have meshtastic devices</p>
|
|
||||||
<h2 id="returns">Returns</h2>
|
|
||||||
<p>list – a list of device paths</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def findPorts():
|
|
||||||
"""Find all ports that might have meshtastic devices
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list -- a list of device paths
|
|
||||||
"""
|
|
||||||
l = list(map(lambda port: port.device,
|
|
||||||
filter(lambda port: port.vid is not None and port.vid not in blacklistVids,
|
|
||||||
serial.tools.list_ports.comports())))
|
|
||||||
l.sort()
|
|
||||||
return l</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.fixme"><code class="name flex">
|
|
||||||
<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>
|
|
||||||
<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 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>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Print the message and return a value.
|
|
||||||
return_value defaults to 1 (non-successful)</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def our_exit(message, return_value = 1):
|
|
||||||
"""Print the message and return a value.
|
|
||||||
return_value defaults to 1 (non-successful)
|
|
||||||
"""
|
|
||||||
print(message)
|
|
||||||
sys.exit(return_value)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.pskToString"><code class="name flex">
|
|
||||||
<span>def <span class="ident">pskToString</span></span>(<span>psk: bytes)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def pskToString(psk: bytes):
|
|
||||||
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
|
|
||||||
if len(psk) == 0:
|
|
||||||
return "unencrypted"
|
|
||||||
elif len(psk) == 1:
|
|
||||||
b = psk[0]
|
|
||||||
if b == 0:
|
|
||||||
return "unencrypted"
|
|
||||||
elif b == 1:
|
|
||||||
return "default"
|
|
||||||
else:
|
|
||||||
return f"simple{b - 1}"
|
|
||||||
else:
|
|
||||||
return "secret"</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.quoteBooleans"><code class="name flex">
|
|
||||||
<span>def <span class="ident">quoteBooleans</span></span>(<span>a_string)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Quote booleans
|
|
||||||
given a string that contains ": true", replace with ": 'true'" (or false)</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def quoteBooleans(a_string):
|
|
||||||
"""Quote booleans
|
|
||||||
given a string that contains ": true", replace with ": 'true'" (or false)
|
|
||||||
"""
|
|
||||||
tmp = a_string.replace(": true", ": 'true'")
|
|
||||||
tmp = tmp.replace(": false", ": 'false'")
|
|
||||||
return tmp</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.stripnl"><code class="name flex">
|
|
||||||
<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>
|
|
||||||
<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)"""
|
|
||||||
s = str(s).replace("\n", " ")
|
|
||||||
return ' '.join(s.split())</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.support_info"><code class="name flex">
|
|
||||||
<span>def <span class="ident">support_info</span></span>(<span>)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<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 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')
|
|
||||||
print('When adding an issue, be sure to include the following info:')
|
|
||||||
print(' System: {0}'.format(platform.system()))
|
|
||||||
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')</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.util.DeferredExecution"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">DeferredExecution</span></span>
|
|
||||||
<span>(</span><span>name=None)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>A thread that accepts closures to run, and runs them as they are received</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class DeferredExecution():
|
|
||||||
"""A thread that accepts closures to run, and runs them as they are received"""
|
|
||||||
|
|
||||||
def __init__(self, name=None):
|
|
||||||
self.queue = Queue()
|
|
||||||
self.thread = threading.Thread(target=self._run, args=(), name=name)
|
|
||||||
self.thread.daemon = True
|
|
||||||
self.thread.start()
|
|
||||||
|
|
||||||
def queueWork(self, runnable):
|
|
||||||
""" Queue up the work"""
|
|
||||||
self.queue.put(runnable)
|
|
||||||
|
|
||||||
def _run(self):
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
o = self.queue.get()
|
|
||||||
o()
|
|
||||||
except:
|
|
||||||
logging.error(
|
|
||||||
f"Unexpected error in deferred execution {sys.exc_info()[0]}")
|
|
||||||
print(traceback.format_exc())</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.util.DeferredExecution.queueWork"><code class="name flex">
|
|
||||||
<span>def <span class="ident">queueWork</span></span>(<span>self, runnable)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Queue up the work</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def queueWork(self, runnable):
|
|
||||||
""" Queue up the work"""
|
|
||||||
self.queue.put(runnable)</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.Timeout"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">Timeout</span></span>
|
|
||||||
<span>(</span><span>maxSecs=20)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Timeout class</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class Timeout:
|
|
||||||
"""Timeout class"""
|
|
||||||
def __init__(self, maxSecs=20):
|
|
||||||
self.expireTime = 0
|
|
||||||
self.sleepInterval = 0.1
|
|
||||||
self.expireTimeout = maxSecs
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""Restart the waitForSet timer"""
|
|
||||||
self.expireTime = time.time() + self.expireTimeout
|
|
||||||
|
|
||||||
def waitForSet(self, target, attrs=()):
|
|
||||||
"""Block until the specified attributes are set. Returns True if config has been received."""
|
|
||||||
self.reset()
|
|
||||||
while time.time() < self.expireTime:
|
|
||||||
if all(map(lambda a: getattr(target, a, None), attrs)):
|
|
||||||
return True
|
|
||||||
time.sleep(self.sleepInterval)
|
|
||||||
return False</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Methods</h3>
|
|
||||||
<dl>
|
|
||||||
<dt id="meshtastic.util.Timeout.reset"><code class="name flex">
|
|
||||||
<span>def <span class="ident">reset</span></span>(<span>self)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Restart the waitForSet timer</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def reset(self):
|
|
||||||
"""Restart the waitForSet timer"""
|
|
||||||
self.expireTime = time.time() + self.expireTimeout</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.Timeout.waitForSet"><code class="name flex">
|
|
||||||
<span>def <span class="ident">waitForSet</span></span>(<span>self, target, attrs=())</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>Block until the specified attributes are set. Returns True if config has been received.</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">def waitForSet(self, target, attrs=()):
|
|
||||||
"""Block until the specified attributes are set. Returns True if config has been received."""
|
|
||||||
self.reset()
|
|
||||||
while time.time() < self.expireTime:
|
|
||||||
if all(map(lambda a: getattr(target, a, None), attrs)):
|
|
||||||
return True
|
|
||||||
time.sleep(self.sleepInterval)
|
|
||||||
return False</code></pre>
|
|
||||||
</details>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
<dt id="meshtastic.util.dotdict"><code class="flex name class">
|
|
||||||
<span>class <span class="ident">dotdict</span></span>
|
|
||||||
<span>(</span><span>*args, **kwargs)</span>
|
|
||||||
</code></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="desc"><p>dot.notation access to dictionary attributes</p></div>
|
|
||||||
<details class="source">
|
|
||||||
<summary>
|
|
||||||
<span>Expand source code</span>
|
|
||||||
</summary>
|
|
||||||
<pre><code class="python">class dotdict(dict):
|
|
||||||
"""dot.notation access to dictionary attributes"""
|
|
||||||
__getattr__ = dict.get
|
|
||||||
__setattr__ = dict.__setitem__
|
|
||||||
__delattr__ = dict.__delitem__</code></pre>
|
|
||||||
</details>
|
|
||||||
<h3>Ancestors</h3>
|
|
||||||
<ul class="hlist">
|
|
||||||
<li>builtins.dict</li>
|
|
||||||
</ul>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
<nav id="sidebar">
|
|
||||||
<h1>Index</h1>
|
|
||||||
<div class="toc">
|
|
||||||
<ul></ul>
|
|
||||||
</div>
|
|
||||||
<ul id="index">
|
|
||||||
<li><h3>Super-module</h3>
|
|
||||||
<ul>
|
|
||||||
<li><code><a title="meshtastic" href="index.html">meshtastic</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-functions">Functions</a></h3>
|
|
||||||
<ul class="two-column">
|
|
||||||
<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.quoteBooleans" href="#meshtastic.util.quoteBooleans">quoteBooleans</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.util.stripnl" href="#meshtastic.util.stripnl">stripnl</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.util.support_info" href="#meshtastic.util.support_info">support_info</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><h3><a href="#header-classes">Classes</a></h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.util.DeferredExecution" href="#meshtastic.util.DeferredExecution">DeferredExecution</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.util.DeferredExecution.queueWork" href="#meshtastic.util.DeferredExecution.queueWork">queueWork</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.util.Timeout" href="#meshtastic.util.Timeout">Timeout</a></code></h4>
|
|
||||||
<ul class="">
|
|
||||||
<li><code><a title="meshtastic.util.Timeout.reset" href="#meshtastic.util.Timeout.reset">reset</a></code></li>
|
|
||||||
<li><code><a title="meshtastic.util.Timeout.waitForSet" href="#meshtastic.util.Timeout.waitForSet">waitForSet</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<h4><code><a title="meshtastic.util.dotdict" href="#meshtastic.util.dotdict">dotdict</a></code></h4>
|
|
||||||
</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>
|
|
||||||
@@ -77,9 +77,11 @@ from pubsub import pub
|
|||||||
from dotmap import DotMap
|
from dotmap import DotMap
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
from google.protobuf.json_format import MessageToJson
|
from google.protobuf.json_format import MessageToJson
|
||||||
from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
from meshtastic.util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
||||||
from .node import Node
|
from meshtastic.node import Node
|
||||||
from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util
|
from meshtastic import (mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2,
|
||||||
|
environmental_measurement_pb2, remote_hardware_pb2,
|
||||||
|
channel_pb2, radioconfig_pb2, util)
|
||||||
|
|
||||||
# Note: To follow PEP224, comments should be after the module variable.
|
# Note: To follow PEP224, comments should be after the module variable.
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ import pyqrcode
|
|||||||
import pkg_resources
|
import pkg_resources
|
||||||
import meshtastic.util
|
import meshtastic.util
|
||||||
import meshtastic.test
|
import meshtastic.test
|
||||||
from . import remote_hardware
|
from meshtastic import remote_hardware
|
||||||
from . import portnums_pb2, channel_pb2, radioconfig_pb2
|
from meshtastic.ble_interface import BLEInterface
|
||||||
from .globals import Globals
|
from meshtastic import portnums_pb2, channel_pb2, radioconfig_pb2
|
||||||
|
from meshtastic.globals import Globals
|
||||||
|
|
||||||
|
|
||||||
have_tunnel = platform.system() == 'Linux'
|
|
||||||
"""We only import the tunnel code if we are on a platform that can run it. """
|
|
||||||
|
|
||||||
def onReceive(packet, interface):
|
def onReceive(packet, interface):
|
||||||
"""Callback invoked when a packet arrives"""
|
"""Callback invoked when a packet arrives"""
|
||||||
our_globals = Globals.getInstance()
|
our_globals = Globals.getInstance()
|
||||||
@@ -45,7 +43,7 @@ def onReceive(packet, interface):
|
|||||||
interface.sendText(reply)
|
interface.sendText(reply)
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print(ex)
|
print(f'Warning: There is no field {ex} in the packet.')
|
||||||
|
|
||||||
|
|
||||||
def onConnection(interface, topic=pub.AUTO_TOPIC):
|
def onConnection(interface, topic=pub.AUTO_TOPIC):
|
||||||
@@ -137,7 +135,9 @@ def onConnected(interface):
|
|||||||
our_globals = Globals.getInstance()
|
our_globals = Globals.getInstance()
|
||||||
args = our_globals.get_args()
|
args = our_globals.get_args()
|
||||||
|
|
||||||
print("Connected to radio")
|
# do not print this line if we are exporting the config
|
||||||
|
if not args.export_config:
|
||||||
|
print("Connected to radio")
|
||||||
|
|
||||||
def getNode():
|
def getNode():
|
||||||
"""This operation could be expensive, so we try to cache the results"""
|
"""This operation could be expensive, so we try to cache the results"""
|
||||||
@@ -495,12 +495,16 @@ def onConnected(interface):
|
|||||||
qr = pyqrcode.create(url)
|
qr = pyqrcode.create(url)
|
||||||
print(qr.terminal())
|
print(qr.terminal())
|
||||||
|
|
||||||
|
have_tunnel = platform.system() == 'Linux'
|
||||||
if have_tunnel and args.tunnel:
|
if have_tunnel and args.tunnel:
|
||||||
# pylint: disable=C0415
|
# pylint: disable=C0415
|
||||||
from . import tunnel
|
from . import tunnel
|
||||||
# Even if others said we could close, stay open if the user asked for a tunnel
|
# Even if others said we could close, stay open if the user asked for a tunnel
|
||||||
closeNow = False
|
closeNow = False
|
||||||
tunnel.Tunnel(interface, subnet=args.tunnel_net)
|
if interface.noProto:
|
||||||
|
logging.warning(f"Not starting Tunnel - disabled by noProto")
|
||||||
|
else:
|
||||||
|
tunnel.Tunnel(interface, subnet=args.tunnel_net)
|
||||||
|
|
||||||
# if the user didn't ask for serial debugging output, we might want to exit after we've done our operation
|
# if the user didn't ask for serial debugging output, we might want to exit after we've done our operation
|
||||||
if (not args.seriallog) and closeNow:
|
if (not args.seriallog) and closeNow:
|
||||||
@@ -627,20 +631,19 @@ def common():
|
|||||||
|
|
||||||
subscribe()
|
subscribe()
|
||||||
if args.ble:
|
if args.ble:
|
||||||
client = meshtastic.ble_interface.BLEInterface(args.ble, debugOut=logfile, noProto=args.noproto)
|
client = BLEInterface(args.ble, debugOut=logfile, noProto=args.noproto)
|
||||||
elif args.host:
|
elif args.host:
|
||||||
client = meshtastic.tcp_interface.TCPInterface(
|
client = meshtastic.tcp_interface.TCPInterface(args.host, debugOut=logfile, noProto=args.noproto)
|
||||||
args.host, debugOut=logfile, noProto=args.noproto)
|
|
||||||
else:
|
else:
|
||||||
client = meshtastic.serial_interface.SerialInterface(
|
client = meshtastic.serial_interface.SerialInterface(args.port, debugOut=logfile, noProto=args.noproto)
|
||||||
args.port, debugOut=logfile, noProto=args.noproto)
|
|
||||||
|
|
||||||
# We assume client is fully connected now
|
# We assume client is fully connected now
|
||||||
onConnected(client)
|
onConnected(client)
|
||||||
#if logfile:
|
#if logfile:
|
||||||
#logfile.close()
|
#logfile.close()
|
||||||
|
|
||||||
if args.noproto or (have_tunnel and args.tunnel): # loop until someone presses ctrlc
|
have_tunnel = platform.system() == 'Linux'
|
||||||
|
if args.noproto or args.reply or (have_tunnel and args.tunnel): # loop until someone presses ctrlc
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1000)
|
time.sleep(1000)
|
||||||
|
|
||||||
@@ -805,11 +808,13 @@ def initParser():
|
|||||||
parser.add_argument('--unset-router', dest='deprecated',
|
parser.add_argument('--unset-router', dest='deprecated',
|
||||||
action='store_false', help='Deprecated, use "--set is_router false" instead')
|
action='store_false', help='Deprecated, use "--set is_router false" instead')
|
||||||
|
|
||||||
|
have_tunnel = platform.system() == 'Linux'
|
||||||
if have_tunnel:
|
if have_tunnel:
|
||||||
parser.add_argument('--tunnel',
|
parser.add_argument('--tunnel', action='store_true',
|
||||||
action='store_true', help="Create a TUN tunnel device for forwarding IP packets over the mesh")
|
help="Create a TUN tunnel device for forwarding IP packets over the mesh")
|
||||||
parser.add_argument(
|
parser.add_argument("--subnet", dest='tunnel_net',
|
||||||
"--subnet", dest='tunnel_net', help="Sets the local-end subnet address for the TUN IP bridge", default=None)
|
help="Sets the local-end subnet address for the TUN IP bridge. (ex: 10.115' which is the default)",
|
||||||
|
default=None)
|
||||||
|
|
||||||
parser.set_defaults(deprecated=None)
|
parser.set_defaults(deprecated=None)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import logging
|
|||||||
import pygatt
|
import pygatt
|
||||||
|
|
||||||
|
|
||||||
from .mesh_interface import MeshInterface
|
from meshtastic.mesh_interface import MeshInterface
|
||||||
|
|
||||||
# Our standard BLE characteristics
|
# Our standard BLE characteristics
|
||||||
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class Globals:
|
|||||||
self.target_node = None
|
self.target_node = None
|
||||||
self.channel_index = None
|
self.channel_index = None
|
||||||
self.logfile = None
|
self.logfile = None
|
||||||
|
self.tunnelInstance = None
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""Reset all of our globals. If you add a member, add it to this method, too."""
|
"""Reset all of our globals. If you add a member, add it to this method, too."""
|
||||||
@@ -37,6 +38,8 @@ class Globals:
|
|||||||
self.parser = None
|
self.parser = None
|
||||||
self.target_node = None
|
self.target_node = None
|
||||||
self.channel_index = None
|
self.channel_index = None
|
||||||
|
self.logfile = None
|
||||||
|
self.tunnelInstance = None
|
||||||
|
|
||||||
# setters
|
# setters
|
||||||
def set_args(self, args):
|
def set_args(self, args):
|
||||||
@@ -59,6 +62,10 @@ class Globals:
|
|||||||
"""Set the logfile"""
|
"""Set the logfile"""
|
||||||
self.logfile = logfile
|
self.logfile = logfile
|
||||||
|
|
||||||
|
def set_tunnelInstance(self, tunnelInstance):
|
||||||
|
"""Set the tunnelInstance"""
|
||||||
|
self.tunnelInstance = tunnelInstance
|
||||||
|
|
||||||
# getters
|
# getters
|
||||||
def get_args(self):
|
def get_args(self):
|
||||||
"""Get args"""
|
"""Get args"""
|
||||||
@@ -79,3 +86,7 @@ class Globals:
|
|||||||
def get_logfile(self):
|
def get_logfile(self):
|
||||||
"""Get logfile"""
|
"""Get logfile"""
|
||||||
return self.logfile
|
return self.logfile
|
||||||
|
|
||||||
|
def get_tunnelInstance(self):
|
||||||
|
"""Get tunnelInstance"""
|
||||||
|
return self.tunnelInstance
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ from google.protobuf.json_format import MessageToJson
|
|||||||
|
|
||||||
|
|
||||||
import meshtastic.node
|
import meshtastic.node
|
||||||
from . import portnums_pb2, mesh_pb2
|
from meshtastic import portnums_pb2, mesh_pb2
|
||||||
from .util import stripnl, Timeout, our_exit
|
from meshtastic.util import stripnl, Timeout, our_exit, remove_keys_from_dict, convert_mac_addr
|
||||||
from .__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
|
from meshtastic.__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
|
||||||
|
|
||||||
class MeshInterface:
|
class MeshInterface:
|
||||||
"""Interface class for meshtastic devices
|
"""Interface class for meshtastic devices
|
||||||
@@ -68,8 +68,7 @@ class MeshInterface:
|
|||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
if exc_type is not None and exc_value is not None:
|
if exc_type is not None and exc_value is not None:
|
||||||
logging.error(
|
logging.error(f'An exception of type {exc_type} with value {exc_value} has occurred')
|
||||||
f'An exception of type {exc_type} with value {exc_value} has occurred')
|
|
||||||
if traceback is not None:
|
if traceback is not None:
|
||||||
logging.error(f'Traceback: {traceback}')
|
logging.error(f'Traceback: {traceback}')
|
||||||
self.close()
|
self.close()
|
||||||
@@ -84,7 +83,19 @@ class MeshInterface:
|
|||||||
nodes = ""
|
nodes = ""
|
||||||
if self.nodes:
|
if self.nodes:
|
||||||
for n in self.nodes.values():
|
for n in self.nodes.values():
|
||||||
nodes = nodes + f" {stripnl(n)}"
|
# when the TBeam is first booted, it sometimes shows the raw data
|
||||||
|
# so, we will just remove any raw keys
|
||||||
|
keys_to_remove = ('raw', 'decoded', 'payload')
|
||||||
|
n2 = remove_keys_from_dict(keys_to_remove, n)
|
||||||
|
|
||||||
|
# if we have 'macaddr', re-format it
|
||||||
|
if 'macaddr' in n2['user']:
|
||||||
|
val = n2['user']['macaddr']
|
||||||
|
# decode the base64 value
|
||||||
|
addr = convert_mac_addr(val)
|
||||||
|
n2['user']['macaddr'] = addr
|
||||||
|
|
||||||
|
nodes = nodes + f" {stripnl(n2)}"
|
||||||
infos = owner + myinfo + mesh + nodes
|
infos = owner + myinfo + mesh + nodes
|
||||||
print(infos)
|
print(infos)
|
||||||
return infos
|
return infos
|
||||||
@@ -382,11 +393,11 @@ class MeshInterface:
|
|||||||
return user.get('shortName', None)
|
return user.get('shortName', None)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _waitConnected(self):
|
def _waitConnected(self, timeout=15.0):
|
||||||
"""Block until the initial node db download is complete, or timeout
|
"""Block until the initial node db download is complete, or timeout
|
||||||
and raise an exception"""
|
and raise an exception"""
|
||||||
if not self.noProto:
|
if not self.noProto:
|
||||||
if not self.isConnected.wait(15.0): # timeout after x seconds
|
if not self.isConnected.wait(timeout): # timeout after x seconds
|
||||||
raise Exception("Timed out waiting for connection completion")
|
raise Exception("Timed out waiting for connection completion")
|
||||||
|
|
||||||
# If we failed while connecting, raise the connection to the client
|
# If we failed while connecting, raise the connection to the client
|
||||||
@@ -404,8 +415,7 @@ class MeshInterface:
|
|||||||
def _disconnected(self):
|
def _disconnected(self):
|
||||||
"""Called by subclasses to tell clients this interface has disconnected"""
|
"""Called by subclasses to tell clients this interface has disconnected"""
|
||||||
self.isConnected.clear()
|
self.isConnected.clear()
|
||||||
publishingThread.queueWork(lambda: pub.sendMessage(
|
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.lost", interface=self))
|
||||||
"meshtastic.connection.lost", interface=self))
|
|
||||||
|
|
||||||
def _startHeartbeat(self):
|
def _startHeartbeat(self):
|
||||||
"""We need to send a heartbeat message to the device every X seconds"""
|
"""We need to send a heartbeat message to the device every X seconds"""
|
||||||
@@ -431,8 +441,7 @@ class MeshInterface:
|
|||||||
if not self.isConnected.is_set():
|
if not self.isConnected.is_set():
|
||||||
self.isConnected.set()
|
self.isConnected.set()
|
||||||
self._startHeartbeat()
|
self._startHeartbeat()
|
||||||
publishingThread.queueWork(lambda: pub.sendMessage(
|
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.established", interface=self))
|
||||||
"meshtastic.connection.established", interface=self))
|
|
||||||
|
|
||||||
def _startConfig(self):
|
def _startConfig(self):
|
||||||
"""Start device packets flowing"""
|
"""Start device packets flowing"""
|
||||||
@@ -504,7 +513,8 @@ class MeshInterface:
|
|||||||
elif fromRadio.HasField("node_info"):
|
elif fromRadio.HasField("node_info"):
|
||||||
node = asDict["nodeInfo"]
|
node = asDict["nodeInfo"]
|
||||||
try:
|
try:
|
||||||
self._fixupPosition(node["position"])
|
newpos = self._fixupPosition(node["position"])
|
||||||
|
node["position"] = newpos
|
||||||
except:
|
except:
|
||||||
logging.debug("Node without position")
|
logging.debug("Node without position")
|
||||||
|
|
||||||
@@ -536,12 +546,14 @@ class MeshInterface:
|
|||||||
"""Convert integer lat/lon into floats
|
"""Convert integer lat/lon into floats
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
position {Position dictionary} -- object ot fix up
|
position {Position dictionary} -- object to fix up
|
||||||
|
Returns the position with the updated keys
|
||||||
"""
|
"""
|
||||||
if "latitudeI" in position:
|
if "latitudeI" in position:
|
||||||
position["latitude"] = position["latitudeI"] * 1e-7
|
position["latitude"] = position["latitudeI"] * 1e-7
|
||||||
if "longitudeI" in position:
|
if "longitudeI" in position:
|
||||||
position["longitude"] = position["longitudeI"] * 1e-7
|
position["longitude"] = position["longitudeI"] * 1e-7
|
||||||
|
return position
|
||||||
|
|
||||||
def _nodeNumToId(self, num):
|
def _nodeNumToId(self, num):
|
||||||
"""Map a node node number to a node ID
|
"""Map a node node number to a node ID
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
from google.protobuf.json_format import MessageToJson
|
from google.protobuf.json_format import MessageToJson
|
||||||
from . import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
|
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
|
||||||
from .util import pskToString, stripnl, Timeout, our_exit, fromPSK
|
from meshtastic.util import pskToString, stripnl, Timeout, our_exit, fromPSK
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from . import portnums_pb2, remote_hardware_pb2
|
from meshtastic import portnums_pb2, remote_hardware_pb2
|
||||||
from .util import our_exit
|
from meshtastic.util import our_exit
|
||||||
|
|
||||||
|
|
||||||
def onGPIOreceive(packet, interface):
|
def onGPIOreceive(packet, interface):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import platform
|
|||||||
import serial
|
import serial
|
||||||
|
|
||||||
import meshtastic.util
|
import meshtastic.util
|
||||||
from .stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
if platform.system() != 'Windows':
|
if platform.system() != 'Windows':
|
||||||
import termios
|
import termios
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import traceback
|
|||||||
import serial
|
import serial
|
||||||
|
|
||||||
|
|
||||||
from .mesh_interface import MeshInterface
|
from meshtastic.mesh_interface import MeshInterface
|
||||||
from .util import stripnl
|
from meshtastic.util import stripnl
|
||||||
|
|
||||||
|
|
||||||
START1 = 0x94
|
START1 = 0x94
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import logging
|
|||||||
import socket
|
import socket
|
||||||
from typing import AnyStr
|
from typing import AnyStr
|
||||||
|
|
||||||
from .stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
class TCPInterface(StreamInterface):
|
class TCPInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a TCP link"""
|
"""Interface class for meshtastic devices over a TCP link"""
|
||||||
@@ -17,8 +17,6 @@ class TCPInterface(StreamInterface):
|
|||||||
hostname {string} -- Hostname/IP address of the device to connect to
|
hostname {string} -- Hostname/IP address of the device to connect to
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Instead of wrapping as a stream, we use the native socket API
|
|
||||||
# self.stream = sock.makefile('rw')
|
|
||||||
self.stream = None
|
self.stream = None
|
||||||
|
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import traceback
|
|||||||
from dotmap import DotMap
|
from dotmap import DotMap
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
import meshtastic.util
|
import meshtastic.util
|
||||||
from .__init__ import BROADCAST_NUM
|
from meshtastic.__init__ import BROADCAST_NUM
|
||||||
from .serial_interface import SerialInterface
|
from meshtastic.serial_interface import SerialInterface
|
||||||
from .tcp_interface import TCPInterface
|
from meshtastic.tcp_interface import TCPInterface
|
||||||
|
|
||||||
|
|
||||||
"""The interfaces we are using for our tests"""
|
"""The interfaces we are using for our tests"""
|
||||||
|
|||||||
@@ -6,13 +6,21 @@ import pytest
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.int
|
@pytest.mark.int
|
||||||
def test_int_no_args():
|
def test_int_meshtastic_no_args():
|
||||||
"""Test without any args"""
|
"""Test meshtastic without any args"""
|
||||||
return_value, out = subprocess.getstatusoutput('meshtastic')
|
return_value, out = subprocess.getstatusoutput('meshtastic')
|
||||||
assert re.match(r'usage: meshtastic', out)
|
assert re.match(r'usage: meshtastic', out)
|
||||||
assert return_value == 1
|
assert return_value == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.int
|
||||||
|
def test_int_mesh_tunnel_no_args():
|
||||||
|
"""Test mesh-tunnel without any args"""
|
||||||
|
return_value, out = subprocess.getstatusoutput('mesh-tunnel')
|
||||||
|
assert re.match(r'usage: mesh-tunnel', out)
|
||||||
|
assert return_value == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.int
|
@pytest.mark.int
|
||||||
def test_int_version():
|
def test_int_version():
|
||||||
"""Test '--version'."""
|
"""Test '--version'."""
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import platform
|
||||||
|
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from meshtastic.__main__ import initParser, main, Globals, onReceive, onConnection, export_config, getPref, setPref
|
from meshtastic.__main__ import initParser, main, Globals, onReceive, onConnection, export_config, getPref, setPref, onNode, tunnelMain
|
||||||
#from ..radioconfig_pb2 import UserPreferences
|
#from ..radioconfig_pb2 import UserPreferences
|
||||||
import meshtastic.radioconfig_pb2
|
import meshtastic.radioconfig_pb2
|
||||||
from ..serial_interface import SerialInterface
|
from ..serial_interface import SerialInterface
|
||||||
from ..tcp_interface import TCPInterface
|
from ..tcp_interface import TCPInterface
|
||||||
from ..ble_interface import BLEInterface
|
#from ..ble_interface import BLEInterface
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
from ..channel_pb2 import Channel
|
from ..channel_pb2 import Channel
|
||||||
from ..remote_hardware import onGPIOreceive
|
from ..remote_hardware import onGPIOreceive
|
||||||
@@ -62,7 +63,7 @@ def test_main_main_version(capsys, reset_globals):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_main_main_no_args(reset_globals):
|
def test_main_main_no_args(reset_globals, capsys):
|
||||||
"""Test with no args"""
|
"""Test with no args"""
|
||||||
sys.argv = ['']
|
sys.argv = ['']
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(sys.argv)
|
||||||
@@ -71,6 +72,8 @@ def test_main_main_no_args(reset_globals):
|
|||||||
main()
|
main()
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
_, err = capsys.readouterr()
|
||||||
|
assert re.search(r'usage:', err, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -111,7 +114,7 @@ def test_main_ch_index_no_devices(patched_find_ports, capsys, reset_globals):
|
|||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('meshtastic.util.findPorts', return_value=[])
|
@patch('meshtastic.util.findPorts', return_value=[])
|
||||||
def test_main_test_no_ports(patched_find_ports, reset_globals):
|
def test_main_test_no_ports(patched_find_ports, reset_globals, capsys):
|
||||||
"""Test --test with no hardware"""
|
"""Test --test with no hardware"""
|
||||||
sys.argv = ['', '--test']
|
sys.argv = ['', '--test']
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(sys.argv)
|
||||||
@@ -122,11 +125,14 @@ def test_main_test_no_ports(patched_find_ports, reset_globals):
|
|||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
patched_find_ports.assert_called()
|
patched_find_ports.assert_called()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Warning: Must have at least two devices connected to USB', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1'])
|
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1'])
|
||||||
def test_main_test_one_port(patched_find_ports, reset_globals):
|
def test_main_test_one_port(patched_find_ports, reset_globals, capsys):
|
||||||
"""Test --test with one fake port"""
|
"""Test --test with one fake port"""
|
||||||
sys.argv = ['', '--test']
|
sys.argv = ['', '--test']
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(sys.argv)
|
||||||
@@ -137,12 +143,14 @@ def test_main_test_one_port(patched_find_ports, reset_globals):
|
|||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
patched_find_ports.assert_called()
|
patched_find_ports.assert_called()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Warning: Must have at least two devices connected to USB', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('meshtastic.test.testAll', return_value=True)
|
@patch('meshtastic.test.testAll', return_value=True)
|
||||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1', '/dev/ttyFake2'])
|
def test_main_test_two_ports_success(patched_test_all, reset_globals, capsys):
|
||||||
def test_main_test_two_ports_success(patched_find_ports, patched_test_all, reset_globals):
|
|
||||||
"""Test --test two fake ports and testAll() is a simulated success"""
|
"""Test --test two fake ports and testAll() is a simulated success"""
|
||||||
sys.argv = ['', '--test']
|
sys.argv = ['', '--test']
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(sys.argv)
|
||||||
@@ -151,14 +159,15 @@ def test_main_test_two_ports_success(patched_find_ports, patched_test_all, reset
|
|||||||
main()
|
main()
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 0
|
assert pytest_wrapped_e.value.code == 0
|
||||||
# TODO: why does this fail? patched_find_ports.assert_called()
|
|
||||||
patched_test_all.assert_called()
|
patched_test_all.assert_called()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Test was a success.', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('meshtastic.test.testAll', return_value=False)
|
@patch('meshtastic.test.testAll', return_value=False)
|
||||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyFake1', '/dev/ttyFake2'])
|
def test_main_test_two_ports_fails(patched_test_all, reset_globals, capsys):
|
||||||
def test_main_test_two_ports_fails(patched_find_ports, patched_test_all, reset_globals):
|
|
||||||
"""Test --test two fake ports and testAll() is a simulated failure"""
|
"""Test --test two fake ports and testAll() is a simulated failure"""
|
||||||
sys.argv = ['', '--test']
|
sys.argv = ['', '--test']
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(sys.argv)
|
||||||
@@ -167,8 +176,10 @@ def test_main_test_two_ports_fails(patched_find_ports, patched_test_all, reset_g
|
|||||||
main()
|
main()
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
# TODO: why does this fail? patched_find_ports.assert_called()
|
|
||||||
patched_test_all.assert_called()
|
patched_test_all.assert_called()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Test was not successful.', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -209,23 +220,24 @@ def test_main_info_with_tcp_interface(capsys, reset_globals):
|
|||||||
mo.assert_called()
|
mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
# TODO: comment out ble (for now)
|
||||||
def test_main_info_with_ble_interface(capsys, reset_globals):
|
#@pytest.mark.unit
|
||||||
"""Test --info"""
|
#def test_main_info_with_ble_interface(capsys, reset_globals):
|
||||||
sys.argv = ['', '--info', '--ble', 'foo']
|
# """Test --info"""
|
||||||
Globals.getInstance().set_args(sys.argv)
|
# sys.argv = ['', '--info', '--ble', 'foo']
|
||||||
|
# Globals.getInstance().set_args(sys.argv)
|
||||||
iface = MagicMock(autospec=BLEInterface)
|
#
|
||||||
def mock_showInfo():
|
# iface = MagicMock(autospec=BLEInterface)
|
||||||
print('inside mocked showInfo')
|
# def mock_showInfo():
|
||||||
iface.showInfo.side_effect = mock_showInfo
|
# print('inside mocked showInfo')
|
||||||
with patch('meshtastic.ble_interface.BLEInterface', return_value=iface) as mo:
|
# iface.showInfo.side_effect = mock_showInfo
|
||||||
main()
|
# with patch('meshtastic.ble_interface.BLEInterface', return_value=iface) as mo:
|
||||||
out, err = capsys.readouterr()
|
# main()
|
||||||
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
# out, err = capsys.readouterr()
|
||||||
assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
|
# assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
assert err == ''
|
# assert re.search(r'inside mocked showInfo', out, re.MULTILINE)
|
||||||
mo.assert_called()
|
# assert err == ''
|
||||||
|
# mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -1232,18 +1244,23 @@ def test_main_setchan(capsys, reset_globals):
|
|||||||
main()
|
main()
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
_, err = capsys.readouterr()
|
||||||
|
assert re.search(r'usage:', err, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_main_onReceive_empty(caplog, reset_globals):
|
def test_main_onReceive_empty(caplog, reset_globals, capsys):
|
||||||
"""Test onReceive"""
|
"""Test onReceive"""
|
||||||
sys.argv = ['']
|
args = MagicMock()
|
||||||
Globals.getInstance().set_args(sys.argv)
|
Globals.getInstance().set_args(args)
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
packet = {'decoded': 'foo'}
|
packet = {}
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
onReceive(packet, iface)
|
onReceive(packet, iface)
|
||||||
assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
|
assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r"Warning: There is no field 'to' in the packet.", out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
# TODO: use this captured position app message (might want/need in the future)
|
# TODO: use this captured position app message (might want/need in the future)
|
||||||
@@ -1258,7 +1275,7 @@ def test_main_onReceive_empty(caplog, reset_globals):
|
|||||||
# }
|
# }
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_main_onReceive_with_sendtext(caplog, reset_globals):
|
def test_main_onReceive_with_sendtext(caplog, capsys, reset_globals):
|
||||||
"""Test onReceive with sendtext
|
"""Test onReceive with sendtext
|
||||||
The entire point of this test is to make sure the interface.close() call
|
The entire point of this test is to make sure the interface.close() call
|
||||||
is made in onReceive().
|
is made in onReceive().
|
||||||
@@ -1287,41 +1304,31 @@ def test_main_onReceive_with_sendtext(caplog, reset_globals):
|
|||||||
onReceive(packet, iface)
|
onReceive(packet, iface)
|
||||||
assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
|
assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
|
||||||
mo.assert_called()
|
mo.assert_called()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Sending text message hello to', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_main_onReceive_with_reply(caplog, capsys, reset_globals):
|
def test_main_onReceive_with_text(caplog, capsys, reset_globals):
|
||||||
"""Test onReceive with a reply
|
"""Test onReceive with text
|
||||||
To capture: on one device run '--sendtext aaa --reply' and on another
|
|
||||||
device run '--sendtext bbb --reply', then back to the first device and
|
|
||||||
run '--sendtext aaa2 --reply'. You should now see a "Sending reply" message.
|
|
||||||
"""
|
"""
|
||||||
sys.argv = ['', '--sendtext', 'hello', '--reply']
|
args = MagicMock()
|
||||||
Globals.getInstance().set_args(sys.argv)
|
args.sendtext.return_value = 'foo'
|
||||||
|
Globals.getInstance().set_args(args)
|
||||||
|
|
||||||
# Note: 'TEXT_MESSAGE_APP' value is 1
|
# Note: 'TEXT_MESSAGE_APP' value is 1
|
||||||
|
# Note: Some of this is faked below.
|
||||||
send_packet = {
|
packet = {
|
||||||
'to': 4294967295,
|
'to': 4294967295,
|
||||||
'decoded': {
|
'decoded': {
|
||||||
'portnum': 1,
|
'portnum': 1,
|
||||||
'payload': "hello"
|
'payload': "hello",
|
||||||
|
'text': "faked"
|
||||||
},
|
},
|
||||||
'id': 334776977,
|
'id': 334776977,
|
||||||
'hop_limit': 3,
|
'hop_limit': 3,
|
||||||
'want_ack': True
|
'want_ack': True,
|
||||||
}
|
|
||||||
|
|
||||||
reply_packet = {
|
|
||||||
'from': 682968668,
|
|
||||||
'to': 4294967295,
|
|
||||||
'decoded': {
|
|
||||||
'portnum': 'TEXT_MESSAGE_APP',
|
|
||||||
'payload': b'bbb',
|
|
||||||
'text': 'bbb'
|
|
||||||
},
|
|
||||||
'id': 1709936182,
|
|
||||||
'rxTime': 1640381999,
|
|
||||||
'rxSnr': 6.0,
|
'rxSnr': 6.0,
|
||||||
'hopLimit': 3,
|
'hopLimit': 3,
|
||||||
'raw': 'faked',
|
'raw': 'faked',
|
||||||
@@ -1332,16 +1339,13 @@ def test_main_onReceive_with_reply(caplog, capsys, reset_globals):
|
|||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
iface.myInfo.my_node_num = 4294967295
|
iface.myInfo.my_node_num = 4294967295
|
||||||
|
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
|
||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
main()
|
onReceive(packet, iface)
|
||||||
onReceive(send_packet, iface)
|
|
||||||
onReceive(reply_packet, iface)
|
|
||||||
assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
|
assert re.search(r'in onReceive', caplog.text, re.MULTILINE)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'got msg ', out, re.MULTILINE)
|
assert re.search(r'Sending reply', out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
mo.assert_called()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -1379,6 +1383,10 @@ fixed_position: true
|
|||||||
position_flags: 35"""
|
position_flags: 35"""
|
||||||
export_config(mo)
|
export_config(mo)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
|
|
||||||
|
# ensure we do not output this line
|
||||||
|
assert not re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
|
||||||
assert re.search(r'owner: foo', out, re.MULTILINE)
|
assert re.search(r'owner: foo', out, re.MULTILINE)
|
||||||
assert re.search(r'channel_url: bar', out, re.MULTILINE)
|
assert re.search(r'channel_url: bar', out, re.MULTILINE)
|
||||||
assert re.search(r'location:', out, re.MULTILINE)
|
assert re.search(r'location:', out, re.MULTILINE)
|
||||||
@@ -1404,7 +1412,7 @@ def test_main_export_config_called_from_main(capsys, reset_globals):
|
|||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
main()
|
main()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
assert not re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
assert re.search(r'# start of Meshtastic configure yaml', out, re.MULTILINE)
|
assert re.search(r'# start of Meshtastic configure yaml', out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
mo.assert_called()
|
mo.assert_called()
|
||||||
@@ -1522,6 +1530,36 @@ def test_main_getPref_valid_field(capsys, reset_globals):
|
|||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_main_getPref_valid_field_string(capsys, reset_globals):
|
||||||
|
"""Test getPref() with a valid field and value as a string"""
|
||||||
|
prefs = MagicMock()
|
||||||
|
prefs.DESCRIPTOR.fields_by_name.get.return_value = 'wifi_ssid'
|
||||||
|
prefs.wifi_ssid = 'foo'
|
||||||
|
prefs.ls_secs = 300
|
||||||
|
prefs.fixed_position = False
|
||||||
|
|
||||||
|
getPref(prefs, 'wifi_ssid')
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'wifi_ssid: foo', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_main_getPref_valid_field_bool(capsys, reset_globals):
|
||||||
|
"""Test getPref() with a valid field and value as a bool"""
|
||||||
|
prefs = MagicMock()
|
||||||
|
prefs.DESCRIPTOR.fields_by_name.get.return_value = 'fixed_position'
|
||||||
|
prefs.wifi_ssid = 'foo'
|
||||||
|
prefs.ls_secs = 300
|
||||||
|
prefs.fixed_position = False
|
||||||
|
|
||||||
|
getPref(prefs, 'fixed_position')
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'fixed_position: False', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_main_getPref_invalid_field(capsys, reset_globals):
|
def test_main_getPref_invalid_field(capsys, reset_globals):
|
||||||
"""Test getPref() with an invalid field"""
|
"""Test getPref() with an invalid field"""
|
||||||
@@ -1554,7 +1592,7 @@ def test_main_getPref_invalid_field(capsys, reset_globals):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_main_setPref_valid_field(capsys, reset_globals):
|
def test_main_setPref_valid_field_int(capsys, reset_globals):
|
||||||
"""Test setPref() with a valid field"""
|
"""Test setPref() with a valid field"""
|
||||||
|
|
||||||
class Field:
|
class Field:
|
||||||
@@ -1604,3 +1642,152 @@ def test_main_setPref_invalid_field(capsys, reset_globals):
|
|||||||
# ensure they are sorted
|
# ensure they are sorted
|
||||||
assert re.search(r'fixed_position\s+is_router\s+ls_secs', out, re.MULTILINE)
|
assert re.search(r'fixed_position\s+is_router\s+ls_secs', out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_main_ch_set_psk_no_ch_index(capsys, reset_globals):
|
||||||
|
"""Test --ch-set psk """
|
||||||
|
sys.argv = ['', '--ch-set', 'psk', 'foo', '--host', 'meshtastic.local']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
iface = MagicMock(autospec=TCPInterface)
|
||||||
|
with patch('meshtastic.tcp_interface.TCPInterface', return_value=iface) as mo:
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
main()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r"Warning: Need to specify '--ch-index'", out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_main_ch_set_psk_with_ch_index(capsys, reset_globals):
|
||||||
|
"""Test --ch-set psk """
|
||||||
|
sys.argv = ['', '--ch-set', 'psk', 'foo', '--host', 'meshtastic.local', '--ch-index', '0']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
iface = MagicMock(autospec=TCPInterface)
|
||||||
|
with patch('meshtastic.tcp_interface.TCPInterface', return_value=iface) as mo:
|
||||||
|
main()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r"Writing modified channels to device", out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_main_ch_set_name_with_ch_index(capsys, reset_globals):
|
||||||
|
"""Test --ch-set setting other than psk"""
|
||||||
|
sys.argv = ['', '--ch-set', 'name', 'foo', '--host', 'meshtastic.local', '--ch-index', '0']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
iface = MagicMock(autospec=TCPInterface)
|
||||||
|
with patch('meshtastic.tcp_interface.TCPInterface', return_value=iface) as mo:
|
||||||
|
main()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert re.search(r'Set name to foo', out, re.MULTILINE)
|
||||||
|
assert re.search(r"Writing modified channels to device", out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
mo.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_onNode(capsys, reset_globals):
|
||||||
|
"""Test onNode"""
|
||||||
|
onNode('foo')
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Node changed', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_tunnel_no_args(capsys, reset_globals):
|
||||||
|
"""Test tunnel no arguments"""
|
||||||
|
sys.argv = ['']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
tunnelMain()
|
||||||
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
_, err = capsys.readouterr()
|
||||||
|
assert re.search(r'usage: ', err, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('meshtastic.util.findPorts', return_value=[])
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_tunnel_tunnel_arg_with_no_devices(mock_platform_system, patched_find_ports, caplog, capsys, reset_globals):
|
||||||
|
"""Test tunnel with tunnel arg (act like we are on a linux system)"""
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
sys.argv = ['', '--tunnel']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
print(f'platform.system():{platform.system()}')
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
tunnelMain()
|
||||||
|
mock_platform_system.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=[])
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_tunnel_subnet_arg_with_no_devices(mock_platform_system, patched_find_ports, caplog, capsys, reset_globals):
|
||||||
|
"""Test tunnel with subnet arg (act like we are on a linux system)"""
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
sys.argv = ['', '--subnet', 'foo']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
print(f'platform.system():{platform.system()}')
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
tunnelMain()
|
||||||
|
mock_platform_system.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('platform.system')
|
||||||
|
def test_tunnel_tunnel_arg(mock_platform_system, caplog, reset_globals, iface_with_nodes, capsys):
|
||||||
|
"""Test tunnel with tunnel arg (act like we are on a linux system)"""
|
||||||
|
# Override the time.sleep so there is no loop
|
||||||
|
def my_sleep(amount):
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
sys.argv = ['', '--tunnel']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface):
|
||||||
|
with patch('time.sleep', side_effect=my_sleep):
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
tunnelMain()
|
||||||
|
mock_platform_system.assert_called()
|
||||||
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
|
assert pytest_wrapped_e.value.code == 3
|
||||||
|
assert re.search(r'Not starting Tunnel', caplog.text, re.MULTILINE)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Connected to radio', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ from ..mesh_interface import MeshInterface
|
|||||||
from ..node import Node
|
from ..node import Node
|
||||||
from .. import mesh_pb2
|
from .. import mesh_pb2
|
||||||
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
||||||
|
from ..radioconfig_pb2 import RadioConfig
|
||||||
|
from ..util import Timeout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -57,10 +59,8 @@ def test_MeshInterface(capsys, reset_globals):
|
|||||||
def test_getMyUser(reset_globals, iface_with_nodes):
|
def test_getMyUser(reset_globals, iface_with_nodes):
|
||||||
"""Test getMyUser()"""
|
"""Test getMyUser()"""
|
||||||
iface = iface_with_nodes
|
iface = iface_with_nodes
|
||||||
|
|
||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
myuser = iface.getMyUser()
|
myuser = iface.getMyUser()
|
||||||
print(f'myuser:{myuser}')
|
|
||||||
assert myuser is not None
|
assert myuser is not None
|
||||||
assert myuser["id"] == '!9388f81c'
|
assert myuser["id"] == '!9388f81c'
|
||||||
|
|
||||||
@@ -166,6 +166,22 @@ def test_sendPosition(reset_globals, caplog):
|
|||||||
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_close_with_heartbeatTimer(reset_globals, caplog):
|
||||||
|
"""Test close() with heartbeatTimer"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
anode = Node('foo', 'bar')
|
||||||
|
radioConfig = RadioConfig()
|
||||||
|
radioConfig.preferences.phone_timeout_secs = 10
|
||||||
|
anode.radioConfig = radioConfig
|
||||||
|
iface.localNode = anode
|
||||||
|
assert iface.heartbeatTimer is None
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
iface._startHeartbeat()
|
||||||
|
assert iface.heartbeatTimer is not None
|
||||||
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_handleFromRadio_empty_payload(reset_globals, caplog):
|
def test_handleFromRadio_empty_payload(reset_globals, caplog):
|
||||||
"""Test _handleFromRadio"""
|
"""Test _handleFromRadio"""
|
||||||
@@ -460,3 +476,155 @@ def test_generatePacketId(capsys, reset_globals):
|
|||||||
assert re.search(r'Not connected yet, can not generate packet', out, re.MULTILINE)
|
assert re.search(r'Not connected yet, can not generate packet', out, re.MULTILINE)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
assert pytest_wrapped_e.type == Exception
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_fixupPosition_empty_pos(capsys, reset_globals):
|
||||||
|
"""Test _fixupPosition()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
pos = {}
|
||||||
|
newpos = iface._fixupPosition(pos)
|
||||||
|
assert newpos == pos
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_fixupPosition_no_changes_needed(capsys, reset_globals):
|
||||||
|
"""Test _fixupPosition()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
pos = {"latitude": 101, "longitude": 102}
|
||||||
|
newpos = iface._fixupPosition(pos)
|
||||||
|
assert newpos == pos
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_fixupPosition(capsys, reset_globals):
|
||||||
|
"""Test _fixupPosition()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
pos = {"latitudeI": 1010000000, "longitudeI": 1020000000}
|
||||||
|
newpos = iface._fixupPosition(pos)
|
||||||
|
assert newpos == {"latitude": 101.0,
|
||||||
|
"latitudeI": 1010000000,
|
||||||
|
"longitude": 102.0,
|
||||||
|
"longitudeI": 1020000000}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_nodeNumToId(capsys, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _nodeNumToId()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
someid = iface._nodeNumToId(2475227164)
|
||||||
|
assert someid == '!9388f81c'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_nodeNumToId_not_found(capsys, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _nodeNumToId()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
someid = iface._nodeNumToId(123)
|
||||||
|
assert someid is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_nodeNumToId_to_all(capsys, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _nodeNumToId()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
someid = iface._nodeNumToId(0xffffffff)
|
||||||
|
assert someid == '^all'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_getOrCreateByNum_minimal(capsys, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _getOrCreateByNum()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
tmp = iface._getOrCreateByNum(123)
|
||||||
|
assert tmp == {'num': 123}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_getOrCreateByNum_not_found(capsys, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _getOrCreateByNum()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface._getOrCreateByNum(0xffffffff)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_getOrCreateByNum(capsys, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _getOrCreateByNum()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
tmp = iface._getOrCreateByNum(2475227164)
|
||||||
|
assert tmp['num'] == 2475227164
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_enter():
|
||||||
|
"""Test __enter__()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
assert iface == iface.__enter__()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_exit_with_exception(caplog):
|
||||||
|
"""Test __exit__()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
with caplog.at_level(logging.ERROR):
|
||||||
|
iface.__exit__('foo', 'bar', 'baz')
|
||||||
|
assert re.search(r'An exception of type foo with value bar has occurred', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'Traceback: baz', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_showNodes_exclude_self(capsys, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test that we hit that continue statement"""
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.localNode.nodeNum = 2475227164
|
||||||
|
iface.showNodes()
|
||||||
|
iface.showNodes(includeSelf=False)
|
||||||
|
capsys.readouterr()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitForConfig(caplog, capsys):
|
||||||
|
"""Test waitForConfig()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
# override how long to wait
|
||||||
|
iface._timeout = Timeout(0.01)
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface.waitForConfig()
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Exception: Timed out waiting for interface config', err, re.MULTILINE)
|
||||||
|
assert out == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitConnected_raises_an_exception(caplog, capsys):
|
||||||
|
"""Test waitConnected()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface.failure = "warn about something"
|
||||||
|
iface._waitConnected(0.01)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'warn about something', err, re.MULTILINE)
|
||||||
|
assert out == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitConnected_isConnected_timeout(caplog, capsys):
|
||||||
|
"""Test waitConnected()"""
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface = MeshInterface()
|
||||||
|
iface._waitConnected(0.01)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'warn about something', err, re.MULTILINE)
|
||||||
|
assert out == ''
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from ..serial_interface import SerialInterface
|
|||||||
from ..admin_pb2 import AdminMessage
|
from ..admin_pb2 import AdminMessage
|
||||||
from ..channel_pb2 import Channel
|
from ..channel_pb2 import Channel
|
||||||
from ..radioconfig_pb2 import RadioConfig
|
from ..radioconfig_pb2 import RadioConfig
|
||||||
|
from ..util import Timeout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -29,7 +30,7 @@ def test_node(capsys):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_node_reqquestConfig():
|
def test_node_requestConfig(capsys):
|
||||||
"""Test run requestConfig"""
|
"""Test run requestConfig"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
amesg = MagicMock(autospec=AdminMessage)
|
amesg = MagicMock(autospec=AdminMessage)
|
||||||
@@ -37,6 +38,9 @@ def test_node_reqquestConfig():
|
|||||||
with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
|
with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
|
||||||
anode = Node(mo, 'bar')
|
anode = Node(mo, 'bar')
|
||||||
anode.requestConfig()
|
anode.requestConfig()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Requesting preferences from remote node', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -87,6 +91,16 @@ def test_setOwner_no_short_name_and_long_name_has_words(caplog):
|
|||||||
assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
|
assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_setOwner_long_name_no_short(caplog):
|
||||||
|
"""Test setOwner"""
|
||||||
|
anode = Node('foo', 'bar', noProto=True)
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
anode.setOwner(long_name ='Aabo', is_licensed=True)
|
||||||
|
assert re.search(r'p.set_owner.long_name:Aabo:', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'p.set_owner.short_name:Aab:', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_exitSimulator(caplog):
|
def test_exitSimulator(caplog):
|
||||||
"""Test exitSimulator"""
|
"""Test exitSimulator"""
|
||||||
@@ -106,13 +120,16 @@ def test_reboot(caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_setURL_empty_url():
|
def test_setURL_empty_url(capsys):
|
||||||
"""Test reboot"""
|
"""Test reboot"""
|
||||||
anode = Node('foo', 'bar', noProto=True)
|
anode = Node('foo', 'bar', noProto=True)
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
anode.setURL('')
|
anode.setURL('')
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Warning: No RadioConfig has been read', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -133,7 +150,7 @@ def test_setURL_valid_URL(caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_setURL_valid_URL_but_no_settings(caplog):
|
def test_setURL_valid_URL_but_no_settings(caplog, capsys):
|
||||||
"""Test setURL"""
|
"""Test setURL"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
url = "https://www.meshtastic.org/d/#"
|
url = "https://www.meshtastic.org/d/#"
|
||||||
@@ -143,6 +160,9 @@ def test_setURL_valid_URL_but_no_settings(caplog):
|
|||||||
anode.setURL(url)
|
anode.setURL(url)
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Warning: There were no settings', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -623,7 +643,7 @@ def test_writeConfig(caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_requestChannel_not_localNode(caplog):
|
def test_requestChannel_not_localNode(caplog, capsys):
|
||||||
"""Test _requestChannel()"""
|
"""Test _requestChannel()"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
@@ -633,6 +653,9 @@ def test_requestChannel_not_localNode(caplog):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
anode._requestChannel(0)
|
anode._requestChannel(0)
|
||||||
assert re.search(r'Requesting channel 0 info from remote node', caplog.text, re.MULTILINE)
|
assert re.search(r'Requesting channel 0 info from remote node', caplog.text, re.MULTILINE)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Requesting channel 0 info', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -857,3 +880,14 @@ def test_onResponseRequestSetting_with_error(capsys):
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Error on response', out)
|
assert re.search(r'Error on response', out)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitForConfig():
|
||||||
|
"""Test waitForConfig()"""
|
||||||
|
anode = Node('foo', 'bar')
|
||||||
|
radioConfig = RadioConfig()
|
||||||
|
anode.radioConfig = radioConfig
|
||||||
|
anode._timeout = Timeout(0.01)
|
||||||
|
result = anode.waitForConfig()
|
||||||
|
assert not result
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ def test_watchGPIOs(caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_sendHardware_no_nodeid():
|
def test_sendHardware_no_nodeid(capsys):
|
||||||
"""Test sending no nodeid to _sendHardware()"""
|
"""Test sending no nodeid to _sendHardware()"""
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
|
||||||
@@ -87,3 +87,6 @@ def test_sendHardware_no_nodeid():
|
|||||||
rhw = RemoteHardwareClient(mo)
|
rhw = RemoteHardwareClient(mo)
|
||||||
rhw._sendHardware(None, None)
|
rhw._sendHardware(None, None)
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Warning: Must use a destination node ID', out)
|
||||||
|
assert err == ''
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from ..serial_interface import SerialInterface
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@patch('serial.Serial')
|
@patch('serial.Serial')
|
||||||
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
|
@patch('meshtastic.util.findPorts', return_value=['/dev/ttyUSBfake'])
|
||||||
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
|
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial, capsys):
|
||||||
"""Test that we can instantiate a SerialInterface with a single port"""
|
"""Test that we can instantiate a SerialInterface with a single port"""
|
||||||
iface = SerialInterface(noProto=True)
|
iface = SerialInterface(noProto=True)
|
||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
@@ -19,6 +19,12 @@ def test_SerialInterface_single_port(mocked_findPorts, mocked_serial):
|
|||||||
iface.close()
|
iface.close()
|
||||||
mocked_findPorts.assert_called()
|
mocked_findPorts.assert_called()
|
||||||
mocked_serial.assert_called()
|
mocked_serial.assert_called()
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
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 channel', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ def test_StreamInterface_with_noProto(caplog, reset_globals):
|
|||||||
assert data == test_data
|
assert data == test_data
|
||||||
|
|
||||||
|
|
||||||
# Note: This takes a bit, so moving from unit to slow
|
## Note: This takes a bit, so moving from unit to slow
|
||||||
# Tip: If you want to see the print output, run with '-s' flag:
|
## Tip: If you want to see the print output, run with '-s' flag:
|
||||||
# pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
## pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_sendToRadioImpl(caplog, reset_globals):
|
def test_sendToRadioImpl(caplog, reset_globals):
|
||||||
"""Test _sendToRadioImpl()"""
|
"""Test _sendToRadioImpl()"""
|
||||||
@@ -78,4 +78,3 @@ def test_sendToRadioImpl(caplog, reset_globals):
|
|||||||
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
assert re.search(r'reading character', caplog.text, re.MULTILINE)
|
||||||
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)
|
||||||
print(caplog.text)
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ def test_TCPInterface(capsys):
|
|||||||
"""Test that we can instantiate a TCPInterface"""
|
"""Test that we can instantiate a TCPInterface"""
|
||||||
with patch('socket.socket') as mock_socket:
|
with patch('socket.socket') as mock_socket:
|
||||||
iface = TCPInterface(hostname='localhost', noProto=True)
|
iface = TCPInterface(hostname='localhost', noProto=True)
|
||||||
|
iface.myConnect()
|
||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
iface.localNode.showInfo()
|
iface.localNode.showInfo()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
@@ -24,3 +25,11 @@ def test_TCPInterface(capsys):
|
|||||||
assert err == ''
|
assert err == ''
|
||||||
assert mock_socket.called
|
assert mock_socket.called
|
||||||
iface.close()
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_TCPInterface_without_connecting(capsys):
|
||||||
|
"""Test that we can instantiate a TCPInterface with connectNow as false"""
|
||||||
|
with patch('socket.socket'):
|
||||||
|
iface = TCPInterface(hostname='localhost', noProto=True, connectNow=False)
|
||||||
|
assert iface.socket is None
|
||||||
|
|||||||
266
meshtastic/tests/test_tunnel.py
Normal file
266
meshtastic/tests/test_tunnel.py
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
"""Meshtastic unit tests for tunnel.py"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ..tcp_interface import TCPInterface
|
||||||
|
from ..tunnel import Tunnel, onTunnelReceive
|
||||||
|
from ..globals import Globals
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_Tunnel_on_non_linux_system(mock_platform_system, reset_globals):
|
||||||
|
"""Test that we cannot instantiate a Tunnel on a non Linux system"""
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'notLinux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with patch('socket.socket') as mock_socket:
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface = TCPInterface(hostname='localhost', noProto=True)
|
||||||
|
Tunnel(iface)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
assert mock_socket.called
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_Tunnel_without_interface(mock_platform_system, reset_globals):
|
||||||
|
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
Tunnel(None)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_Tunnel_with_interface(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test that we can not instantiate a Tunnel without a valid interface"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
assert tun == Globals.getInstance().get_tunnelInstance()
|
||||||
|
iface.close()
|
||||||
|
assert re.search(r'Not creating a TapDevice()', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'Not starting TUN reader', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'Not sending packet', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test onTunnelReceive"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
sys.argv = ['']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
packet = {'decoded': { 'payload': 'foo'}, 'from': 2475227164}
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
Globals.getInstance().set_tunnelInstance(tun)
|
||||||
|
onTunnelReceive(packet, iface)
|
||||||
|
assert re.search(r'in onTunnelReceive', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'Ignoring message we sent', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_onTunnelReceive_from_someone_else(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test onTunnelReceive"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.myInfo.my_node_num = 2475227164
|
||||||
|
sys.argv = ['']
|
||||||
|
Globals.getInstance().set_args(sys.argv)
|
||||||
|
packet = {'decoded': { 'payload': 'foo'}, 'from': 123}
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
Globals.getInstance().set_tunnelInstance(tun)
|
||||||
|
onTunnelReceive(packet, iface)
|
||||||
|
assert re.search(r'in onTunnelReceive', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_random(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# random packet
|
||||||
|
packet = b'1234567890123456789012345678901234567890'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_in_blacklist(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# faked IGMP
|
||||||
|
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_icmp(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# faked ICMP
|
||||||
|
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert re.search(r'forwarding ICMP message', caplog.text, re.MULTILINE)
|
||||||
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_udp(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# faked UDP
|
||||||
|
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert re.search(r'forwarding udp', caplog.text, re.MULTILINE)
|
||||||
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_udp_blacklisted(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# faked UDP
|
||||||
|
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x6c\x07\x6c\x00\x00\x00'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
# Note: custom logging level
|
||||||
|
LOG_TRACE = 5
|
||||||
|
with caplog.at_level(LOG_TRACE):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert re.search(r'ignoring blacklisted UDP', caplog.text, re.MULTILINE)
|
||||||
|
assert ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_tcp(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# faked TCP
|
||||||
|
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert re.search(r'forwarding tcp', caplog.text, re.MULTILINE)
|
||||||
|
assert not ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_shouldFilterPacket_tcp_blacklisted(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _shouldFilterPacket()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
# faked TCP
|
||||||
|
packet = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x0c\x17\x0c\x00\x00\x00'
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
# Note: custom logging level
|
||||||
|
LOG_TRACE = 5
|
||||||
|
with caplog.at_level(LOG_TRACE):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
ignore = tun._shouldFilterPacket(packet)
|
||||||
|
assert re.search(r'ignoring blacklisted TCP', caplog.text, re.MULTILINE)
|
||||||
|
assert ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_ipToNodeId_none(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _ipToNodeId()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
nodeid = tun._ipToNodeId('something not useful')
|
||||||
|
assert nodeid is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('platform.system')
|
||||||
|
def test_ipToNodeId_all(mock_platform_system, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test _ipToNodeId()"""
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.noProto = True
|
||||||
|
a_mock = MagicMock()
|
||||||
|
a_mock.return_value = 'Linux'
|
||||||
|
mock_platform_system.side_effect = a_mock
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
with patch('socket.socket'):
|
||||||
|
tun = Tunnel(iface)
|
||||||
|
nodeid = tun._ipToNodeId(b'\x00\x00\xff\xff')
|
||||||
|
assert nodeid == '^all'
|
||||||
@@ -3,11 +3,14 @@
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
|
from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
|
||||||
support_info, genPSK256, fromStr, fromPSK,
|
support_info, genPSK256, fromStr, fromPSK,
|
||||||
quoteBooleans, catchAndIgnore)
|
quoteBooleans, catchAndIgnore,
|
||||||
|
remove_keys_from_dict, Timeout, hexstr,
|
||||||
|
ipstr, readnet_u16, findPorts, convert_mac_addr)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -38,7 +41,7 @@ def test_fromStr():
|
|||||||
assert fromStr('abc') == 'abc'
|
assert fromStr('abc') == 'abc'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unitslow
|
||||||
def test_quoteBooleans():
|
def test_quoteBooleans():
|
||||||
"""Test quoteBooleans"""
|
"""Test quoteBooleans"""
|
||||||
assert quoteBooleans('') == ''
|
assert quoteBooleans('') == ''
|
||||||
@@ -91,7 +94,7 @@ def test_pskToString_one_byte_non_zero_value():
|
|||||||
assert pskToString(bytes([0x01])) == 'default'
|
assert pskToString(bytes([0x01])) == 'default'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unitslow
|
||||||
def test_pskToString_many_bytes():
|
def test_pskToString_many_bytes():
|
||||||
"""Test pskToString many bytes"""
|
"""Test pskToString many bytes"""
|
||||||
assert pskToString(bytes([0x02, 0x01])) == 'secret'
|
assert pskToString(bytes([0x02, 0x01])) == 'secret'
|
||||||
@@ -103,20 +106,26 @@ def test_pskToString_simple():
|
|||||||
assert pskToString(bytes([0x03])) == 'simple2'
|
assert pskToString(bytes([0x03])) == 'simple2'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unitslow
|
||||||
def test_our_exit_zero_return_value():
|
def test_our_exit_zero_return_value(capsys):
|
||||||
"""Test our_exit with a zero return value"""
|
"""Test our_exit with a zero return value"""
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
our_exit("Warning: Some message", 0)
|
our_exit("Warning: Some message", 0)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Warning: Some message', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 0
|
assert pytest_wrapped_e.value.code == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_our_exit_non_zero_return_value():
|
def test_our_exit_non_zero_return_value(capsys):
|
||||||
"""Test our_exit with a non-zero return value"""
|
"""Test our_exit with a non-zero return value"""
|
||||||
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
our_exit("Error: Some message", 1)
|
our_exit("Error: Some message", 1)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Error: Some message', out, re.MULTILINE)
|
||||||
|
assert err == ''
|
||||||
assert pytest_wrapped_e.type == SystemExit
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
assert pytest_wrapped_e.value.code == 1
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
|
||||||
@@ -149,3 +158,94 @@ def test_catchAndIgnore(caplog):
|
|||||||
with caplog.at_level(logging.DEBUG):
|
with caplog.at_level(logging.DEBUG):
|
||||||
catchAndIgnore("something", some_closure)
|
catchAndIgnore("something", some_closure)
|
||||||
assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)
|
assert re.search(r'Exception thrown in something', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
def test_remove_keys_from_dict_empty_keys_empty_dict():
|
||||||
|
"""Test when keys and dict both are empty"""
|
||||||
|
assert not remove_keys_from_dict((), {})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict_empty_dict():
|
||||||
|
"""Test when dict is empty"""
|
||||||
|
assert not remove_keys_from_dict(('a'), {})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict_empty_keys():
|
||||||
|
"""Test when keys is empty"""
|
||||||
|
assert remove_keys_from_dict((), {'a':1}) == {'a':1}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict():
|
||||||
|
"""Test remove_keys_from_dict()"""
|
||||||
|
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict_multiple_keys():
|
||||||
|
"""Test remove_keys_from_dict()"""
|
||||||
|
keys = ('a', 'b')
|
||||||
|
adict = {'a': 1, 'b': 2, 'c': 3}
|
||||||
|
assert remove_keys_from_dict(keys, adict) == {'c':3}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict_nested():
|
||||||
|
"""Test remove_keys_from_dict()"""
|
||||||
|
keys = ('b')
|
||||||
|
adict = {'a': {'b': 1}, 'b': 2, 'c': 3}
|
||||||
|
exp = {'a': {}, 'c': 3}
|
||||||
|
assert remove_keys_from_dict(keys, adict) == exp
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
def test_Timeout_not_found():
|
||||||
|
"""Test Timeout()"""
|
||||||
|
to = Timeout(0.2)
|
||||||
|
attrs = ('foo')
|
||||||
|
to.waitForSet('bar', attrs)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
def test_Timeout_found():
|
||||||
|
"""Test Timeout()"""
|
||||||
|
to = Timeout(0.2)
|
||||||
|
attrs = ()
|
||||||
|
to.waitForSet('bar', attrs)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
def test_hexstr():
|
||||||
|
"""Test hexstr()"""
|
||||||
|
assert hexstr(b'123') == '31:32:33'
|
||||||
|
assert hexstr(b'') == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_ipstr():
|
||||||
|
"""Test ipstr()"""
|
||||||
|
assert ipstr(b'1234') == '49.50.51.52'
|
||||||
|
assert ipstr(b'') == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unitslow
|
||||||
|
def test_readnet_u16():
|
||||||
|
"""Test readnet_u16()"""
|
||||||
|
assert readnet_u16(b'123456', 2) == 13108
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@patch('serial.tools.list_ports.comports', return_value=[])
|
||||||
|
def test_findPorts_when_none_found(patch_comports):
|
||||||
|
"""Test findPorts()"""
|
||||||
|
assert not findPorts()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_convert_mac_addr():
|
||||||
|
"""Test convert_mac_addr()"""
|
||||||
|
assert convert_mac_addr('/c0gFyhb') == 'fd:cd:20:17:28:5b'
|
||||||
|
assert convert_mac_addr('') == ''
|
||||||
|
|||||||
@@ -17,61 +17,28 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
import platform
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
|
|
||||||
from pytap2 import TapDevice
|
from pytap2 import TapDevice
|
||||||
|
|
||||||
from . import portnums_pb2
|
from meshtastic import portnums_pb2
|
||||||
|
from meshtastic.util import ipstr, readnet_u16
|
||||||
# A new non standard log level that is lower level than DEBUG
|
from meshtastic.globals import Globals
|
||||||
LOG_TRACE = 5
|
|
||||||
|
|
||||||
# fixme - find a way to move onTunnelReceive inside of the class
|
|
||||||
tunnelInstance = None
|
|
||||||
|
|
||||||
"""A list of chatty UDP services we should never accidentally
|
|
||||||
forward to our slow network"""
|
|
||||||
udpBlacklist = {
|
|
||||||
1900, # SSDP
|
|
||||||
5353, # multicast DNS
|
|
||||||
}
|
|
||||||
|
|
||||||
"""A list of TCP services to block"""
|
|
||||||
tcpBlacklist = {}
|
|
||||||
|
|
||||||
"""A list of protocols we ignore"""
|
|
||||||
protocolBlacklist = {
|
|
||||||
0x02, # IGMP
|
|
||||||
0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def hexstr(barray):
|
|
||||||
"""Print a string of hex digits"""
|
|
||||||
return ":".join('{:02x}'.format(x) for x in barray)
|
|
||||||
|
|
||||||
|
|
||||||
def ipstr(barray):
|
|
||||||
"""Print a string of ip digits"""
|
|
||||||
return ".".join('{}'.format(x) for x in barray)
|
|
||||||
|
|
||||||
|
|
||||||
def readnet_u16(p, offset):
|
|
||||||
"""Read big endian u16 (network byte order)"""
|
|
||||||
return p[offset] * 256 + p[offset + 1]
|
|
||||||
|
|
||||||
|
|
||||||
def onTunnelReceive(packet, interface):
|
def onTunnelReceive(packet, interface):
|
||||||
"""Callback for received tunneled messages from mesh
|
"""Callback for received tunneled messages from mesh."""
|
||||||
|
logging.debug(f'in onTunnelReceive()')
|
||||||
FIXME figure out how to do closures with methods in python"""
|
our_globals = Globals.getInstance()
|
||||||
|
tunnelInstance = our_globals.get_tunnelInstance()
|
||||||
tunnelInstance.onReceive(packet)
|
tunnelInstance.onReceive(packet)
|
||||||
|
|
||||||
|
|
||||||
class Tunnel:
|
class Tunnel:
|
||||||
"""A TUN based IP tunnel over meshtastic"""
|
"""A TUN based IP tunnel over meshtastic"""
|
||||||
|
|
||||||
def __init__(self, iface, subnet=None, netmask="255.255.0.0"):
|
def __init__(self, iface, subnet='10.115', netmask="255.255.0.0"):
|
||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
|
|
||||||
@@ -79,35 +46,69 @@ class Tunnel:
|
|||||||
subnet is used to construct our network number (normally 10.115.x.x)
|
subnet is used to construct our network number (normally 10.115.x.x)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if subnet is None:
|
if not iface:
|
||||||
subnet = "10.115"
|
raise Exception("Tunnel() must have a interface")
|
||||||
|
|
||||||
self.iface = iface
|
self.iface = iface
|
||||||
self.subnetPrefix = subnet
|
self.subnetPrefix = subnet
|
||||||
|
|
||||||
global tunnelInstance
|
if platform.system() != 'Linux':
|
||||||
tunnelInstance = self
|
raise Exception("Tunnel() can only be run instantiated on a Linux system")
|
||||||
|
|
||||||
|
our_globals = Globals.getInstance()
|
||||||
|
our_globals.set_tunnelInstance(self)
|
||||||
|
|
||||||
|
"""A list of chatty UDP services we should never accidentally
|
||||||
|
forward to our slow network"""
|
||||||
|
self.udpBlacklist = {
|
||||||
|
1900, # SSDP
|
||||||
|
5353, # multicast DNS
|
||||||
|
}
|
||||||
|
|
||||||
|
"""A list of TCP services to block"""
|
||||||
|
self.tcpBlacklist = {
|
||||||
|
5900, # VNC (Note: Only adding for testing purposes.)
|
||||||
|
}
|
||||||
|
|
||||||
|
"""A list of protocols we ignore"""
|
||||||
|
self.protocolBlacklist = {
|
||||||
|
0x02, # IGMP
|
||||||
|
0x80, # Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment
|
||||||
|
}
|
||||||
|
|
||||||
|
# A new non standard log level that is lower level than DEBUG
|
||||||
|
self.LOG_TRACE = 5
|
||||||
|
|
||||||
|
# TODO: check if root?
|
||||||
logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\
|
logging.info("Starting IP to mesh tunnel (you must be root for this *pre-alpha* "\
|
||||||
"feature to work). Mesh members:")
|
"feature to work). Mesh members:")
|
||||||
|
|
||||||
pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP")
|
pub.subscribe(onTunnelReceive, "meshtastic.receive.data.IP_TUNNEL_APP")
|
||||||
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
myAddr = self._nodeNumToIp(self.iface.myInfo.my_node_num)
|
||||||
|
|
||||||
for node in self.iface.nodes.values():
|
if self.iface.nodes:
|
||||||
nodeId = node["user"]["id"]
|
for node in self.iface.nodes.values():
|
||||||
ip = self._nodeNumToIp(node["num"])
|
nodeId = node["user"]["id"]
|
||||||
logging.info(f"Node { nodeId } has IP address { ip }")
|
ip = self._nodeNumToIp(node["num"])
|
||||||
|
logging.info(f"Node { nodeId } has IP address { ip }")
|
||||||
|
|
||||||
logging.debug("creating TUN device with MTU=200")
|
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
|
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
|
||||||
self.tun = TapDevice(name="mesh")
|
self.tun = None
|
||||||
self.tun.up()
|
if self.iface.noProto:
|
||||||
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
|
logging.warning(f"Not creating a TapDevice() because it is disabled by noProto")
|
||||||
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
else:
|
||||||
self._rxThread = threading.Thread(
|
self.tun = TapDevice(name="mesh")
|
||||||
target=self.__tunReader, args=(), daemon=True)
|
self.tun.up()
|
||||||
self._rxThread.start()
|
self.tun.ifconfig(address=myAddr, netmask=netmask, mtu=200)
|
||||||
|
|
||||||
|
self._rxThread = None
|
||||||
|
if self.iface.noProto:
|
||||||
|
logging.warning(f"Not starting TUN reader because it is disabled by noProto")
|
||||||
|
else:
|
||||||
|
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
|
||||||
|
self._rxThread = threading.Thread(target=self.__tunReader, args=(), daemon=True)
|
||||||
|
self._rxThread.start()
|
||||||
|
|
||||||
def onReceive(self, packet):
|
def onReceive(self, packet):
|
||||||
"""onReceive"""
|
"""onReceive"""
|
||||||
@@ -115,12 +116,12 @@ class Tunnel:
|
|||||||
if packet["from"] == self.iface.myInfo.my_node_num:
|
if packet["from"] == self.iface.myInfo.my_node_num:
|
||||||
logging.debug("Ignoring message we sent")
|
logging.debug("Ignoring message we sent")
|
||||||
else:
|
else:
|
||||||
logging.debug(
|
logging.debug(f"Received mesh tunnel message type={type(p)} len={len(p)}")
|
||||||
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),
|
# 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
|
# but this provides useful debug printing on types of packets received
|
||||||
if not self._shouldFilterPacket(p):
|
if not self.iface.noProto:
|
||||||
self.tun.write(p)
|
if not self._shouldFilterPacket(p):
|
||||||
|
self.tun.write(p)
|
||||||
|
|
||||||
def _shouldFilterPacket(self, p):
|
def _shouldFilterPacket(self, p):
|
||||||
"""Given a packet, decode it and return true if it should be ignored"""
|
"""Given a packet, decode it and return true if it should be ignored"""
|
||||||
@@ -129,10 +130,9 @@ class Tunnel:
|
|||||||
destAddr = p[16:20]
|
destAddr = p[16:20]
|
||||||
subheader = 20
|
subheader = 20
|
||||||
ignore = False # Assume we will be forwarding the packet
|
ignore = False # Assume we will be forwarding the packet
|
||||||
if protocol in protocolBlacklist:
|
if protocol in self.protocolBlacklist:
|
||||||
ignore = True
|
ignore = True
|
||||||
logging.log(
|
logging.log(self.LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
||||||
LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}")
|
|
||||||
elif protocol == 0x01: # ICMP
|
elif protocol == 0x01: # ICMP
|
||||||
icmpType = p[20]
|
icmpType = p[20]
|
||||||
icmpCode = p[21]
|
icmpCode = p[21]
|
||||||
@@ -145,19 +145,17 @@ class Tunnel:
|
|||||||
elif protocol == 0x11: # UDP
|
elif protocol == 0x11: # UDP
|
||||||
srcport = readnet_u16(p, subheader)
|
srcport = readnet_u16(p, subheader)
|
||||||
destport = readnet_u16(p, subheader + 2)
|
destport = readnet_u16(p, subheader + 2)
|
||||||
if destport in udpBlacklist:
|
if destport in self.udpBlacklist:
|
||||||
ignore = True
|
ignore = True
|
||||||
logging.log(
|
logging.log(self.LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
|
||||||
LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
|
|
||||||
else:
|
else:
|
||||||
logging.debug(
|
logging.debug(f"forwarding udp srcport={srcport}, destport={destport}")
|
||||||
f"forwarding udp srcport={srcport}, destport={destport}")
|
|
||||||
elif protocol == 0x06: # TCP
|
elif protocol == 0x06: # TCP
|
||||||
srcport = readnet_u16(p, subheader)
|
srcport = readnet_u16(p, subheader)
|
||||||
destport = readnet_u16(p, subheader + 2)
|
destport = readnet_u16(p, subheader + 2)
|
||||||
if destport in tcpBlacklist:
|
if destport in self.tcpBlacklist:
|
||||||
ignore = True
|
ignore = True
|
||||||
logging.log(LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
logging.log(self.LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
|
||||||
else:
|
else:
|
||||||
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
|
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import traceback
|
|||||||
from queue import Queue
|
from queue import Queue
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import base64
|
||||||
import time
|
import time
|
||||||
import platform
|
import platform
|
||||||
import logging
|
import logging
|
||||||
@@ -169,8 +170,7 @@ class DeferredExecution():
|
|||||||
o = self.queue.get()
|
o = self.queue.get()
|
||||||
o()
|
o()
|
||||||
except:
|
except:
|
||||||
logging.error(
|
logging.error(f"Unexpected error in deferred execution {sys.exc_info()[0]}")
|
||||||
f"Unexpected error in deferred execution {sys.exc_info()[0]}")
|
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
@@ -201,3 +201,42 @@ def support_info():
|
|||||||
platform.python_implementation(), platform.python_compiler()))
|
platform.python_implementation(), platform.python_compiler()))
|
||||||
print('')
|
print('')
|
||||||
print('Please add the output from the command: meshtastic --info')
|
print('Please add the output from the command: meshtastic --info')
|
||||||
|
|
||||||
|
|
||||||
|
def remove_keys_from_dict(keys, adict):
|
||||||
|
"""Return a dictionary without some keys in it.
|
||||||
|
Will removed nested keys.
|
||||||
|
"""
|
||||||
|
for key in keys:
|
||||||
|
try:
|
||||||
|
del adict[key]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
for val in adict.values():
|
||||||
|
if isinstance(val, dict):
|
||||||
|
remove_keys_from_dict(keys, val)
|
||||||
|
return adict
|
||||||
|
|
||||||
|
|
||||||
|
def hexstr(barray):
|
||||||
|
"""Print a string of hex digits"""
|
||||||
|
return ":".join('{:02x}'.format(x) for x in barray)
|
||||||
|
|
||||||
|
|
||||||
|
def ipstr(barray):
|
||||||
|
"""Print a string of ip digits"""
|
||||||
|
return ".".join('{}'.format(x) for x in barray)
|
||||||
|
|
||||||
|
|
||||||
|
def readnet_u16(p, offset):
|
||||||
|
"""Read big endian u16 (network byte order)"""
|
||||||
|
return p[offset] * 256 + p[offset + 1]
|
||||||
|
|
||||||
|
|
||||||
|
def convert_mac_addr(val):
|
||||||
|
"""Convert the base 64 encoded value to a mac address
|
||||||
|
val - base64 encoded value (ex: '/c0gFyhb'))
|
||||||
|
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
|
||||||
|
"""
|
||||||
|
val_as_bytes = base64.b64decode(val)
|
||||||
|
return hexstr(val_as_bytes)
|
||||||
|
|||||||
2
proto
2
proto
Submodule proto updated: 1d3b4806ab...7b80bde421
5
setup.py
5
setup.py
@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
|
|||||||
# This call to setup() does all the work
|
# This call to setup() does all the work
|
||||||
setup(
|
setup(
|
||||||
name="meshtastic",
|
name="meshtastic",
|
||||||
version="1.2.47",
|
version="1.2.52",
|
||||||
description="Python API & client shell for talking to Meshtastic devices",
|
description="Python API & client shell for talking to Meshtastic devices",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
@@ -23,7 +23,10 @@ setup(
|
|||||||
classifiers=[
|
classifiers=[
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
],
|
],
|
||||||
packages=["meshtastic"],
|
packages=["meshtastic"],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|||||||
5
vercel.json
Normal file
5
vercel.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"github": {
|
||||||
|
"silent": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user