mirror of
https://github.com/meshtastic/python.git
synced 2026-01-02 12:57:56 -05:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4630b53eb | ||
|
|
646aa981d5 | ||
|
|
9381acd6ac | ||
|
|
384063db19 | ||
|
|
20d75d9023 | ||
|
|
0deb1d788f | ||
|
|
1070d9202b | ||
|
|
b90de8b73b | ||
|
|
2e79ecf759 | ||
|
|
578d3e4b24 | ||
|
|
4ca13bcede | ||
|
|
6ceae7c72f | ||
|
|
4c29d7dd0f | ||
|
|
839bbbcad2 | ||
|
|
1abb9fb213 | ||
|
|
7fcbbe9b80 | ||
|
|
073274cb00 | ||
|
|
92a3986a8f | ||
|
|
f08ec1885b | ||
|
|
feca49faed | ||
|
|
dfaf1a275d | ||
|
|
da7fa31805 | ||
|
|
12fd29b203 | ||
|
|
a43dd201ba | ||
|
|
a64a9d203a | ||
|
|
10136962d7 | ||
|
|
3afb294f9b | ||
|
|
ee405fec41 | ||
|
|
3eabaf91d0 | ||
|
|
78b92cecc9 | ||
|
|
7088b90514 | ||
|
|
2ae81f8602 | ||
|
|
923f5e82d0 | ||
|
|
05731128fa | ||
|
|
0523d4c94f | ||
|
|
90e901de79 | ||
|
|
6606851135 | ||
|
|
33fecbd74d | ||
|
|
6b9db7abd9 | ||
|
|
ece6286d82 | ||
|
|
e335f12a3b | ||
|
|
0da405168f | ||
|
|
58d9039a04 | ||
|
|
f77e788aa8 | ||
|
|
aba381fb54 | ||
|
|
0bb4b31b6a | ||
|
|
915066e0af | ||
|
|
6be3969577 | ||
|
|
b73cc1f499 | ||
|
|
65305af184 | ||
|
|
3fb1e67357 | ||
|
|
cbd3c119fe | ||
|
|
bbd6d6a541 | ||
|
|
6e1217c7ca | ||
|
|
81db38956b | ||
|
|
27729995d2 | ||
|
|
d875a574b6 | ||
|
|
40019a9712 | ||
|
|
de657bab24 | ||
|
|
40353a387e | ||
|
|
9949d144a1 | ||
|
|
48a06c6e1e | ||
|
|
c696d59b90 | ||
|
|
4fdbcb9679 | ||
|
|
5d6dfb877b | ||
|
|
951edfe27b | ||
|
|
5cc9627e21 | ||
|
|
bf904c6906 | ||
|
|
2026212a00 | ||
|
|
34f9be255e | ||
|
|
e561222ea7 | ||
|
|
73a1bbc7d5 | ||
|
|
8f2c397fbf | ||
|
|
62f5201a38 | ||
|
|
9e7d5e96ab | ||
|
|
aa74db46cb | ||
|
|
1967519deb | ||
|
|
662aea049a | ||
|
|
44cfd72a80 | ||
|
|
abe1dd47ca | ||
|
|
584a14f578 | ||
|
|
8ba92da7cf | ||
|
|
3811226a61 | ||
|
|
b6547c9737 | ||
|
|
9612aea9b9 | ||
|
|
60de9dddb1 | ||
|
|
a29ee840f2 |
@@ -16,7 +16,7 @@ Events are delivered using a publish-subscribe model, and you can subscribe to o
|
|||||||
|
|
||||||
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
|
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
|
||||||
|
|
||||||
(Documentation/API Reference is currently offline)
|
**[API Documentation](https://python.meshtastic.org)**
|
||||||
|
|
||||||
## Call for Contributors
|
## Call for Contributors
|
||||||
|
|
||||||
|
|||||||
@@ -2,41 +2,44 @@
|
|||||||
# A library for the Meshtastic Client API
|
# A library for the Meshtastic Client API
|
||||||
|
|
||||||
Primary interfaces: SerialInterface, TCPInterface, BLEInterface
|
Primary interfaces: SerialInterface, TCPInterface, BLEInterface
|
||||||
|
|
||||||
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
||||||
|
|
||||||
Source code on [github](https://github.com/meshtastic/python)
|
Source code on [github](https://github.com/meshtastic/python)
|
||||||
|
|
||||||
notable properties of interface classes:
|
notable properties of interface classes:
|
||||||
|
|
||||||
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
|
- `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.
|
node in the mesh. This is a read-only datastructure.
|
||||||
- nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId
|
- `nodesByNum` - like "nodes" but keyed by nodeNum instead of nodeId. As such, includes "unknown" nodes which haven't seen a User packet yet
|
||||||
- myInfo & metadata - Contain read-only information about the local radio device (software version, hardware version, etc)
|
- `myInfo` & `metadata` - Contain read-only information about the local radio device (software version, hardware version, etc)
|
||||||
- localNode - Pointer to a node object for the local node
|
- `localNode` - Pointer to a node object for the local node
|
||||||
|
|
||||||
notable properties of nodes:
|
notable properties of nodes:
|
||||||
- localConfig - Current radio settings, can be written to the radio with the `writeConfig` method.
|
|
||||||
- moduleConfig - Current module settings, can be written to the radio with the `writeConfig` method.
|
- `localConfig` - Current radio settings, can be written to the radio with the `writeConfig` method.
|
||||||
- channels - The node's channels, keyed by index.
|
- `moduleConfig` - Current module settings, can be written to the radio with the `writeConfig` method.
|
||||||
|
- `channels` - The node's channels, keyed by index.
|
||||||
|
|
||||||
# Published PubSub topics
|
# Published PubSub topics
|
||||||
|
|
||||||
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
|
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
|
||||||
topics:
|
topics:
|
||||||
|
|
||||||
- meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB
|
- `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.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
|
- `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".
|
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.position(packet)`
|
||||||
- meshtastic.receive.user(packet)
|
- `meshtastic.receive.user(packet)`
|
||||||
- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
|
- `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...)
|
- `meshtastic.node.updated(node = NodeInfo)` - published when a node in the DB changes (appears, location changed, username changed, etc...)
|
||||||
- meshtastic.log.line(line) - a raw unparsed log line from the radio
|
- `meshtastic.log.line(line)` - a raw unparsed log line from the radio
|
||||||
|
|
||||||
We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for
|
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
|
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
|
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
|
`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.
|
unicode scripts they can be different.
|
||||||
|
|
||||||
# Example Usage
|
# Example Usage
|
||||||
@@ -108,13 +111,13 @@ from . import (
|
|||||||
LOCAL_ADDR = "^local"
|
LOCAL_ADDR = "^local"
|
||||||
"""A special ID that means the local node"""
|
"""A special ID that means the local node"""
|
||||||
|
|
||||||
BROADCAST_NUM = 0xFFFFFFFF
|
BROADCAST_NUM: int = 0xFFFFFFFF
|
||||||
"""if using 8 bit nodenums this will be shortened on the target"""
|
"""if using 8 bit nodenums this will be shortened on the target"""
|
||||||
|
|
||||||
BROADCAST_ADDR = "^all"
|
BROADCAST_ADDR = "^all"
|
||||||
"""A special ID that means broadcast"""
|
"""A special ID that means broadcast"""
|
||||||
|
|
||||||
OUR_APP_VERSION = 20300
|
OUR_APP_VERSION: int = 20300
|
||||||
"""The numeric buildnumber (shared with android apps) specifying the
|
"""The numeric buildnumber (shared with android apps) specifying the
|
||||||
level of device code we are guaranteed to understand
|
level of device code we are guaranteed to understand
|
||||||
|
|
||||||
@@ -131,7 +134,9 @@ class ResponseHandler(NamedTuple):
|
|||||||
"""A pending response callback, waiting for a response to one of our messages"""
|
"""A pending response callback, waiting for a response to one of our messages"""
|
||||||
|
|
||||||
# requestId: int - used only as a key
|
# requestId: int - used only as a key
|
||||||
|
#: a callable to call when a response is received
|
||||||
callback: Callable
|
callback: Callable
|
||||||
|
#: Whether ACKs and NAKs should be passed to this handler
|
||||||
ackPermitted: bool = False
|
ackPermitted: bool = False
|
||||||
# FIXME, add timestamp and age out old requests
|
# FIXME, add timestamp and age out old requests
|
||||||
|
|
||||||
@@ -139,11 +144,11 @@ class ResponseHandler(NamedTuple):
|
|||||||
class KnownProtocol(NamedTuple):
|
class KnownProtocol(NamedTuple):
|
||||||
"""Used to automatically decode known protocol payloads"""
|
"""Used to automatically decode known protocol payloads"""
|
||||||
|
|
||||||
|
#: A descriptive name (e.g. "text", "user", "admin")
|
||||||
name: str
|
name: str
|
||||||
# portnum: int, now a key
|
#: If set, will be called to parse as a protocol buffer
|
||||||
# If set, will be called to prase as a protocol buffer
|
|
||||||
protobufFactory: Optional[Callable] = None
|
protobufFactory: Optional[Callable] = None
|
||||||
# If set, invoked as onReceive(interface, packet)
|
#: If set, invoked as onReceive(interface, packet)
|
||||||
onReceive: Optional[Callable] = None
|
onReceive: Optional[Callable] = None
|
||||||
|
|
||||||
|
|
||||||
@@ -194,13 +199,31 @@ def _onNodeInfoReceive(iface, asDict):
|
|||||||
def _onTelemetryReceive(iface, asDict):
|
def _onTelemetryReceive(iface, asDict):
|
||||||
"""Automatically update device metrics on received packets"""
|
"""Automatically update device metrics on received packets"""
|
||||||
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
||||||
deviceMetrics = asDict.get("decoded", {}).get("telemetry", {}).get("deviceMetrics")
|
if "from" not in asDict:
|
||||||
if "from" in asDict and deviceMetrics is not None:
|
return
|
||||||
node = iface._getOrCreateByNum(asDict["from"])
|
|
||||||
newMetrics = node.get("deviceMetrics", {})
|
toUpdate = None
|
||||||
newMetrics.update(deviceMetrics)
|
|
||||||
logging.debug(f"updating metrics for {asDict['from']} to {newMetrics}")
|
telemetry = asDict.get("decoded", {}).get("telemetry", {})
|
||||||
node["deviceMetrics"] = newMetrics
|
node = iface._getOrCreateByNum(asDict["from"])
|
||||||
|
if "deviceMetrics" in telemetry:
|
||||||
|
toUpdate = "deviceMetrics"
|
||||||
|
elif "environmentMetrics" in telemetry:
|
||||||
|
toUpdate = "environmentMetrics"
|
||||||
|
elif "airQualityMetrics" in telemetry:
|
||||||
|
toUpdate = "airQualityMetrics"
|
||||||
|
elif "powerMetrics" in telemetry:
|
||||||
|
toUpdate = "powerMetrics"
|
||||||
|
elif "localStats" in telemetry:
|
||||||
|
toUpdate = "localStats"
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
updateObj = telemetry.get(toUpdate)
|
||||||
|
newMetrics = node.get(toUpdate, {})
|
||||||
|
newMetrics.update(updateObj)
|
||||||
|
logging.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
|
||||||
|
node[toUpdate] = newMetrics
|
||||||
|
|
||||||
def _receiveInfoUpdate(iface, asDict):
|
def _receiveInfoUpdate(iface, asDict):
|
||||||
if "from" in asDict:
|
if "from" in asDict:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import atexit
|
|||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
|
import io
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
@@ -34,9 +35,9 @@ class BLEInterface(MeshInterface):
|
|||||||
self,
|
self,
|
||||||
address: Optional[str],
|
address: Optional[str],
|
||||||
noProto: bool = False,
|
noProto: bool = False,
|
||||||
debugOut=None,
|
debugOut: Optional[io.TextIOWrapper]=None,
|
||||||
noNodes: bool = False,
|
noNodes: bool = False,
|
||||||
):
|
) -> None:
|
||||||
MeshInterface.__init__(
|
MeshInterface.__init__(
|
||||||
self, debugOut=debugOut, noProto=noProto, noNodes=noNodes
|
self, debugOut=debugOut, noProto=noProto, noNodes=noNodes
|
||||||
)
|
)
|
||||||
@@ -82,7 +83,7 @@ class BLEInterface(MeshInterface):
|
|||||||
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
|
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
|
||||||
self._exit_handler = atexit.register(self.client.disconnect)
|
self._exit_handler = atexit.register(self.client.disconnect)
|
||||||
|
|
||||||
def from_num_handler(self, _, b): # pylint: disable=C0116
|
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
|
||||||
"""Handle callbacks for fromnum notify.
|
"""Handle callbacks for fromnum notify.
|
||||||
Note: this method does not need to be async because it is just setting a bool.
|
Note: this method does not need to be async because it is just setting a bool.
|
||||||
"""
|
"""
|
||||||
@@ -150,9 +151,12 @@ class BLEInterface(MeshInterface):
|
|||||||
)
|
)
|
||||||
return addressed_devices[0]
|
return addressed_devices[0]
|
||||||
|
|
||||||
def _sanitize_address(address): # pylint: disable=E0213
|
def _sanitize_address(self, address: Optional[str]) -> Optional[str]: # pylint: disable=E0213
|
||||||
"Standardize BLE address by removing extraneous characters and lowercasing."
|
"Standardize BLE address by removing extraneous characters and lowercasing."
|
||||||
return address.replace("-", "").replace("_", "").replace(":", "").lower()
|
if address is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return address.replace("-", "").replace("_", "").replace(":", "").lower()
|
||||||
|
|
||||||
def connect(self, address: Optional[str] = None) -> "BLEClient":
|
def connect(self, address: Optional[str] = None) -> "BLEClient":
|
||||||
"Connect to a device by address."
|
"Connect to a device by address."
|
||||||
@@ -164,12 +168,16 @@ class BLEInterface(MeshInterface):
|
|||||||
client.discover()
|
client.discover()
|
||||||
return client
|
return client
|
||||||
|
|
||||||
def _receiveFromRadioImpl(self):
|
def _receiveFromRadioImpl(self) -> None:
|
||||||
while self._want_receive:
|
while self._want_receive:
|
||||||
if self.should_read:
|
if self.should_read:
|
||||||
self.should_read = False
|
self.should_read = False
|
||||||
retries = 0
|
retries: int = 0
|
||||||
while self._want_receive:
|
while self._want_receive:
|
||||||
|
if self.client is None:
|
||||||
|
logging.debug(f"BLE client is None, shutting down")
|
||||||
|
self._want_receive = False
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
b = bytes(self.client.read_gatt_char(FROMRADIO_UUID))
|
b = bytes(self.client.read_gatt_char(FROMRADIO_UUID))
|
||||||
except BleakDBusError as e:
|
except BleakDBusError as e:
|
||||||
@@ -194,8 +202,8 @@ class BLEInterface(MeshInterface):
|
|||||||
else:
|
else:
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
def _sendToRadioImpl(self, toRadio) -> None:
|
||||||
b = toRadio.SerializeToString()
|
b: bytes = toRadio.SerializeToString()
|
||||||
if b and self.client: # we silently ignore writes while we are shutting down
|
if b and self.client: # we silently ignore writes while we are shutting down
|
||||||
logging.debug(f"TORADIO write: {b.hex()}")
|
logging.debug(f"TORADIO write: {b.hex()}")
|
||||||
try:
|
try:
|
||||||
@@ -211,14 +219,14 @@ class BLEInterface(MeshInterface):
|
|||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
self.should_read = True
|
self.should_read = True
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
try:
|
try:
|
||||||
MeshInterface.close(self)
|
MeshInterface.close(self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error closing mesh interface: {e}")
|
logging.error(f"Error closing mesh interface: {e}")
|
||||||
|
|
||||||
if self._want_receive:
|
if self._want_receive:
|
||||||
self.want_receive = False # Tell the thread we want it to stop
|
self._want_receive = False # Tell the thread we want it to stop
|
||||||
if self._receiveThread:
|
if self._receiveThread:
|
||||||
self._receiveThread.join(
|
self._receiveThread.join(
|
||||||
timeout=2
|
timeout=2
|
||||||
@@ -230,12 +238,13 @@ class BLEInterface(MeshInterface):
|
|||||||
self.client.disconnect()
|
self.client.disconnect()
|
||||||
self.client.close()
|
self.client.close()
|
||||||
self.client = None
|
self.client = None
|
||||||
|
self._disconnected() # send the disconnected indicator up to clients
|
||||||
|
|
||||||
|
|
||||||
class BLEClient:
|
class BLEClient:
|
||||||
"""Client for managing connection to a BLE device"""
|
"""Client for managing connection to a BLE device"""
|
||||||
|
|
||||||
def __init__(self, address=None, **kwargs):
|
def __init__(self, address=None, **kwargs) -> None:
|
||||||
self._eventLoop = asyncio.new_event_loop()
|
self._eventLoop = asyncio.new_event_loop()
|
||||||
self._eventThread = Thread(
|
self._eventThread = Thread(
|
||||||
target=self._run_event_loop, name="BLEClient", daemon=True
|
target=self._run_event_loop, name="BLEClient", daemon=True
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Mesh Interface class
|
"""Mesh Interface class
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
@@ -8,6 +9,7 @@ import random
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Any, Callable, Dict, List, Optional, Union
|
from typing import Any, Callable, Dict, List, Optional, Union
|
||||||
@@ -139,13 +141,13 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, trace):
|
||||||
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 trace is not None:
|
||||||
logging.error(f"Traceback: {traceback}")
|
logging.error(f"Traceback: {trace}")
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -315,19 +317,33 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
return table
|
return table
|
||||||
|
|
||||||
def getNode(
|
def getNode(
|
||||||
self, nodeId: str, requestChannels: bool = True
|
self, nodeId: str, requestChannels: bool = True, requestChannelAttempts: int = 3, timeout: int = 300
|
||||||
) -> meshtastic.node.Node:
|
) -> meshtastic.node.Node:
|
||||||
"""Return a node object which contains device settings and channel info"""
|
"""Return a node object which contains device settings and channel info"""
|
||||||
if nodeId in (LOCAL_ADDR, BROADCAST_ADDR):
|
if nodeId in (LOCAL_ADDR, BROADCAST_ADDR):
|
||||||
return self.localNode
|
return self.localNode
|
||||||
else:
|
else:
|
||||||
n = meshtastic.node.Node(self, nodeId)
|
n = meshtastic.node.Node(self, nodeId, timeout=timeout)
|
||||||
# Only request device settings and channel info when necessary
|
# Only request device settings and channel info when necessary
|
||||||
if requestChannels:
|
if requestChannels:
|
||||||
logging.debug("About to requestChannels")
|
logging.debug("About to requestChannels")
|
||||||
n.requestChannels()
|
n.requestChannels()
|
||||||
if not n.waitForConfig():
|
retries_left = requestChannelAttempts
|
||||||
our_exit("Error: Timed out waiting for channels")
|
last_index: int = 0
|
||||||
|
while retries_left > 0:
|
||||||
|
retries_left -= 1
|
||||||
|
if not n.waitForConfig():
|
||||||
|
new_index: int = len(n.partialChannels) if n.partialChannels else 0
|
||||||
|
# each time we get a new channel, reset the counter
|
||||||
|
if new_index != last_index:
|
||||||
|
retries_left = requestChannelAttempts - 1
|
||||||
|
if retries_left <= 0:
|
||||||
|
our_exit(f"Error: Timed out waiting for channels, giving up")
|
||||||
|
print("Timed out trying to retrieve channel info, retrying")
|
||||||
|
n.requestChannels(startingIndex=new_index)
|
||||||
|
last_index = new_index
|
||||||
|
else:
|
||||||
|
break
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def sendText(
|
def sendText(
|
||||||
@@ -380,7 +396,9 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
onResponseAckPermitted: bool=False,
|
onResponseAckPermitted: bool=False,
|
||||||
channelIndex: int=0,
|
channelIndex: int=0,
|
||||||
hopLimit: Optional[int]=None,
|
hopLimit: Optional[int]=None,
|
||||||
):
|
pkiEncrypted: Optional[bool]=False,
|
||||||
|
publicKey: Optional[bytes]=None,
|
||||||
|
): # pylint: disable=R0913
|
||||||
"""Send a data packet to some other node
|
"""Send a data packet to some other node
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
@@ -435,7 +453,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
if onResponse is not None:
|
if onResponse is not None:
|
||||||
logging.debug(f"Setting a response handler for requestId {meshPacket.id}")
|
logging.debug(f"Setting a response handler for requestId {meshPacket.id}")
|
||||||
self._addResponseHandler(meshPacket.id, onResponse, ackPermitted=onResponseAckPermitted)
|
self._addResponseHandler(meshPacket.id, onResponse, ackPermitted=onResponseAckPermitted)
|
||||||
p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit)
|
p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck, hopLimit=hopLimit, pkiEncrypted=pkiEncrypted, publicKey=publicKey)
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def sendPosition(
|
def sendPosition(
|
||||||
@@ -588,32 +606,38 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
|
telemetryType: str = "device_metrics"
|
||||||
):
|
):
|
||||||
"""Send telemetry and optionally ask for a response"""
|
"""Send telemetry and optionally ask for a response"""
|
||||||
r = telemetry_pb2.Telemetry()
|
r = telemetry_pb2.Telemetry()
|
||||||
|
|
||||||
if self.nodes is not None:
|
if telemetryType == "environment_metrics":
|
||||||
node = next(
|
r.environment_metrics.CopyFrom(telemetry_pb2.EnvironmentMetrics())
|
||||||
n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum
|
elif telemetryType == "air_quality_metrics":
|
||||||
)
|
r.air_quality_metrics.CopyFrom(telemetry_pb2.AirQualityMetrics())
|
||||||
if node is not None:
|
elif telemetryType == "power_metrics":
|
||||||
metrics = node.get("deviceMetrics")
|
r.power_metrics.CopyFrom(telemetry_pb2.PowerMetrics())
|
||||||
if metrics:
|
else: # fall through to device metrics
|
||||||
batteryLevel = metrics.get("batteryLevel")
|
if self.nodesByNum is not None:
|
||||||
if batteryLevel is not None:
|
node = self.nodesByNum.get(self.localNode.nodeNum)
|
||||||
r.device_metrics.battery_level = batteryLevel
|
if node is not None:
|
||||||
voltage = metrics.get("voltage")
|
metrics = node.get("deviceMetrics")
|
||||||
if voltage is not None:
|
if metrics:
|
||||||
r.device_metrics.voltage = voltage
|
batteryLevel = metrics.get("batteryLevel")
|
||||||
channel_utilization = metrics.get("channelUtilization")
|
if batteryLevel is not None:
|
||||||
if channel_utilization is not None:
|
r.device_metrics.battery_level = batteryLevel
|
||||||
r.device_metrics.channel_utilization = channel_utilization
|
voltage = metrics.get("voltage")
|
||||||
air_util_tx = metrics.get("airUtilTx")
|
if voltage is not None:
|
||||||
if air_util_tx is not None:
|
r.device_metrics.voltage = voltage
|
||||||
r.device_metrics.air_util_tx = air_util_tx
|
channel_utilization = metrics.get("channelUtilization")
|
||||||
uptime_seconds = metrics.get("uptimeSeconds")
|
if channel_utilization is not None:
|
||||||
if uptime_seconds is not None:
|
r.device_metrics.channel_utilization = channel_utilization
|
||||||
r.device_metrics.uptime_seconds = uptime_seconds
|
air_util_tx = metrics.get("airUtilTx")
|
||||||
|
if air_util_tx is not None:
|
||||||
|
r.device_metrics.air_util_tx = air_util_tx
|
||||||
|
uptime_seconds = metrics.get("uptimeSeconds")
|
||||||
|
if uptime_seconds is not None:
|
||||||
|
r.device_metrics.uptime_seconds = uptime_seconds
|
||||||
|
|
||||||
if wantResponse:
|
if wantResponse:
|
||||||
onResponse = self.onResponseTelemetry
|
onResponse = self.onResponseTelemetry
|
||||||
@@ -637,22 +661,32 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
self._acknowledgment.receivedTelemetry = True
|
self._acknowledgment.receivedTelemetry = True
|
||||||
telemetry = telemetry_pb2.Telemetry()
|
telemetry = telemetry_pb2.Telemetry()
|
||||||
telemetry.ParseFromString(p["decoded"]["payload"])
|
telemetry.ParseFromString(p["decoded"]["payload"])
|
||||||
|
|
||||||
print("Telemetry received:")
|
print("Telemetry received:")
|
||||||
if telemetry.device_metrics.battery_level is not None:
|
# Check if the telemetry message has the device_metrics field
|
||||||
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
|
# This is the original code that was the default for --request-telemetry and is kept for compatibility
|
||||||
if telemetry.device_metrics.voltage is not None:
|
if telemetry.HasField("device_metrics"):
|
||||||
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
|
if telemetry.device_metrics.battery_level is not None:
|
||||||
if telemetry.device_metrics.channel_utilization is not None:
|
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
|
||||||
print(
|
if telemetry.device_metrics.voltage is not None:
|
||||||
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
|
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
|
||||||
)
|
if telemetry.device_metrics.channel_utilization is not None:
|
||||||
if telemetry.device_metrics.air_util_tx is not None:
|
print(
|
||||||
print(
|
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
|
||||||
f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%"
|
)
|
||||||
)
|
if telemetry.device_metrics.air_util_tx is not None:
|
||||||
if telemetry.device_metrics.uptime_seconds is not None:
|
print(
|
||||||
print(f"Uptime: {telemetry.device_metrics.uptime_seconds} s")
|
f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%"
|
||||||
|
)
|
||||||
|
if telemetry.device_metrics.uptime_seconds is not None:
|
||||||
|
print(f"Uptime: {telemetry.device_metrics.uptime_seconds} s")
|
||||||
|
else:
|
||||||
|
# this is the new code if --request-telemetry <type> is used.
|
||||||
|
telemetry_dict = google.protobuf.json_format.MessageToDict(telemetry)
|
||||||
|
for key, value in telemetry_dict.items():
|
||||||
|
if key != "time": # protobuf includes a time field that we don't print for device_metrics.
|
||||||
|
print(f"{key}:")
|
||||||
|
for sub_key, sub_value in value.items():
|
||||||
|
print(f" {sub_key}: {sub_value}")
|
||||||
|
|
||||||
elif p["decoded"]["portnum"] == "ROUTING_APP":
|
elif p["decoded"]["portnum"] == "ROUTING_APP":
|
||||||
if p["decoded"]["routing"]["errorReason"] == "NO_RESPONSE":
|
if p["decoded"]["routing"]["errorReason"] == "NO_RESPONSE":
|
||||||
@@ -675,7 +709,9 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
meshPacket: mesh_pb2.MeshPacket,
|
meshPacket: mesh_pb2.MeshPacket,
|
||||||
destinationId: Union[int,str]=BROADCAST_ADDR,
|
destinationId: Union[int,str]=BROADCAST_ADDR,
|
||||||
wantAck: bool=False,
|
wantAck: bool=False,
|
||||||
hopLimit: Optional[int]=None
|
hopLimit: Optional[int]=None,
|
||||||
|
pkiEncrypted: Optional[bool]=False,
|
||||||
|
publicKey: Optional[bytes]=None,
|
||||||
):
|
):
|
||||||
"""Send a MeshPacket to the specified node (or if unspecified, broadcast).
|
"""Send a MeshPacket to the specified node (or if unspecified, broadcast).
|
||||||
You probably don't want this - use sendData instead.
|
You probably don't want this - use sendData instead.
|
||||||
@@ -724,6 +760,12 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
loraConfig = getattr(self.localNode.localConfig, "lora")
|
loraConfig = getattr(self.localNode.localConfig, "lora")
|
||||||
meshPacket.hop_limit = getattr(loraConfig, "hop_limit")
|
meshPacket.hop_limit = getattr(loraConfig, "hop_limit")
|
||||||
|
|
||||||
|
if pkiEncrypted:
|
||||||
|
meshPacket.pki_encrypted = True
|
||||||
|
|
||||||
|
if publicKey is not None:
|
||||||
|
meshPacket.public_key = publicKey
|
||||||
|
|
||||||
# if the user hasn't set an ID for this packet (likely and recommended),
|
# if the user hasn't set an ID for this packet (likely and recommended),
|
||||||
# we should pick a new unique ID so the message can be tracked.
|
# we should pick a new unique ID so the message can be tracked.
|
||||||
if meshPacket.id == 0:
|
if meshPacket.id == 0:
|
||||||
@@ -889,6 +931,8 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
startConfig = mesh_pb2.ToRadio()
|
startConfig = mesh_pb2.ToRadio()
|
||||||
if self.configId is None or not self.noNodes:
|
if self.configId is None or not self.noNodes:
|
||||||
self.configId = random.randint(0, 0xFFFFFFFF)
|
self.configId = random.randint(0, 0xFFFFFFFF)
|
||||||
|
if self.configId == NODELESS_WANT_CONFIG_ID:
|
||||||
|
self.configId = self.configId + 1
|
||||||
startConfig.want_config_id = self.configId
|
startConfig.want_config_id = self.configId
|
||||||
self._sendToRadio(startConfig)
|
self._sendToRadio(startConfig)
|
||||||
|
|
||||||
@@ -999,10 +1043,17 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
|
|
||||||
Called by subclasses."""
|
Called by subclasses."""
|
||||||
fromRadio = mesh_pb2.FromRadio()
|
fromRadio = mesh_pb2.FromRadio()
|
||||||
fromRadio.ParseFromString(fromRadioBytes)
|
|
||||||
logging.debug(
|
logging.debug(
|
||||||
f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}"
|
f"in mesh_interface.py _handleFromRadio() fromRadioBytes: {fromRadioBytes}"
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
fromRadio.ParseFromString(fromRadioBytes)
|
||||||
|
except Exception as ex:
|
||||||
|
logging.error(
|
||||||
|
f"Error while parsing FromRadio bytes:{fromRadioBytes} {ex}"
|
||||||
|
)
|
||||||
|
traceback.print_exc()
|
||||||
|
raise ex
|
||||||
asDict = google.protobuf.json_format.MessageToDict(fromRadio)
|
asDict = google.protobuf.json_format.MessageToDict(fromRadio)
|
||||||
logging.debug(f"Received from radio: {fromRadio}")
|
logging.debug(f"Received from radio: {fromRadio}")
|
||||||
if fromRadio.HasField("my_info"):
|
if fromRadio.HasField("my_info"):
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ with rather more easily once the code is simplified by this change.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
"""
|
"""
|
||||||
Restore the namespace to pristine condition.
|
Restore the namespace to pristine condition.
|
||||||
@@ -33,5 +35,5 @@ args = None
|
|||||||
parser = None
|
parser = None
|
||||||
channel_index = None
|
channel_index = None
|
||||||
logfile = None
|
logfile = None
|
||||||
tunnelInstance = None
|
tunnelInstance: Optional[Any] = None
|
||||||
camel_case = False
|
camel_case = False
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import base64
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union, List
|
||||||
|
|
||||||
from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2
|
from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2
|
||||||
from meshtastic.util import (
|
from meshtastic.util import (
|
||||||
@@ -25,15 +25,15 @@ class Node:
|
|||||||
Includes methods for localConfig, moduleConfig and channels
|
Includes methods for localConfig, moduleConfig and channels
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, iface, nodeNum, noProto=False):
|
def __init__(self, iface, nodeNum, noProto=False, timeout: int = 300):
|
||||||
"""Constructor"""
|
"""Constructor"""
|
||||||
self.iface = iface
|
self.iface = iface
|
||||||
self.nodeNum = nodeNum
|
self.nodeNum = nodeNum
|
||||||
self.localConfig = localonly_pb2.LocalConfig()
|
self.localConfig = localonly_pb2.LocalConfig()
|
||||||
self.moduleConfig = localonly_pb2.LocalModuleConfig()
|
self.moduleConfig = localonly_pb2.LocalModuleConfig()
|
||||||
self.channels = None
|
self.channels = None
|
||||||
self._timeout = Timeout(maxSecs=300)
|
self._timeout = Timeout(maxSecs=timeout)
|
||||||
self.partialChannels = None
|
self.partialChannels: Optional[List] = None
|
||||||
self.noProto = noProto
|
self.noProto = noProto
|
||||||
self.cannedPluginMessage = None
|
self.cannedPluginMessage = None
|
||||||
self.cannedPluginMessageMessages = None
|
self.cannedPluginMessageMessages = None
|
||||||
@@ -77,13 +77,14 @@ class Node:
|
|||||||
self.channels = channels
|
self.channels = channels
|
||||||
self._fixupChannels()
|
self._fixupChannels()
|
||||||
|
|
||||||
def requestChannels(self):
|
def requestChannels(self, startingIndex: int = 0):
|
||||||
"""Send regular MeshPackets to ask channels."""
|
"""Send regular MeshPackets to ask channels."""
|
||||||
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
||||||
self.channels = None
|
# only initialize if we're starting out fresh
|
||||||
self.partialChannels = [] # We keep our channels in a temp array until finished
|
if startingIndex == 0:
|
||||||
|
self.channels = None
|
||||||
self._requestChannel(0)
|
self.partialChannels = [] # We keep our channels in a temp array until finished
|
||||||
|
self._requestChannel(startingIndex)
|
||||||
|
|
||||||
def onResponseRequestSettings(self, p):
|
def onResponseRequestSettings(self, p):
|
||||||
"""Handle the response packets for requesting settings _requestSettings()"""
|
"""Handle the response packets for requesting settings _requestSettings()"""
|
||||||
@@ -120,7 +121,7 @@ class Node:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
if config_values is not None:
|
if config_values is not None:
|
||||||
raw_config = getattr(getattr(adminMessage['raw'], oneof), field)
|
raw_config = getattr(getattr(adminMessage['raw'], oneof), camel_to_snake(field))
|
||||||
config_values.CopyFrom(raw_config)
|
config_values.CopyFrom(raw_config)
|
||||||
print(f"{str(camel_to_snake(field))}:\n{str(config_values)}")
|
print(f"{str(camel_to_snake(field))}:\n{str(config_values)}")
|
||||||
|
|
||||||
@@ -684,9 +685,6 @@ class Node:
|
|||||||
def setFixedPosition(self, lat: Union[int, float], lon: Union[int, float], alt: int):
|
def setFixedPosition(self, lat: Union[int, float], lon: Union[int, float], alt: int):
|
||||||
"""Tell the node to set fixed position to the provided value and enable the fixed position setting"""
|
"""Tell the node to set fixed position to the provided value and enable the fixed position setting"""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
if self != self.iface.localNode:
|
|
||||||
logging.error("Setting position of remote nodes is not supported.")
|
|
||||||
return None
|
|
||||||
|
|
||||||
p = mesh_pb2.Position()
|
p = mesh_pb2.Position()
|
||||||
if isinstance(lat, float) and lat != 0.0:
|
if isinstance(lat, float) and lat != 0.0:
|
||||||
@@ -704,7 +702,12 @@ class Node:
|
|||||||
|
|
||||||
a = admin_pb2.AdminMessage()
|
a = admin_pb2.AdminMessage()
|
||||||
a.set_fixed_position.CopyFrom(p)
|
a.set_fixed_position.CopyFrom(p)
|
||||||
return self._sendAdmin(a)
|
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(a, onResponse=onResponse)
|
||||||
|
|
||||||
def removeFixedPosition(self):
|
def removeFixedPosition(self):
|
||||||
"""Tell the node to remove the fixed position and set the fixed position setting to false"""
|
"""Tell the node to remove the fixed position and set the fixed position setting to false"""
|
||||||
@@ -713,7 +716,26 @@ class Node:
|
|||||||
p.remove_fixed_position = True
|
p.remove_fixed_position = True
|
||||||
logging.info(f"Telling node to remove fixed position")
|
logging.info(f"Telling node to remove fixed position")
|
||||||
|
|
||||||
return self._sendAdmin(p)
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
|
def setTime(self, timeSec: int = 0):
|
||||||
|
"""Tell the node to set its time to the provided timestamp, or the system's current time if not provided or 0."""
|
||||||
|
self.ensureSessionKey()
|
||||||
|
if timeSec == 0:
|
||||||
|
timeSec = int(time.time())
|
||||||
|
p = admin_pb2.AdminMessage()
|
||||||
|
p.set_time_only = timeSec
|
||||||
|
logging.info(f"Setting node time to {timeSec}")
|
||||||
|
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
def _fixupChannels(self):
|
def _fixupChannels(self):
|
||||||
"""Fixup indexes and add disabled channels as needed"""
|
"""Fixup indexes and add disabled channels as needed"""
|
||||||
@@ -872,6 +894,7 @@ class Node:
|
|||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=adminIndex,
|
channelIndex=adminIndex,
|
||||||
|
pkiEncrypted=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def ensureSessionKey(self):
|
def ensureSessionKey(self):
|
||||||
|
|||||||
32
meshtastic/protobuf/atak_pb2.py
generated
32
meshtastic/protobuf/atak_pb2.py
generated
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\x93\x02\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12-\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.Contact\x12)\n\x05group\x18\x03 \x01(\x0b\x32\x1a.meshtastic.protobuf.Group\x12+\n\x06status\x18\x04 \x01(\x0b\x32\x1b.meshtastic.protobuf.Status\x12\'\n\x03pli\x18\x05 \x01(\x0b\x32\x18.meshtastic.protobuf.PLIH\x00\x12,\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x1c.meshtastic.protobuf.GeoChatH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"_\n\x05Group\x12-\n\x04role\x18\x01 \x01(\x0e\x32\x1f.meshtastic.protobuf.MemberRole\x12\'\n\x04team\x18\x02 \x01(\x0e\x32\x19.meshtastic.protobuf.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42_\n\x13\x63om.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\xa5\x02\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12-\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.Contact\x12)\n\x05group\x18\x03 \x01(\x0b\x32\x1a.meshtastic.protobuf.Group\x12+\n\x06status\x18\x04 \x01(\x0b\x32\x1b.meshtastic.protobuf.Status\x12\'\n\x03pli\x18\x05 \x01(\x0b\x32\x18.meshtastic.protobuf.PLIH\x00\x12,\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x1c.meshtastic.protobuf.GeoChatH\x00\x12\x10\n\x06\x64\x65tail\x18\x07 \x01(\x0cH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"_\n\x05Group\x12-\n\x04role\x18\x01 \x01(\x0e\x32\x1f.meshtastic.protobuf.MemberRole\x12\'\n\x04team\x18\x02 \x01(\x0e\x32\x19.meshtastic.protobuf.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42_\n\x13\x63om.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
@@ -21,20 +21,20 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.atak_pb
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_TEAM']._serialized_start=703
|
_globals['_TEAM']._serialized_start=721
|
||||||
_globals['_TEAM']._serialized_end=895
|
_globals['_TEAM']._serialized_end=913
|
||||||
_globals['_MEMBERROLE']._serialized_start=897
|
_globals['_MEMBERROLE']._serialized_start=915
|
||||||
_globals['_MEMBERROLE']._serialized_end=1024
|
_globals['_MEMBERROLE']._serialized_end=1042
|
||||||
_globals['_TAKPACKET']._serialized_start=56
|
_globals['_TAKPACKET']._serialized_start=56
|
||||||
_globals['_TAKPACKET']._serialized_end=331
|
_globals['_TAKPACKET']._serialized_end=349
|
||||||
_globals['_GEOCHAT']._serialized_start=333
|
_globals['_GEOCHAT']._serialized_start=351
|
||||||
_globals['_GEOCHAT']._serialized_end=425
|
_globals['_GEOCHAT']._serialized_end=443
|
||||||
_globals['_GROUP']._serialized_start=427
|
_globals['_GROUP']._serialized_start=445
|
||||||
_globals['_GROUP']._serialized_end=522
|
_globals['_GROUP']._serialized_end=540
|
||||||
_globals['_STATUS']._serialized_start=524
|
_globals['_STATUS']._serialized_start=542
|
||||||
_globals['_STATUS']._serialized_end=549
|
_globals['_STATUS']._serialized_end=567
|
||||||
_globals['_CONTACT']._serialized_start=551
|
_globals['_CONTACT']._serialized_start=569
|
||||||
_globals['_CONTACT']._serialized_end=603
|
_globals['_CONTACT']._serialized_end=621
|
||||||
_globals['_PLI']._serialized_start=605
|
_globals['_PLI']._serialized_start=623
|
||||||
_globals['_PLI']._serialized_end=700
|
_globals['_PLI']._serialized_end=718
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
13
meshtastic/protobuf/atak_pb2.pyi
generated
13
meshtastic/protobuf/atak_pb2.pyi
generated
@@ -248,10 +248,16 @@ class TAKPacket(google.protobuf.message.Message):
|
|||||||
STATUS_FIELD_NUMBER: builtins.int
|
STATUS_FIELD_NUMBER: builtins.int
|
||||||
PLI_FIELD_NUMBER: builtins.int
|
PLI_FIELD_NUMBER: builtins.int
|
||||||
CHAT_FIELD_NUMBER: builtins.int
|
CHAT_FIELD_NUMBER: builtins.int
|
||||||
|
DETAIL_FIELD_NUMBER: builtins.int
|
||||||
is_compressed: builtins.bool
|
is_compressed: builtins.bool
|
||||||
"""
|
"""
|
||||||
Are the payloads strings compressed for LoRA transport?
|
Are the payloads strings compressed for LoRA transport?
|
||||||
"""
|
"""
|
||||||
|
detail: builtins.bytes
|
||||||
|
"""
|
||||||
|
Generic CoT detail XML
|
||||||
|
May be compressed / truncated by the sender
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def contact(self) -> global___Contact:
|
def contact(self) -> global___Contact:
|
||||||
"""
|
"""
|
||||||
@@ -291,10 +297,11 @@ class TAKPacket(google.protobuf.message.Message):
|
|||||||
status: global___Status | None = ...,
|
status: global___Status | None = ...,
|
||||||
pli: global___PLI | None = ...,
|
pli: global___PLI | None = ...,
|
||||||
chat: global___GeoChat | None = ...,
|
chat: global___GeoChat | None = ...,
|
||||||
|
detail: builtins.bytes = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "group", b"group", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "detail", b"detail", "group", b"group", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "group", b"group", "is_compressed", b"is_compressed", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "detail", b"detail", "group", b"group", "is_compressed", b"is_compressed", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> None: ...
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["pli", "chat"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["pli", "chat", "detail"] | None: ...
|
||||||
|
|
||||||
global___TAKPacket = TAKPacket
|
global___TAKPacket = TAKPacket
|
||||||
|
|
||||||
|
|||||||
7
meshtastic/protobuf/clientonly_pb2.py
generated
7
meshtastic/protobuf/clientonly_pb2.py
generated
@@ -12,9 +12,10 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
|
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
|
||||||
|
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\"\x9f\x02\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x35\n\x06\x63onfig\x18\x04 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfigH\x03\x88\x01\x01\x12\x42\n\rmodule_config\x18\x05 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfigH\x04\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"\xc4\x03\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x35\n\x06\x63onfig\x18\x04 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfigH\x03\x88\x01\x01\x12\x42\n\rmodule_config\x18\x05 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfigH\x04\x88\x01\x01\x12:\n\x0e\x66ixed_position\x18\x06 \x01(\x0b\x32\x1d.meshtastic.protobuf.PositionH\x05\x88\x01\x01\x12\x15\n\x08ringtone\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x0f\x63\x61nned_messages\x18\x08 \x01(\tH\x07\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configB\x11\n\x0f_fixed_positionB\x0b\n\t_ringtoneB\x12\n\x10_canned_messagesBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
@@ -22,6 +23,6 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cliento
|
|||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
|
||||||
_globals['_DEVICEPROFILE']._serialized_start=99
|
_globals['_DEVICEPROFILE']._serialized_start=131
|
||||||
_globals['_DEVICEPROFILE']._serialized_end=386
|
_globals['_DEVICEPROFILE']._serialized_end=583
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
31
meshtastic/protobuf/clientonly_pb2.pyi
generated
31
meshtastic/protobuf/clientonly_pb2.pyi
generated
@@ -7,6 +7,7 @@ import builtins
|
|||||||
import google.protobuf.descriptor
|
import google.protobuf.descriptor
|
||||||
import google.protobuf.message
|
import google.protobuf.message
|
||||||
import meshtastic.protobuf.localonly_pb2
|
import meshtastic.protobuf.localonly_pb2
|
||||||
|
import meshtastic.protobuf.mesh_pb2
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
||||||
@@ -25,6 +26,9 @@ class DeviceProfile(google.protobuf.message.Message):
|
|||||||
CHANNEL_URL_FIELD_NUMBER: builtins.int
|
CHANNEL_URL_FIELD_NUMBER: builtins.int
|
||||||
CONFIG_FIELD_NUMBER: builtins.int
|
CONFIG_FIELD_NUMBER: builtins.int
|
||||||
MODULE_CONFIG_FIELD_NUMBER: builtins.int
|
MODULE_CONFIG_FIELD_NUMBER: builtins.int
|
||||||
|
FIXED_POSITION_FIELD_NUMBER: builtins.int
|
||||||
|
RINGTONE_FIELD_NUMBER: builtins.int
|
||||||
|
CANNED_MESSAGES_FIELD_NUMBER: builtins.int
|
||||||
long_name: builtins.str
|
long_name: builtins.str
|
||||||
"""
|
"""
|
||||||
Long name for the node
|
Long name for the node
|
||||||
@@ -37,6 +41,14 @@ class DeviceProfile(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
The url of the channels from our node
|
The url of the channels from our node
|
||||||
"""
|
"""
|
||||||
|
ringtone: builtins.str
|
||||||
|
"""
|
||||||
|
Ringtone for ExternalNotification
|
||||||
|
"""
|
||||||
|
canned_messages: builtins.str
|
||||||
|
"""
|
||||||
|
Predefined messages for CannedMessage
|
||||||
|
"""
|
||||||
@property
|
@property
|
||||||
def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig:
|
def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig:
|
||||||
"""
|
"""
|
||||||
@@ -49,6 +61,12 @@ class DeviceProfile(google.protobuf.message.Message):
|
|||||||
The ModuleConfig of the node
|
The ModuleConfig of the node
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fixed_position(self) -> meshtastic.protobuf.mesh_pb2.Position:
|
||||||
|
"""
|
||||||
|
Fixed position data
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -57,18 +75,27 @@ class DeviceProfile(google.protobuf.message.Message):
|
|||||||
channel_url: builtins.str | None = ...,
|
channel_url: builtins.str | None = ...,
|
||||||
config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ...,
|
config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ...,
|
||||||
module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ...,
|
module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ...,
|
||||||
|
fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ...,
|
||||||
|
ringtone: builtins.str | None = ...,
|
||||||
|
canned_messages: builtins.str | None = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["_channel_url", b"_channel_url", "_config", b"_config", "_long_name", b"_long_name", "_module_config", b"_module_config", "_short_name", b"_short_name", "channel_url", b"channel_url", "config", b"config", "long_name", b"long_name", "module_config", b"module_config", "short_name", b"short_name"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["_channel_url", b"_channel_url", "_config", b"_config", "_long_name", b"_long_name", "_module_config", b"_module_config", "_short_name", b"_short_name", "channel_url", b"channel_url", "config", b"config", "long_name", b"long_name", "module_config", b"module_config", "short_name", b"short_name"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> None: ...
|
||||||
|
@typing.overload
|
||||||
|
def WhichOneof(self, oneof_group: typing.Literal["_canned_messages", b"_canned_messages"]) -> typing.Literal["canned_messages"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_channel_url", b"_channel_url"]) -> typing.Literal["channel_url"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_channel_url", b"_channel_url"]) -> typing.Literal["channel_url"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_config", b"_config"]) -> typing.Literal["config"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_config", b"_config"]) -> typing.Literal["config"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
|
def WhichOneof(self, oneof_group: typing.Literal["_fixed_position", b"_fixed_position"]) -> typing.Literal["fixed_position"] | None: ...
|
||||||
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_long_name", b"_long_name"]) -> typing.Literal["long_name"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_long_name", b"_long_name"]) -> typing.Literal["long_name"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_module_config", b"_module_config"]) -> typing.Literal["module_config"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_module_config", b"_module_config"]) -> typing.Literal["module_config"] | None: ...
|
||||||
@typing.overload
|
@typing.overload
|
||||||
|
def WhichOneof(self, oneof_group: typing.Literal["_ringtone", b"_ringtone"]) -> typing.Literal["ringtone"] | None: ...
|
||||||
|
@typing.overload
|
||||||
def WhichOneof(self, oneof_group: typing.Literal["_short_name", b"_short_name"]) -> typing.Literal["short_name"] | None: ...
|
def WhichOneof(self, oneof_group: typing.Literal["_short_name", b"_short_name"]) -> typing.Literal["short_name"] | None: ...
|
||||||
|
|
||||||
global___DeviceProfile = DeviceProfile
|
global___DeviceProfile = DeviceProfile
|
||||||
|
|||||||
20
meshtastic/protobuf/deviceonly_pb2.py
generated
20
meshtastic/protobuf/deviceonly_pb2.py
generated
@@ -19,7 +19,7 @@ from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config
|
|||||||
import nanopb_pb2 as nanopb__pb2
|
import nanopb_pb2 as nanopb__pb2
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/config.proto\x1a\x0cnanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\xe2\x01\n\x08UserLite\x12\x13\n\x07macaddr\x18\x01 \x01(\x0c\x42\x02\x18\x01\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x05 \x01(\x08\x12;\n\x04role\x18\x06 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x12\n\npublic_key\x18\x07 \x01(\x0c\"\xa5\x02\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12+\n\x04user\x18\x02 \x01(\x0b\x32\x1d.meshtastic.protobuf.UserLite\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x11\n\thops_away\x18\t \x01(\r\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\"\x82\x04\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x36\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12M\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\x12\x63\n\x0cnode_db_lite\x18\x0e \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector<meshtastic_NodeInfoLite>\"N\n\x0b\x43hannelFile\x12.\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\xb2\x02\n\x08OEMStore\x12\x16\n\x0eoem_icon_width\x18\x01 \x01(\r\x12\x17\n\x0foem_icon_height\x18\x02 \x01(\r\x12\x15\n\roem_icon_bits\x18\x03 \x01(\x0c\x12\x32\n\x08oem_font\x18\x04 \x01(\x0e\x32 .meshtastic.protobuf.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t\x12\x13\n\x0boem_aes_key\x18\x06 \x01(\x0c\x12:\n\x10oem_local_config\x18\x07 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12G\n\x17oem_local_module_config\x18\x08 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42m\n\x13\x63om.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08<vector>b\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/config.proto\x1a\x0cnanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\xe2\x01\n\x08UserLite\x12\x13\n\x07macaddr\x18\x01 \x01(\x0c\x42\x02\x18\x01\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x05 \x01(\x08\x12;\n\x04role\x18\x06 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x12\n\npublic_key\x18\x07 \x01(\x0c\"\xb8\x02\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12+\n\x04user\x18\x02 \x01(\x0b\x32\x1d.meshtastic.protobuf.UserLite\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x16\n\thops_away\x18\t \x01(\rH\x00\x88\x01\x01\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\x42\x0c\n\n_hops_away\"\x82\x04\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x36\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12M\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\x12\x63\n\x0cnode_db_lite\x18\x0e \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector<meshtastic_NodeInfoLite>\"N\n\x0b\x43hannelFile\x12.\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\xb2\x02\n\x08OEMStore\x12\x16\n\x0eoem_icon_width\x18\x01 \x01(\r\x12\x17\n\x0foem_icon_height\x18\x02 \x01(\r\x12\x15\n\roem_icon_bits\x18\x03 \x01(\x0c\x12\x32\n\x08oem_font\x18\x04 \x01(\x0e\x32 .meshtastic.protobuf.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t\x12\x13\n\x0boem_aes_key\x18\x06 \x01(\x0c\x12:\n\x10oem_local_config\x18\x07 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12G\n\x17oem_local_module_config\x18\x08 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42m\n\x13\x63om.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08<vector>b\x06proto3')
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
@@ -33,18 +33,18 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|||||||
_DEVICESTATE.fields_by_name['no_save']._serialized_options = b'\030\001'
|
_DEVICESTATE.fields_by_name['no_save']._serialized_options = b'\030\001'
|
||||||
_DEVICESTATE.fields_by_name['node_db_lite']._options = None
|
_DEVICESTATE.fields_by_name['node_db_lite']._options = None
|
||||||
_DEVICESTATE.fields_by_name['node_db_lite']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
|
_DEVICESTATE.fields_by_name['node_db_lite']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
|
||||||
_globals['_SCREENFONTS']._serialized_start=1837
|
_globals['_SCREENFONTS']._serialized_start=1856
|
||||||
_globals['_SCREENFONTS']._serialized_end=1899
|
_globals['_SCREENFONTS']._serialized_end=1918
|
||||||
_globals['_POSITIONLITE']._serialized_start=251
|
_globals['_POSITIONLITE']._serialized_start=251
|
||||||
_globals['_POSITIONLITE']._serialized_end=404
|
_globals['_POSITIONLITE']._serialized_end=404
|
||||||
_globals['_USERLITE']._serialized_start=407
|
_globals['_USERLITE']._serialized_start=407
|
||||||
_globals['_USERLITE']._serialized_end=633
|
_globals['_USERLITE']._serialized_end=633
|
||||||
_globals['_NODEINFOLITE']._serialized_start=636
|
_globals['_NODEINFOLITE']._serialized_start=636
|
||||||
_globals['_NODEINFOLITE']._serialized_end=929
|
_globals['_NODEINFOLITE']._serialized_end=948
|
||||||
_globals['_DEVICESTATE']._serialized_start=932
|
_globals['_DEVICESTATE']._serialized_start=951
|
||||||
_globals['_DEVICESTATE']._serialized_end=1446
|
_globals['_DEVICESTATE']._serialized_end=1465
|
||||||
_globals['_CHANNELFILE']._serialized_start=1448
|
_globals['_CHANNELFILE']._serialized_start=1467
|
||||||
_globals['_CHANNELFILE']._serialized_end=1526
|
_globals['_CHANNELFILE']._serialized_end=1545
|
||||||
_globals['_OEMSTORE']._serialized_start=1529
|
_globals['_OEMSTORE']._serialized_start=1548
|
||||||
_globals['_OEMSTORE']._serialized_end=1835
|
_globals['_OEMSTORE']._serialized_end=1854
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
7
meshtastic/protobuf/deviceonly_pb2.pyi
generated
7
meshtastic/protobuf/deviceonly_pb2.pyi
generated
@@ -248,11 +248,12 @@ class NodeInfoLite(google.protobuf.message.Message):
|
|||||||
device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ...,
|
device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ...,
|
||||||
channel: builtins.int = ...,
|
channel: builtins.int = ...,
|
||||||
via_mqtt: builtins.bool = ...,
|
via_mqtt: builtins.bool = ...,
|
||||||
hops_away: builtins.int = ...,
|
hops_away: builtins.int | None = ...,
|
||||||
is_favorite: builtins.bool = ...,
|
is_favorite: builtins.bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["device_metrics", b"device_metrics", "position", b"position", "user", b"user"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
|
||||||
|
def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ...
|
||||||
|
|
||||||
global___NodeInfoLite = NodeInfoLite
|
global___NodeInfoLite = NodeInfoLite
|
||||||
|
|
||||||
|
|||||||
114
meshtastic/protobuf/mesh_pb2.py
generated
114
meshtastic/protobuf/mesh_pb2.py
generated
File diff suppressed because one or more lines are too long
75
meshtastic/protobuf/mesh_pb2.pyi
generated
75
meshtastic/protobuf/mesh_pb2.pyi
generated
@@ -129,6 +129,10 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
|||||||
"""
|
"""
|
||||||
Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
||||||
"""
|
"""
|
||||||
|
HELTEC_WIRELESS_BRIDGE: _HardwareModel.ValueType # 24
|
||||||
|
"""
|
||||||
|
Heltec Wireless Bridge
|
||||||
|
"""
|
||||||
STATION_G1: _HardwareModel.ValueType # 25
|
STATION_G1: _HardwareModel.ValueType # 25
|
||||||
"""
|
"""
|
||||||
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
||||||
@@ -201,7 +205,7 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
|||||||
"""
|
"""
|
||||||
M5STACK: _HardwareModel.ValueType # 42
|
M5STACK: _HardwareModel.ValueType # 42
|
||||||
"""
|
"""
|
||||||
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
|
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/
|
||||||
"""
|
"""
|
||||||
HELTEC_V3: _HardwareModel.ValueType # 43
|
HELTEC_V3: _HardwareModel.ValueType # 43
|
||||||
"""
|
"""
|
||||||
@@ -359,8 +363,22 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
|||||||
^^^ short A0 to switch to I2C address 0x3C
|
^^^ short A0 to switch to I2C address 0x3C
|
||||||
"""
|
"""
|
||||||
M5STACK_COREBASIC: _HardwareModel.ValueType # 77
|
M5STACK_COREBASIC: _HardwareModel.ValueType # 77
|
||||||
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/"""
|
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
|
||||||
M5STACK_CORE2: _HardwareModel.ValueType # 78
|
M5STACK_CORE2: _HardwareModel.ValueType # 78
|
||||||
|
RPI_PICO2: _HardwareModel.ValueType # 79
|
||||||
|
"""Pico2 with Waveshare Hat, same as Pico"""
|
||||||
|
M5STACK_CORES3: _HardwareModel.ValueType # 80
|
||||||
|
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
|
||||||
|
SEEED_XIAO_S3: _HardwareModel.ValueType # 81
|
||||||
|
"""Seeed XIAO S3 DK"""
|
||||||
|
MS24SF1: _HardwareModel.ValueType # 82
|
||||||
|
"""
|
||||||
|
Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1
|
||||||
|
"""
|
||||||
|
TLORA_C6: _HardwareModel.ValueType # 83
|
||||||
|
"""
|
||||||
|
Lilygo TLora-C6 with the new ESP32-C6 MCU
|
||||||
|
"""
|
||||||
PRIVATE_HW: _HardwareModel.ValueType # 255
|
PRIVATE_HW: _HardwareModel.ValueType # 255
|
||||||
"""
|
"""
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -474,6 +492,10 @@ HELTEC_HRU_3601: HardwareModel.ValueType # 23
|
|||||||
"""
|
"""
|
||||||
Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
||||||
"""
|
"""
|
||||||
|
HELTEC_WIRELESS_BRIDGE: HardwareModel.ValueType # 24
|
||||||
|
"""
|
||||||
|
Heltec Wireless Bridge
|
||||||
|
"""
|
||||||
STATION_G1: HardwareModel.ValueType # 25
|
STATION_G1: HardwareModel.ValueType # 25
|
||||||
"""
|
"""
|
||||||
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
||||||
@@ -546,7 +568,7 @@ Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio
|
|||||||
"""
|
"""
|
||||||
M5STACK: HardwareModel.ValueType # 42
|
M5STACK: HardwareModel.ValueType # 42
|
||||||
"""
|
"""
|
||||||
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
|
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/
|
||||||
"""
|
"""
|
||||||
HELTEC_V3: HardwareModel.ValueType # 43
|
HELTEC_V3: HardwareModel.ValueType # 43
|
||||||
"""
|
"""
|
||||||
@@ -704,8 +726,22 @@ https://www.adafruit.com/product/938
|
|||||||
^^^ short A0 to switch to I2C address 0x3C
|
^^^ short A0 to switch to I2C address 0x3C
|
||||||
"""
|
"""
|
||||||
M5STACK_COREBASIC: HardwareModel.ValueType # 77
|
M5STACK_COREBASIC: HardwareModel.ValueType # 77
|
||||||
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/"""
|
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
|
||||||
M5STACK_CORE2: HardwareModel.ValueType # 78
|
M5STACK_CORE2: HardwareModel.ValueType # 78
|
||||||
|
RPI_PICO2: HardwareModel.ValueType # 79
|
||||||
|
"""Pico2 with Waveshare Hat, same as Pico"""
|
||||||
|
M5STACK_CORES3: HardwareModel.ValueType # 80
|
||||||
|
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
|
||||||
|
SEEED_XIAO_S3: HardwareModel.ValueType # 81
|
||||||
|
"""Seeed XIAO S3 DK"""
|
||||||
|
MS24SF1: HardwareModel.ValueType # 82
|
||||||
|
"""
|
||||||
|
Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1
|
||||||
|
"""
|
||||||
|
TLORA_C6: HardwareModel.ValueType # 83
|
||||||
|
"""
|
||||||
|
Lilygo TLora-C6 with the new ESP32-C6 MCU
|
||||||
|
"""
|
||||||
PRIVATE_HW: HardwareModel.ValueType # 255
|
PRIVATE_HW: HardwareModel.ValueType # 255
|
||||||
"""
|
"""
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -1389,6 +1425,14 @@ class Routing(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
The receiving node does not have a Public Key to decode with
|
The receiving node does not have a Public Key to decode with
|
||||||
"""
|
"""
|
||||||
|
ADMIN_BAD_SESSION_KEY: Routing._Error.ValueType # 36
|
||||||
|
"""
|
||||||
|
Admin packet otherwise checks out, but uses a bogus or expired session key
|
||||||
|
"""
|
||||||
|
ADMIN_PUBLIC_KEY_UNAUTHORIZED: Routing._Error.ValueType # 37
|
||||||
|
"""
|
||||||
|
Admin packet sent using PKC, but not from a public key on the admin key list
|
||||||
|
"""
|
||||||
|
|
||||||
class Error(_Error, metaclass=_ErrorEnumTypeWrapper):
|
class Error(_Error, metaclass=_ErrorEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -1454,6 +1498,14 @@ class Routing(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
The receiving node does not have a Public Key to decode with
|
The receiving node does not have a Public Key to decode with
|
||||||
"""
|
"""
|
||||||
|
ADMIN_BAD_SESSION_KEY: Routing.Error.ValueType # 36
|
||||||
|
"""
|
||||||
|
Admin packet otherwise checks out, but uses a bogus or expired session key
|
||||||
|
"""
|
||||||
|
ADMIN_PUBLIC_KEY_UNAUTHORIZED: Routing.Error.ValueType # 37
|
||||||
|
"""
|
||||||
|
Admin packet sent using PKC, but not from a public key on the admin key list
|
||||||
|
"""
|
||||||
|
|
||||||
ROUTE_REQUEST_FIELD_NUMBER: builtins.int
|
ROUTE_REQUEST_FIELD_NUMBER: builtins.int
|
||||||
ROUTE_REPLY_FIELD_NUMBER: builtins.int
|
ROUTE_REPLY_FIELD_NUMBER: builtins.int
|
||||||
@@ -2080,11 +2132,12 @@ class NodeInfo(google.protobuf.message.Message):
|
|||||||
device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ...,
|
device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ...,
|
||||||
channel: builtins.int = ...,
|
channel: builtins.int = ...,
|
||||||
via_mqtt: builtins.bool = ...,
|
via_mqtt: builtins.bool = ...,
|
||||||
hops_away: builtins.int = ...,
|
hops_away: builtins.int | None = ...,
|
||||||
is_favorite: builtins.bool = ...,
|
is_favorite: builtins.bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def HasField(self, field_name: typing.Literal["device_metrics", b"device_metrics", "position", b"position", "user", b"user"]) -> builtins.bool: ...
|
def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ...
|
||||||
def ClearField(self, field_name: typing.Literal["channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
|
||||||
|
def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ...
|
||||||
|
|
||||||
global___NodeInfo = NodeInfo
|
global___NodeInfo = NodeInfo
|
||||||
|
|
||||||
@@ -2695,6 +2748,7 @@ class DeviceMetadata(google.protobuf.message.Message):
|
|||||||
POSITION_FLAGS_FIELD_NUMBER: builtins.int
|
POSITION_FLAGS_FIELD_NUMBER: builtins.int
|
||||||
HW_MODEL_FIELD_NUMBER: builtins.int
|
HW_MODEL_FIELD_NUMBER: builtins.int
|
||||||
HASREMOTEHARDWARE_FIELD_NUMBER: builtins.int
|
HASREMOTEHARDWARE_FIELD_NUMBER: builtins.int
|
||||||
|
HASPKC_FIELD_NUMBER: builtins.int
|
||||||
firmware_version: builtins.str
|
firmware_version: builtins.str
|
||||||
"""
|
"""
|
||||||
Device firmware version string
|
Device firmware version string
|
||||||
@@ -2735,6 +2789,10 @@ class DeviceMetadata(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
Has Remote Hardware enabled
|
Has Remote Hardware enabled
|
||||||
"""
|
"""
|
||||||
|
hasPKC: builtins.bool
|
||||||
|
"""
|
||||||
|
Has PKC capabilities
|
||||||
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -2748,8 +2806,9 @@ class DeviceMetadata(google.protobuf.message.Message):
|
|||||||
position_flags: builtins.int = ...,
|
position_flags: builtins.int = ...,
|
||||||
hw_model: global___HardwareModel.ValueType = ...,
|
hw_model: global___HardwareModel.ValueType = ...,
|
||||||
hasRemoteHardware: builtins.bool = ...,
|
hasRemoteHardware: builtins.bool = ...,
|
||||||
|
hasPKC: builtins.bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["canShutdown", b"canShutdown", "device_state_version", b"device_state_version", "firmware_version", b"firmware_version", "hasBluetooth", b"hasBluetooth", "hasEthernet", b"hasEthernet", "hasRemoteHardware", b"hasRemoteHardware", "hasWifi", b"hasWifi", "hw_model", b"hw_model", "position_flags", b"position_flags", "role", b"role"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["canShutdown", b"canShutdown", "device_state_version", b"device_state_version", "firmware_version", b"firmware_version", "hasBluetooth", b"hasBluetooth", "hasEthernet", b"hasEthernet", "hasPKC", b"hasPKC", "hasRemoteHardware", b"hasRemoteHardware", "hasWifi", b"hasWifi", "hw_model", b"hw_model", "position_flags", b"position_flags", "role", b"role"]) -> None: ...
|
||||||
|
|
||||||
global___DeviceMetadata = DeviceMetadata
|
global___DeviceMetadata = DeviceMetadata
|
||||||
|
|
||||||
|
|||||||
68
meshtastic/protobuf/module_config_pb2.py
generated
68
meshtastic/protobuf/module_config_pb2.py
generated
File diff suppressed because one or more lines are too long
62
meshtastic/protobuf/module_config_pb2.pyi
generated
62
meshtastic/protobuf/module_config_pb2.pyi
generated
@@ -250,13 +250,54 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||||
|
|
||||||
|
class _TriggerType:
|
||||||
|
ValueType = typing.NewType("ValueType", builtins.int)
|
||||||
|
V: typing_extensions.TypeAlias = ValueType
|
||||||
|
|
||||||
|
class _TriggerTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.DetectionSensorConfig._TriggerType.ValueType], builtins.type):
|
||||||
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
||||||
|
LOGIC_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 0
|
||||||
|
"""Event is triggered if pin is low"""
|
||||||
|
LOGIC_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 1
|
||||||
|
"""Event is triggered if pin is high"""
|
||||||
|
FALLING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 2
|
||||||
|
"""Event is triggered when pin goes high to low"""
|
||||||
|
RISING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 3
|
||||||
|
"""Event is triggered when pin goes low to high"""
|
||||||
|
EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 4
|
||||||
|
"""Event is triggered on every pin state change, low is considered to be
|
||||||
|
"active"
|
||||||
|
"""
|
||||||
|
EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 5
|
||||||
|
"""Event is triggered on every pin state change, high is considered to be
|
||||||
|
"active"
|
||||||
|
"""
|
||||||
|
|
||||||
|
class TriggerType(_TriggerType, metaclass=_TriggerTypeEnumTypeWrapper): ...
|
||||||
|
LOGIC_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 0
|
||||||
|
"""Event is triggered if pin is low"""
|
||||||
|
LOGIC_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 1
|
||||||
|
"""Event is triggered if pin is high"""
|
||||||
|
FALLING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 2
|
||||||
|
"""Event is triggered when pin goes high to low"""
|
||||||
|
RISING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 3
|
||||||
|
"""Event is triggered when pin goes low to high"""
|
||||||
|
EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 4
|
||||||
|
"""Event is triggered on every pin state change, low is considered to be
|
||||||
|
"active"
|
||||||
|
"""
|
||||||
|
EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 5
|
||||||
|
"""Event is triggered on every pin state change, high is considered to be
|
||||||
|
"active"
|
||||||
|
"""
|
||||||
|
|
||||||
ENABLED_FIELD_NUMBER: builtins.int
|
ENABLED_FIELD_NUMBER: builtins.int
|
||||||
MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
||||||
STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
||||||
SEND_BELL_FIELD_NUMBER: builtins.int
|
SEND_BELL_FIELD_NUMBER: builtins.int
|
||||||
NAME_FIELD_NUMBER: builtins.int
|
NAME_FIELD_NUMBER: builtins.int
|
||||||
MONITOR_PIN_FIELD_NUMBER: builtins.int
|
MONITOR_PIN_FIELD_NUMBER: builtins.int
|
||||||
DETECTION_TRIGGERED_HIGH_FIELD_NUMBER: builtins.int
|
DETECTION_TRIGGER_TYPE_FIELD_NUMBER: builtins.int
|
||||||
USE_PULLUP_FIELD_NUMBER: builtins.int
|
USE_PULLUP_FIELD_NUMBER: builtins.int
|
||||||
enabled: builtins.bool
|
enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -264,13 +305,15 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
minimum_broadcast_secs: builtins.int
|
minimum_broadcast_secs: builtins.int
|
||||||
"""
|
"""
|
||||||
Interval in seconds of how often we can send a message to the mesh when a state change is detected
|
Interval in seconds of how often we can send a message to the mesh when a
|
||||||
|
trigger event is detected
|
||||||
"""
|
"""
|
||||||
state_broadcast_secs: builtins.int
|
state_broadcast_secs: builtins.int
|
||||||
"""
|
"""
|
||||||
Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes
|
Interval in seconds of how often we should send a message to the mesh
|
||||||
When set to 0, only state changes will be broadcasted
|
with the current state regardless of trigger events When set to 0, only
|
||||||
Works as a sort of status heartbeat for peace of mind
|
trigger events will be broadcasted Works as a sort of status heartbeat
|
||||||
|
for peace of mind
|
||||||
"""
|
"""
|
||||||
send_bell: builtins.bool
|
send_bell: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -287,10 +330,9 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
GPIO pin to monitor for state changes
|
GPIO pin to monitor for state changes
|
||||||
"""
|
"""
|
||||||
detection_triggered_high: builtins.bool
|
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType
|
||||||
"""
|
"""
|
||||||
Whether or not the GPIO pin state detection is triggered on HIGH (1)
|
The type of trigger event to be used
|
||||||
Otherwise LOW (0)
|
|
||||||
"""
|
"""
|
||||||
use_pullup: builtins.bool
|
use_pullup: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -306,10 +348,10 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
send_bell: builtins.bool = ...,
|
send_bell: builtins.bool = ...,
|
||||||
name: builtins.str = ...,
|
name: builtins.str = ...,
|
||||||
monitor_pin: builtins.int = ...,
|
monitor_pin: builtins.int = ...,
|
||||||
detection_triggered_high: builtins.bool = ...,
|
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType = ...,
|
||||||
use_pullup: builtins.bool = ...,
|
use_pullup: builtins.bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def ClearField(self, field_name: typing.Literal["detection_triggered_high", b"detection_triggered_high", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ...
|
def ClearField(self, field_name: typing.Literal["detection_trigger_type", b"detection_trigger_type", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ...
|
||||||
|
|
||||||
@typing.final
|
@typing.final
|
||||||
class AudioConfig(google.protobuf.message.Message):
|
class AudioConfig(google.protobuf.message.Message):
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from meshtastic.protobuf import portnums_pb2, remote_hardware_pb2
|
|||||||
from meshtastic.util import our_exit
|
from meshtastic.util import our_exit
|
||||||
|
|
||||||
|
|
||||||
def onGPIOreceive(packet, interface):
|
def onGPIOreceive(packet, interface) -> None:
|
||||||
"""Callback for received GPIO responses"""
|
"""Callback for received GPIO responses"""
|
||||||
logging.debug(f"packet:{packet} interface:{interface}")
|
logging.debug(f"packet:{packet} interface:{interface}")
|
||||||
gpioValue = 0
|
gpioValue = 0
|
||||||
@@ -37,7 +37,7 @@ class RemoteHardwareClient:
|
|||||||
code for how you can connect to your own custom meshtastic services
|
code for how you can connect to your own custom meshtastic services
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, iface):
|
def __init__(self, iface) -> None:
|
||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
""" Serial interface class
|
""" Serial interface class
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from typing import Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import serial # type: ignore[import-untyped]
|
import serial # type: ignore[import-untyped]
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ if platform.system() != "Windows":
|
|||||||
class SerialInterface(StreamInterface):
|
class SerialInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a serial link"""
|
"""Interface class for meshtastic devices over a serial link"""
|
||||||
|
|
||||||
def __init__(self, devPath: Optional[str]=None, debugOut=None, noProto=False, connectNow=True, noNodes: bool=False):
|
def __init__(self, devPath: Optional[str]=None, debugOut=None, noProto: bool=False, connectNow: bool=True, noNodes: bool=False) -> None:
|
||||||
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
||||||
find one Meshtastic device by probing
|
find one Meshtastic device by probing
|
||||||
|
|
||||||
@@ -31,13 +32,13 @@ class SerialInterface(StreamInterface):
|
|||||||
self.devPath: Optional[str] = devPath
|
self.devPath: Optional[str] = devPath
|
||||||
|
|
||||||
if self.devPath is None:
|
if self.devPath is None:
|
||||||
ports = meshtastic.util.findPorts(True)
|
ports: List[str] = meshtastic.util.findPorts(True)
|
||||||
logging.debug(f"ports:{ports}")
|
logging.debug(f"ports:{ports}")
|
||||||
if len(ports) == 0:
|
if len(ports) == 0:
|
||||||
print("No Serial Meshtastic device detected, attempting TCP connection on localhost.")
|
print("No Serial Meshtastic device detected, attempting TCP connection on localhost.")
|
||||||
return
|
return
|
||||||
elif len(ports) > 1:
|
elif len(ports) > 1:
|
||||||
message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
|
message: str = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
|
||||||
message += f" Ports detected:{ports}"
|
message += f" Ports detected:{ports}"
|
||||||
meshtastic.util.our_exit(message)
|
meshtastic.util.our_exit(message)
|
||||||
else:
|
else:
|
||||||
@@ -58,14 +59,14 @@ class SerialInterface(StreamInterface):
|
|||||||
self.stream = serial.Serial(
|
self.stream = serial.Serial(
|
||||||
self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0
|
||||||
)
|
)
|
||||||
self.stream.flush()
|
self.stream.flush() # type: ignore[attr-defined]
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
StreamInterface.__init__(
|
StreamInterface.__init__(
|
||||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
||||||
)
|
)
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
if self.stream: # Stream can be null if we were already closed
|
if self.stream: # Stream can be null if we were already closed
|
||||||
self.stream.flush() # FIXME: why are there these two flushes with 100ms sleeps? This shouldn't be necessary
|
self.stream.flush() # FIXME: why are there these two flushes with 100ms sleeps? This shouldn't be necessary
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
"""Stream Interface base class
|
"""Stream Interface base class
|
||||||
"""
|
"""
|
||||||
|
import io
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from typing import Optional, cast
|
||||||
|
|
||||||
import serial # type: ignore[import-untyped]
|
import serial # type: ignore[import-untyped]
|
||||||
|
|
||||||
from meshtastic.mesh_interface import MeshInterface
|
from meshtastic.mesh_interface import MeshInterface
|
||||||
@@ -19,7 +22,7 @@ MAX_TO_FROM_RADIO_SIZE = 512
|
|||||||
class StreamInterface(MeshInterface):
|
class StreamInterface(MeshInterface):
|
||||||
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
||||||
|
|
||||||
def __init__(self, debugOut=None, noProto=False, connectNow=True, noNodes=False):
|
def __init__(self, debugOut: Optional[io.TextIOWrapper]=None, noProto: bool=False, connectNow: bool=True, noNodes: bool=False) -> None:
|
||||||
"""Constructor, opens a connection to self.stream
|
"""Constructor, opens a connection to self.stream
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
@@ -35,6 +38,7 @@ class StreamInterface(MeshInterface):
|
|||||||
raise Exception( # pylint: disable=W0719
|
raise Exception( # pylint: disable=W0719
|
||||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
|
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
|
||||||
)
|
)
|
||||||
|
self.stream: Optional[serial.Serial] # only serial uses this, TCPInterface overrides the relevant methods instead
|
||||||
self._rxBuf = bytes() # empty
|
self._rxBuf = bytes() # empty
|
||||||
self._wantExit = False
|
self._wantExit = False
|
||||||
|
|
||||||
@@ -52,7 +56,7 @@ class StreamInterface(MeshInterface):
|
|||||||
if not noProto:
|
if not noProto:
|
||||||
self.waitForConfig()
|
self.waitForConfig()
|
||||||
|
|
||||||
def connect(self):
|
def connect(self) -> None:
|
||||||
"""Connect to our radio
|
"""Connect to our radio
|
||||||
|
|
||||||
Normally this is called automatically by the constructor, but if you
|
Normally this is called automatically by the constructor, but if you
|
||||||
@@ -63,7 +67,7 @@ class StreamInterface(MeshInterface):
|
|||||||
# if the reading statemachine was parsing a bad packet make sure
|
# if the reading statemachine was parsing a bad packet make sure
|
||||||
# we write enough start bytes to force it to resync (we don't use START1
|
# we write enough start bytes to force it to resync (we don't use START1
|
||||||
# because we want to ensure it is looking for START1)
|
# because we want to ensure it is looking for START1)
|
||||||
p = bytearray([START2] * 32)
|
p: bytes = bytearray([START2] * 32)
|
||||||
self._writeBytes(p)
|
self._writeBytes(p)
|
||||||
time.sleep(0.1) # wait 100ms to give device time to start running
|
time.sleep(0.1) # wait 100ms to give device time to start running
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ class StreamInterface(MeshInterface):
|
|||||||
if not self.noProto: # Wait for the db download if using the protocol
|
if not self.noProto: # Wait for the db download if using the protocol
|
||||||
self._waitConnected()
|
self._waitConnected()
|
||||||
|
|
||||||
def _disconnected(self):
|
def _disconnected(self) -> None:
|
||||||
"""We override the superclass implementation to close our port"""
|
"""We override the superclass implementation to close our port"""
|
||||||
MeshInterface._disconnected(self)
|
MeshInterface._disconnected(self)
|
||||||
|
|
||||||
@@ -86,7 +90,7 @@ class StreamInterface(MeshInterface):
|
|||||||
# pylint: disable=W0201
|
# pylint: disable=W0201
|
||||||
self.stream = None
|
self.stream = None
|
||||||
|
|
||||||
def _writeBytes(self, b):
|
def _writeBytes(self, b: bytes) -> None:
|
||||||
"""Write an array of bytes to our stream and flush"""
|
"""Write an array of bytes to our stream and flush"""
|
||||||
if self.stream: # ignore writes when stream is closed
|
if self.stream: # ignore writes when stream is closed
|
||||||
self.stream.write(b)
|
self.stream.write(b)
|
||||||
@@ -98,24 +102,24 @@ class StreamInterface(MeshInterface):
|
|||||||
# we sleep here to give the TBeam a chance to work
|
# we sleep here to give the TBeam a chance to work
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def _readBytes(self, length):
|
def _readBytes(self, length) -> Optional[bytes]:
|
||||||
"""Read an array of bytes from our stream"""
|
"""Read an array of bytes from our stream"""
|
||||||
if self.stream:
|
if self.stream:
|
||||||
return self.stream.read(length)
|
return self.stream.read(length)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _sendToRadioImpl(self, toRadio):
|
def _sendToRadioImpl(self, toRadio) -> None:
|
||||||
"""Send a ToRadio protobuf to the device"""
|
"""Send a ToRadio protobuf to the device"""
|
||||||
logging.debug(f"Sending: {stripnl(toRadio)}")
|
logging.debug(f"Sending: {stripnl(toRadio)}")
|
||||||
b = toRadio.SerializeToString()
|
b: bytes = toRadio.SerializeToString()
|
||||||
bufLen = len(b)
|
bufLen: int = len(b)
|
||||||
# We convert into a string, because the TCP code doesn't work with byte arrays
|
# We convert into a string, because the TCP code doesn't work with byte arrays
|
||||||
header = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
|
header: bytes = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
|
||||||
logging.debug(f"sending header:{header} b:{b}")
|
logging.debug(f"sending header:{header!r} b:{b!r}")
|
||||||
self._writeBytes(header + b)
|
self._writeBytes(header + b)
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
logging.debug("Closing stream")
|
logging.debug("Closing stream")
|
||||||
MeshInterface.close(self)
|
MeshInterface.close(self)
|
||||||
@@ -142,7 +146,7 @@ class StreamInterface(MeshInterface):
|
|||||||
else:
|
else:
|
||||||
self.cur_log_line += utf
|
self.cur_log_line += utf
|
||||||
|
|
||||||
def __reader(self):
|
def __reader(self) -> None:
|
||||||
"""The reader thread that reads bytes from our stream"""
|
"""The reader thread that reads bytes from our stream"""
|
||||||
logging.debug("in __reader()")
|
logging.debug("in __reader()")
|
||||||
empty = bytes()
|
empty = bytes()
|
||||||
@@ -150,13 +154,13 @@ class StreamInterface(MeshInterface):
|
|||||||
try:
|
try:
|
||||||
while not self._wantExit:
|
while not self._wantExit:
|
||||||
# logging.debug("reading character")
|
# logging.debug("reading character")
|
||||||
b = self._readBytes(1)
|
b: Optional[bytes] = self._readBytes(1)
|
||||||
# logging.debug("In reader loop")
|
# logging.debug("In reader loop")
|
||||||
# logging.debug(f"read returned {b}")
|
# logging.debug(f"read returned {b}")
|
||||||
if len(b) > 0:
|
if b is not None and len(cast(bytes, b)) > 0:
|
||||||
c = b[0]
|
c: int = b[0]
|
||||||
# logging.debug(f'c:{c}')
|
# logging.debug(f'c:{c}')
|
||||||
ptr = len(self._rxBuf)
|
ptr: int = len(self._rxBuf)
|
||||||
|
|
||||||
# Assume we want to append this byte, fixme use bytearray instead
|
# Assume we want to append this byte, fixme use bytearray instead
|
||||||
self._rxBuf = self._rxBuf + b
|
self._rxBuf = self._rxBuf + b
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
""" Supported Meshtastic Devices - This is a class and collection of Meshtastic devices.
|
""" Supported Meshtastic Devices - This is a class and collection of Meshtastic devices.
|
||||||
It is used for auto detection as to which device might be connected.
|
It is used for auto detection as to which device might be connected.
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
|
|
||||||
# Goal is to detect which device and port to use from the supported devices
|
# Goal is to detect which device and port to use from the supported devices
|
||||||
# without installing any libraries that are not currently in the python meshtastic library
|
# without installing any libraries that are not currently in the python meshtastic library
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
"""TCPInterface class for interfacing with http endpoint
|
"""TCPInterface class for interfacing with http endpoint
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from typing import Optional
|
from typing import Optional, cast
|
||||||
|
|
||||||
from meshtastic.stream_interface import StreamInterface
|
from meshtastic.stream_interface import StreamInterface
|
||||||
|
|
||||||
|
DEFAULT_TCP_PORT = 4403
|
||||||
|
|
||||||
class TCPInterface(StreamInterface):
|
class TCPInterface(StreamInterface):
|
||||||
"""Interface class for meshtastic devices over a TCP link"""
|
"""Interface class for meshtastic devices over a TCP link"""
|
||||||
@@ -14,9 +16,9 @@ class TCPInterface(StreamInterface):
|
|||||||
self,
|
self,
|
||||||
hostname: str,
|
hostname: str,
|
||||||
debugOut=None,
|
debugOut=None,
|
||||||
noProto=False,
|
noProto: bool=False,
|
||||||
connectNow=True,
|
connectNow: bool=True,
|
||||||
portNumber=4403,
|
portNumber: int=DEFAULT_TCP_PORT,
|
||||||
noNodes:bool=False,
|
noNodes:bool=False,
|
||||||
):
|
):
|
||||||
"""Constructor, opens a connection to a specified IP address/hostname
|
"""Constructor, opens a connection to a specified IP address/hostname
|
||||||
@@ -27,14 +29,16 @@ class TCPInterface(StreamInterface):
|
|||||||
|
|
||||||
self.stream = None
|
self.stream = None
|
||||||
|
|
||||||
self.hostname = hostname
|
self.hostname: str = hostname
|
||||||
self.portNumber = portNumber
|
self.portNumber: int = portNumber
|
||||||
|
|
||||||
|
self.socket: Optional[socket.socket] = None
|
||||||
|
|
||||||
if connectNow:
|
if connectNow:
|
||||||
logging.debug(f"Connecting to {hostname}") # type: ignore[str-bytes-safe]
|
logging.debug(f"Connecting to {hostname}") # type: ignore[str-bytes-safe]
|
||||||
server_address = (hostname, portNumber)
|
server_address: tuple[str, int] = (hostname, portNumber)
|
||||||
sock = socket.create_connection(server_address)
|
sock: Optional[socket.socket] = socket.create_connection(server_address)
|
||||||
self.socket: Optional[socket.socket] = sock
|
self.socket = sock
|
||||||
else:
|
else:
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
|
||||||
@@ -42,25 +46,26 @@ class TCPInterface(StreamInterface):
|
|||||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
||||||
)
|
)
|
||||||
|
|
||||||
def _socket_shutdown(self):
|
def _socket_shutdown(self) -> None:
|
||||||
"""Shutdown the socket.
|
"""Shutdown the socket.
|
||||||
Note: Broke out this line so the exception could be unit tested.
|
Note: Broke out this line so the exception could be unit tested.
|
||||||
"""
|
"""
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
if self.socket: #mian: please check that this should be "if self.socket:"
|
||||||
|
cast(socket.socket, self.socket).shutdown(socket.SHUT_RDWR)
|
||||||
|
|
||||||
def myConnect(self):
|
def myConnect(self) -> None:
|
||||||
"""Connect to socket"""
|
"""Connect to socket"""
|
||||||
server_address = (self.hostname, self.portNumber)
|
server_address: tuple[str, int] = (self.hostname, self.portNumber)
|
||||||
sock = socket.create_connection(server_address)
|
sock: Optional[socket.socket] = socket.create_connection(server_address)
|
||||||
self.socket = sock
|
self.socket = sock
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
logging.debug("Closing TCP stream")
|
logging.debug("Closing TCP stream")
|
||||||
StreamInterface.close(self)
|
StreamInterface.close(self)
|
||||||
# Sometimes the socket read might be blocked in the reader thread.
|
# Sometimes the socket read might be blocked in the reader thread.
|
||||||
# Therefore we force the shutdown by closing the socket here
|
# Therefore we force the shutdown by closing the socket here
|
||||||
self._wantExit = True
|
self._wantExit: bool = True
|
||||||
if not self.socket is None:
|
if not self.socket is None:
|
||||||
try:
|
try:
|
||||||
self._socket_shutdown()
|
self._socket_shutdown()
|
||||||
@@ -68,10 +73,14 @@ class TCPInterface(StreamInterface):
|
|||||||
pass # Ignore errors in shutdown, because we might have a race with the server
|
pass # Ignore errors in shutdown, because we might have a race with the server
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
|
|
||||||
def _writeBytes(self, b):
|
def _writeBytes(self, b: bytes) -> None:
|
||||||
"""Write an array of bytes to our stream and flush"""
|
"""Write an array of bytes to our stream and flush"""
|
||||||
self.socket.send(b)
|
if self.socket:
|
||||||
|
self.socket.send(b)
|
||||||
|
|
||||||
def _readBytes(self, length):
|
def _readBytes(self, length) -> Optional[bytes]:
|
||||||
"""Read an array of bytes from our stream"""
|
"""Read an array of bytes from our stream"""
|
||||||
return self.socket.recv(length)
|
if self.socket:
|
||||||
|
return self.socket.recv(length)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
import io
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from dotmap import DotMap # type: ignore[import-untyped]
|
from dotmap import DotMap # type: ignore[import-untyped]
|
||||||
from pubsub import pub # type: ignore[import-untyped]
|
from pubsub import pub # type: ignore[import-untyped]
|
||||||
@@ -15,19 +18,19 @@ from meshtastic.serial_interface import SerialInterface
|
|||||||
from meshtastic.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"""
|
||||||
interfaces = None
|
interfaces: List = []
|
||||||
|
|
||||||
"""A list of all packets we received while the current test was running"""
|
"""A list of all packets we received while the current test was running"""
|
||||||
receivedPackets = None
|
receivedPackets: Optional[List] = None
|
||||||
|
|
||||||
testsRunning = False
|
testsRunning: bool = False
|
||||||
|
|
||||||
testNumber = 0
|
testNumber: int = 0
|
||||||
|
|
||||||
sendingInterface = None
|
sendingInterface = None
|
||||||
|
|
||||||
|
|
||||||
def onReceive(packet, interface):
|
def onReceive(packet, interface) -> None:
|
||||||
"""Callback invoked when a packet arrives"""
|
"""Callback invoked when a packet arrives"""
|
||||||
if sendingInterface == interface:
|
if sendingInterface == interface:
|
||||||
pass
|
pass
|
||||||
@@ -42,20 +45,20 @@ def onReceive(packet, interface):
|
|||||||
receivedPackets.append(p)
|
receivedPackets.append(p)
|
||||||
|
|
||||||
|
|
||||||
def onNode(node):
|
def onNode(node) -> None:
|
||||||
"""Callback invoked when the node DB changes"""
|
"""Callback invoked when the node DB changes"""
|
||||||
print(f"Node changed: {node}")
|
print(f"Node changed: {node}")
|
||||||
|
|
||||||
|
|
||||||
def subscribe():
|
def subscribe() -> None:
|
||||||
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
|
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
|
||||||
|
|
||||||
pub.subscribe(onNode, "meshtastic.node")
|
pub.subscribe(onNode, "meshtastic.node")
|
||||||
|
|
||||||
|
|
||||||
def testSend(
|
def testSend(
|
||||||
fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False
|
fromInterface, toInterface, isBroadcast: bool=False, asBinary: bool=False, wantAck: bool=False
|
||||||
):
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Sends one test packet between two nodes and then returns success or failure
|
Sends one test packet between two nodes and then returns success or failure
|
||||||
|
|
||||||
@@ -93,16 +96,16 @@ def testSend(
|
|||||||
return False # Failed to send
|
return False # Failed to send
|
||||||
|
|
||||||
|
|
||||||
def runTests(numTests=50, wantAck=False, maxFailures=0):
|
def runTests(numTests: int=50, wantAck: bool=False, maxFailures: int=0) -> bool:
|
||||||
"""Run the tests."""
|
"""Run the tests."""
|
||||||
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
|
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
|
||||||
numFail = 0
|
numFail: int = 0
|
||||||
numSuccess = 0
|
numSuccess: int = 0
|
||||||
for _ in range(numTests):
|
for _ in range(numTests):
|
||||||
# pylint: disable=W0603
|
# pylint: disable=W0603
|
||||||
global testNumber
|
global testNumber
|
||||||
testNumber = testNumber + 1
|
testNumber = testNumber + 1
|
||||||
isBroadcast = True
|
isBroadcast:bool = True
|
||||||
# asBinary=(i % 2 == 0)
|
# asBinary=(i % 2 == 0)
|
||||||
success = testSend(
|
success = testSend(
|
||||||
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck
|
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck
|
||||||
@@ -126,10 +129,10 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def testThread(numTests=50):
|
def testThread(numTests=50) -> bool:
|
||||||
"""Test thread"""
|
"""Test thread"""
|
||||||
logging.info("Found devices, starting tests...")
|
logging.info("Found devices, starting tests...")
|
||||||
result = runTests(numTests, wantAck=True)
|
result: bool = runTests(numTests, wantAck=True)
|
||||||
if result:
|
if result:
|
||||||
# Run another test
|
# Run another test
|
||||||
# Allow a few dropped packets
|
# Allow a few dropped packets
|
||||||
@@ -137,25 +140,25 @@ def testThread(numTests=50):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def onConnection(topic=pub.AUTO_TOPIC):
|
def onConnection(topic=pub.AUTO_TOPIC) -> None:
|
||||||
"""Callback invoked when we connect/disconnect from a radio"""
|
"""Callback invoked when we connect/disconnect from a radio"""
|
||||||
print(f"Connection changed: {topic.getName()}")
|
print(f"Connection changed: {topic.getName()}")
|
||||||
|
|
||||||
|
|
||||||
def openDebugLog(portName):
|
def openDebugLog(portName) -> io.TextIOWrapper:
|
||||||
"""Open the debug log file"""
|
"""Open the debug log file"""
|
||||||
debugname = "log" + portName.replace("/", "_")
|
debugname = "log" + portName.replace("/", "_")
|
||||||
logging.info(f"Writing serial debugging to {debugname}")
|
logging.info(f"Writing serial debugging to {debugname}")
|
||||||
return open(debugname, "w+", buffering=1, encoding="utf8")
|
return open(debugname, "w+", buffering=1, encoding="utf8")
|
||||||
|
|
||||||
|
|
||||||
def testAll(numTests=5):
|
def testAll(numTests: int=5) -> bool:
|
||||||
"""
|
"""
|
||||||
Run a series of tests using devices we can find.
|
Run a series of tests using devices we can find.
|
||||||
This is called from the cli with the "--test" option.
|
This is called from the cli with the "--test" option.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ports = meshtastic.util.findPorts(True)
|
ports: List[str] = meshtastic.util.findPorts(True)
|
||||||
if len(ports) < 2:
|
if len(ports) < 2:
|
||||||
meshtastic.util.our_exit(
|
meshtastic.util.our_exit(
|
||||||
"Warning: Must have at least two devices connected to USB."
|
"Warning: Must have at least two devices connected to USB."
|
||||||
@@ -175,7 +178,7 @@ def testAll(numTests=5):
|
|||||||
)
|
)
|
||||||
|
|
||||||
logging.info("Ports opened, starting test")
|
logging.info("Ports opened, starting test")
|
||||||
result = testThread(numTests)
|
result: bool = testThread(numTests)
|
||||||
|
|
||||||
for i in interfaces:
|
for i in interfaces:
|
||||||
i.close()
|
i.close()
|
||||||
@@ -183,7 +186,7 @@ def testAll(numTests=5):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def testSimulator():
|
def testSimulator() -> None:
|
||||||
"""
|
"""
|
||||||
Assume that someone has launched meshtastic-native as a simulated node.
|
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
|
Talk to that node over TCP, do some operations and if they are successful
|
||||||
@@ -195,7 +198,7 @@ def testSimulator():
|
|||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logging.info("Connecting to simulator on localhost!")
|
logging.info("Connecting to simulator on localhost!")
|
||||||
try:
|
try:
|
||||||
iface = TCPInterface("localhost")
|
iface: meshtastic.tcp_interface.TCPInterface = TCPInterface("localhost")
|
||||||
iface.showInfo()
|
iface.showInfo()
|
||||||
iface.localNode.showInfo()
|
iface.localNode.showInfo()
|
||||||
iface.localNode.exitSimulator()
|
iface.localNode.exitSimulator()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Meshtastic unit tests for __main__.py"""
|
"""Meshtastic unit tests for __main__.py"""
|
||||||
# pylint: disable=C0302,W0613
|
# pylint: disable=C0302,W0613,R0917
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@@ -726,8 +726,8 @@ def test_main_sendtext_with_dest(mock_findPorts, mock_serial, mocked_open, mock_
|
|||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
@pytest.mark.usefixtures("reset_mt_config")
|
||||||
def test_main_removeposition_invalid(capsys):
|
def test_main_removeposition_remote(capsys):
|
||||||
"""Test --remove-position with an invalid dest"""
|
"""Test --remove-position with a remote dest"""
|
||||||
sys.argv = ["", "--remove-position", "--dest", "!12345678"]
|
sys.argv = ["", "--remove-position", "--dest", "!12345678"]
|
||||||
mt_config.args = sys.argv
|
mt_config.args = sys.argv
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
@@ -735,14 +735,15 @@ def test_main_removeposition_invalid(capsys):
|
|||||||
main()
|
main()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r"Connected to radio", out, re.MULTILINE)
|
assert re.search(r"Connected to radio", out, re.MULTILINE)
|
||||||
assert re.search(r"remote nodes is not supported", out, re.MULTILINE)
|
assert re.search(r"Removing fixed position and disabling fixed position setting", out, re.MULTILINE)
|
||||||
|
assert re.search(r"Waiting for an acknowledgment from remote node", out, re.MULTILINE)
|
||||||
assert err == ""
|
assert err == ""
|
||||||
mo.assert_called()
|
mo.assert_called()
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@pytest.mark.usefixtures("reset_mt_config")
|
@pytest.mark.usefixtures("reset_mt_config")
|
||||||
def test_main_setlat_invalid(capsys):
|
def test_main_setlat_remote(capsys):
|
||||||
"""Test --setlat with an invalid dest"""
|
"""Test --setlat with a remote dest"""
|
||||||
sys.argv = ["", "--setlat", "37.5", "--dest", "!12345678"]
|
sys.argv = ["", "--setlat", "37.5", "--dest", "!12345678"]
|
||||||
mt_config.args = sys.argv
|
mt_config.args = sys.argv
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
@@ -750,7 +751,8 @@ def test_main_setlat_invalid(capsys):
|
|||||||
main()
|
main()
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r"Connected to radio", out, re.MULTILINE)
|
assert re.search(r"Connected to radio", out, re.MULTILINE)
|
||||||
assert re.search(r"remote nodes is not supported", out, re.MULTILINE)
|
assert re.search(r"Setting device position and enabling fixed position setting", out, re.MULTILINE)
|
||||||
|
assert re.search(r"Waiting for an acknowledgment from remote node", out, re.MULTILINE)
|
||||||
assert err == ""
|
assert err == ""
|
||||||
mo.assert_called()
|
mo.assert_called()
|
||||||
|
|
||||||
@@ -769,7 +771,7 @@ def test_main_removeposition(capsys):
|
|||||||
mocked_node.removeFixedPosition.side_effect = mock_removeFixedPosition
|
mocked_node.removeFixedPosition.side_effect = mock_removeFixedPosition
|
||||||
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
iface.localNode = mocked_node
|
iface.getNode.return_value = mocked_node
|
||||||
|
|
||||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||||
main()
|
main()
|
||||||
@@ -796,7 +798,7 @@ def test_main_setlat(capsys):
|
|||||||
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
|
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
|
||||||
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
iface.localNode = mocked_node
|
iface.getNode.return_value = mocked_node
|
||||||
|
|
||||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||||
main()
|
main()
|
||||||
@@ -825,7 +827,7 @@ def test_main_setlon(capsys):
|
|||||||
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
|
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
|
||||||
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
iface.localNode = mocked_node
|
iface.getNode.return_value = mocked_node
|
||||||
|
|
||||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||||
main()
|
main()
|
||||||
@@ -854,7 +856,7 @@ def test_main_setalt(capsys):
|
|||||||
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
|
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
|
||||||
|
|
||||||
iface = MagicMock(autospec=SerialInterface)
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
iface.localNode = mocked_node
|
iface.getNode.return_value = mocked_node
|
||||||
|
|
||||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -173,7 +173,23 @@ def test_getNode_not_local_timeout(capsys):
|
|||||||
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()
|
out, err = capsys.readouterr()
|
||||||
assert re.match(r"Error: Timed out waiting for channels", out)
|
assert re.match(r"Timed out trying to retrieve channel info, retrying", out)
|
||||||
|
assert err == ""
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
@pytest.mark.usefixtures("reset_mt_config")
|
||||||
|
def test_getNode_not_local_timeout_attempts(capsys):
|
||||||
|
"""Test getNode not local, simulate timeout"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
anode = MagicMock(autospec=Node)
|
||||||
|
anode.waitForConfig.return_value = False
|
||||||
|
with patch("meshtastic.node.Node", return_value=anode):
|
||||||
|
with pytest.raises(SystemExit) as pytest_wrapped_e:
|
||||||
|
iface.getNode("bar2", requestChannelAttempts=2)
|
||||||
|
assert pytest_wrapped_e.type == SystemExit
|
||||||
|
assert pytest_wrapped_e.value.code == 1
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert out == 'Timed out trying to retrieve channel info, retrying\nError: Timed out waiting for channels, giving up\n'
|
||||||
assert err == ""
|
assert err == ""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -842,6 +842,34 @@ def test_requestChannel_localNode(caplog):
|
|||||||
assert re.search(r"Requesting channel 0", caplog.text, re.MULTILINE)
|
assert re.search(r"Requesting channel 0", caplog.text, re.MULTILINE)
|
||||||
assert not re.search(r"from remote node", caplog.text, re.MULTILINE)
|
assert not re.search(r"from remote node", caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_requestChannels_non_localNode(caplog):
|
||||||
|
"""Test requestChannels() with a starting index of 0"""
|
||||||
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
|
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||||
|
mo.localNode.getChannelByName.return_value = None
|
||||||
|
mo.myInfo.max_channels = 8
|
||||||
|
anode = Node(mo, "bar", noProto=True)
|
||||||
|
anode.partialChannels = ['0']
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
anode.requestChannels(0)
|
||||||
|
assert re.search(f"Requesting channel 0 info from remote node", caplog.text, re.MULTILINE)
|
||||||
|
assert anode.partialChannels == []
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_requestChannels_non_localNode_starting_index(caplog):
|
||||||
|
"""Test requestChannels() with a starting index of non-0"""
|
||||||
|
iface = MagicMock(autospec=SerialInterface)
|
||||||
|
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||||
|
mo.localNode.getChannelByName.return_value = None
|
||||||
|
mo.myInfo.max_channels = 8
|
||||||
|
anode = Node(mo, "bar", noProto=True)
|
||||||
|
anode.partialChannels = ['1']
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
anode.requestChannels(3)
|
||||||
|
assert re.search(f"Requesting channel 3 info from remote node", caplog.text, re.MULTILINE)
|
||||||
|
# make sure it hasn't been initialized
|
||||||
|
assert anode.partialChannels == ['1']
|
||||||
|
|
||||||
# @pytest.mark.unit
|
# @pytest.mark.unit
|
||||||
# def test_onResponseRequestCannedMessagePluginMesagePart1(caplog):
|
# def test_onResponseRequestCannedMessagePluginMesagePart1(caplog):
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"""Meshtastic unit tests for serial_interface.py"""
|
"""Meshtastic unit tests for serial_interface.py"""
|
||||||
|
# pylint: disable=R0917
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from unittest.mock import mock_open, patch
|
from unittest.mock import mock_open, patch
|
||||||
|
|||||||
@@ -649,4 +649,17 @@ def test_fuzz_fromStr(valstr):
|
|||||||
int(valstr)
|
int(valstr)
|
||||||
assert isinstance(result, int)
|
assert isinstance(result, int)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
assert isinstance(result, str)
|
try:
|
||||||
|
float(valstr)
|
||||||
|
assert isinstance(result, float)
|
||||||
|
except ValueError:
|
||||||
|
assert isinstance(result, str)
|
||||||
|
|
||||||
|
def test_shorthex():
|
||||||
|
"""Test the shortest hex string representations"""
|
||||||
|
result = fromStr('0x0')
|
||||||
|
assert result == b'\x00'
|
||||||
|
result = fromStr('0x5')
|
||||||
|
assert result == b'\x05'
|
||||||
|
result = fromStr('0xffff')
|
||||||
|
assert result == b'\xff\xff'
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class Tunnel:
|
|||||||
self.message = message
|
self.message = message
|
||||||
super().__init__(self.message)
|
super().__init__(self.message)
|
||||||
|
|
||||||
def __init__(self, iface, subnet="10.115", netmask="255.255.0.0"):
|
def __init__(self, iface, subnet: str="10.115", netmask: str="255.255.0.0") -> None:
|
||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from typing import List, NoReturn, Union
|
from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
from google.protobuf.json_format import MessageToJson
|
from google.protobuf.json_format import MessageToJson
|
||||||
from google.protobuf.message import Message
|
from google.protobuf.message import Message
|
||||||
@@ -31,7 +31,7 @@ from meshtastic.version import get_active_version
|
|||||||
0925 Lakeview Research Saleae Logic (logic analyzer)
|
0925 Lakeview Research Saleae Logic (logic analyzer)
|
||||||
04b4:602a Cypress Semiconductor Corp. Hantek DSO-6022BL (oscilloscope)
|
04b4:602a Cypress Semiconductor Corp. Hantek DSO-6022BL (oscilloscope)
|
||||||
"""
|
"""
|
||||||
blacklistVids = dict.fromkeys([0x1366, 0x0483, 0x1915, 0x0925, 0x04b4])
|
blacklistVids: Dict = dict.fromkeys([0x1366, 0x0483, 0x1915, 0x0925, 0x04b4])
|
||||||
|
|
||||||
"""Some devices are highly likely to be meshtastic.
|
"""Some devices are highly likely to be meshtastic.
|
||||||
0x239a RAK4631
|
0x239a RAK4631
|
||||||
@@ -39,21 +39,21 @@ blacklistVids = dict.fromkeys([0x1366, 0x0483, 0x1915, 0x0925, 0x04b4])
|
|||||||
whitelistVids = dict.fromkeys([0x239a, 0x303a])
|
whitelistVids = dict.fromkeys([0x239a, 0x303a])
|
||||||
|
|
||||||
|
|
||||||
def quoteBooleans(a_string):
|
def quoteBooleans(a_string: str) -> str:
|
||||||
"""Quote booleans
|
"""Quote booleans
|
||||||
given a string that contains ": true", replace with ": 'true'" (or false)
|
given a string that contains ": true", replace with ": 'true'" (or false)
|
||||||
"""
|
"""
|
||||||
tmp = a_string.replace(": true", ": 'true'")
|
tmp: str = a_string.replace(": true", ": 'true'")
|
||||||
tmp = tmp.replace(": false", ": 'false'")
|
tmp = tmp.replace(": false", ": 'false'")
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
def genPSK256():
|
def genPSK256() -> bytes:
|
||||||
"""Generate a random preshared key"""
|
"""Generate a random preshared key"""
|
||||||
return os.urandom(32)
|
return os.urandom(32)
|
||||||
|
|
||||||
|
|
||||||
def fromPSK(valstr):
|
def fromPSK(valstr: str) -> Any:
|
||||||
"""A special version of fromStr that assumes the user is trying to set a PSK.
|
"""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
|
In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN
|
||||||
"""
|
"""
|
||||||
@@ -70,7 +70,7 @@ def fromPSK(valstr):
|
|||||||
return fromStr(valstr)
|
return fromStr(valstr)
|
||||||
|
|
||||||
|
|
||||||
def fromStr(valstr):
|
def fromStr(valstr: str) -> Any:
|
||||||
"""Try to parse as int, float or bool (and fallback to a string as last resort)
|
"""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)
|
Returns: an int, bool, float, str or byte array (for strings of hex digits)
|
||||||
@@ -78,11 +78,12 @@ def fromStr(valstr):
|
|||||||
Args:
|
Args:
|
||||||
valstr (string): A user provided string
|
valstr (string): A user provided string
|
||||||
"""
|
"""
|
||||||
|
val: Any
|
||||||
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
if len(valstr) == 0: # Treat an emptystring as an empty bytes
|
||||||
val = bytes()
|
val = bytes()
|
||||||
elif valstr.startswith("0x"):
|
elif valstr.startswith("0x"):
|
||||||
# if needed convert to string with asBytes.decode('utf-8')
|
# if needed convert to string with asBytes.decode('utf-8')
|
||||||
val = bytes.fromhex(valstr[2:])
|
val = bytes.fromhex(valstr[2:].zfill(2))
|
||||||
elif valstr.startswith("base64:"):
|
elif valstr.startswith("base64:"):
|
||||||
val = base64.b64decode(valstr[7:])
|
val = base64.b64decode(valstr[7:])
|
||||||
elif valstr.lower() in {"t", "true", "yes"}:
|
elif valstr.lower() in {"t", "true", "yes"}:
|
||||||
@@ -100,7 +101,15 @@ def fromStr(valstr):
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def pskToString(psk: bytes):
|
|
||||||
|
def toStr(raw_value):
|
||||||
|
"""Convert a value to a string that can be used in a config file"""
|
||||||
|
if isinstance(raw_value, bytes):
|
||||||
|
return "base64:" + base64.b64encode(raw_value).decode("utf-8")
|
||||||
|
return str(raw_value)
|
||||||
|
|
||||||
|
|
||||||
|
def pskToString(psk: bytes) -> str:
|
||||||
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
|
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
|
||||||
if len(psk) == 0:
|
if len(psk) == 0:
|
||||||
return "unencrypted"
|
return "unencrypted"
|
||||||
@@ -122,12 +131,12 @@ def stripnl(s) -> str:
|
|||||||
return " ".join(s.split())
|
return " ".join(s.split())
|
||||||
|
|
||||||
|
|
||||||
def fixme(message):
|
def fixme(message: str) -> None:
|
||||||
"""Raise an exception for things that needs to be fixed"""
|
"""Raise an exception for things that needs to be fixed"""
|
||||||
raise Exception(f"FIXME: {message}") # pylint: disable=W0719
|
raise Exception(f"FIXME: {message}") # pylint: disable=W0719
|
||||||
|
|
||||||
|
|
||||||
def catchAndIgnore(reason, closure):
|
def catchAndIgnore(reason: str, closure) -> None:
|
||||||
"""Call a closure but if it throws an exception print it and continue"""
|
"""Call a closure but if it throws an exception print it and continue"""
|
||||||
try:
|
try:
|
||||||
closure()
|
closure()
|
||||||
@@ -145,7 +154,7 @@ def findPorts(eliminate_duplicates: bool=False) -> List[str]:
|
|||||||
all_ports = serial.tools.list_ports.comports()
|
all_ports = serial.tools.list_ports.comports()
|
||||||
|
|
||||||
# look for 'likely' meshtastic devices
|
# look for 'likely' meshtastic devices
|
||||||
ports = list(
|
ports: List = list(
|
||||||
map(
|
map(
|
||||||
lambda port: port.device,
|
lambda port: port.device,
|
||||||
filter(
|
filter(
|
||||||
@@ -184,12 +193,12 @@ class dotdict(dict):
|
|||||||
class Timeout:
|
class Timeout:
|
||||||
"""Timeout class"""
|
"""Timeout class"""
|
||||||
|
|
||||||
def __init__(self, maxSecs: int=20):
|
def __init__(self, maxSecs: int=20) -> None:
|
||||||
self.expireTime: Union[int, float] = 0
|
self.expireTime: Union[int, float] = 0
|
||||||
self.sleepInterval: float = 0.1
|
self.sleepInterval: float = 0.1
|
||||||
self.expireTimeout: int = maxSecs
|
self.expireTimeout: int = maxSecs
|
||||||
|
|
||||||
def reset(self):
|
def reset(self) -> None:
|
||||||
"""Restart the waitForSet timer"""
|
"""Restart the waitForSet timer"""
|
||||||
self.expireTime = time.time() + self.expireTimeout
|
self.expireTime = time.time() + self.expireTimeout
|
||||||
|
|
||||||
@@ -248,7 +257,7 @@ class Timeout:
|
|||||||
class Acknowledgment:
|
class Acknowledgment:
|
||||||
"A class that records which type of acknowledgment was just received, if any."
|
"A class that records which type of acknowledgment was just received, if any."
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
"""initialize"""
|
"""initialize"""
|
||||||
self.receivedAck = False
|
self.receivedAck = False
|
||||||
self.receivedNak = False
|
self.receivedNak = False
|
||||||
@@ -257,7 +266,7 @@ class Acknowledgment:
|
|||||||
self.receivedTelemetry = False
|
self.receivedTelemetry = False
|
||||||
self.receivedPosition = False
|
self.receivedPosition = False
|
||||||
|
|
||||||
def reset(self):
|
def reset(self) -> None:
|
||||||
"""reset"""
|
"""reset"""
|
||||||
self.receivedAck = False
|
self.receivedAck = False
|
||||||
self.receivedNak = False
|
self.receivedNak = False
|
||||||
@@ -270,18 +279,18 @@ class Acknowledgment:
|
|||||||
class DeferredExecution:
|
class DeferredExecution:
|
||||||
"""A thread that accepts closures to run, and runs them as they are received"""
|
"""A thread that accepts closures to run, and runs them as they are received"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name) -> None:
|
||||||
self.queue = Queue()
|
self.queue: Queue = Queue()
|
||||||
# this thread must be marked as daemon, otherwise it will prevent clients from exiting
|
# this thread must be marked as daemon, otherwise it will prevent clients from exiting
|
||||||
self.thread = threading.Thread(target=self._run, args=(), name=name, daemon=True)
|
self.thread = threading.Thread(target=self._run, args=(), name=name, daemon=True)
|
||||||
self.thread.daemon = True
|
self.thread.daemon = True
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
def queueWork(self, runnable):
|
def queueWork(self, runnable) -> None:
|
||||||
"""Queue up the work"""
|
"""Queue up the work"""
|
||||||
self.queue.put(runnable)
|
self.queue.put(runnable)
|
||||||
|
|
||||||
def _run(self):
|
def _run(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
o = self.queue.get()
|
o = self.queue.get()
|
||||||
@@ -301,7 +310,7 @@ def our_exit(message, return_value=1) -> NoReturn:
|
|||||||
sys.exit(return_value)
|
sys.exit(return_value)
|
||||||
|
|
||||||
|
|
||||||
def support_info():
|
def support_info() -> None:
|
||||||
"""Print out info that helps troubleshooting of the cli."""
|
"""Print out info that helps troubleshooting of the cli."""
|
||||||
print("")
|
print("")
|
||||||
print("If having issues with meshtastic cli or python library")
|
print("If having issues with meshtastic cli or python library")
|
||||||
@@ -330,7 +339,7 @@ def support_info():
|
|||||||
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):
|
def remove_keys_from_dict(keys: Union[Tuple, List, Set], adict: Dict) -> Dict:
|
||||||
"""Return a dictionary without some keys in it.
|
"""Return a dictionary without some keys in it.
|
||||||
Will removed nested keys.
|
Will removed nested keys.
|
||||||
"""
|
"""
|
||||||
@@ -345,33 +354,33 @@ def remove_keys_from_dict(keys, adict):
|
|||||||
return adict
|
return adict
|
||||||
|
|
||||||
|
|
||||||
def hexstr(barray):
|
def hexstr(barray: bytes) -> str:
|
||||||
"""Print a string of hex digits"""
|
"""Print a string of hex digits"""
|
||||||
return ":".join(f"{x:02x}" for x in barray)
|
return ":".join(f"{x:02x}" for x in barray)
|
||||||
|
|
||||||
|
|
||||||
def ipstr(barray):
|
def ipstr(barray: bytes) -> str:
|
||||||
"""Print a string of ip digits"""
|
"""Print a string of ip digits"""
|
||||||
return ".".join(f"{x}" for x in barray)
|
return ".".join(f"{x}" for x in barray)
|
||||||
|
|
||||||
|
|
||||||
def readnet_u16(p, offset):
|
def readnet_u16(p, offset: int) -> int:
|
||||||
"""Read big endian u16 (network byte order)"""
|
"""Read big endian u16 (network byte order)"""
|
||||||
return p[offset] * 256 + p[offset + 1]
|
return p[offset] * 256 + p[offset + 1]
|
||||||
|
|
||||||
|
|
||||||
def convert_mac_addr(val):
|
def convert_mac_addr(val: str) -> Union[str, bytes]:
|
||||||
"""Convert the base 64 encoded value to a mac address
|
"""Convert the base 64 encoded value to a mac address
|
||||||
val - base64 encoded value (ex: '/c0gFyhb'))
|
val - base64 encoded value (ex: '/c0gFyhb'))
|
||||||
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
|
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
|
||||||
"""
|
"""
|
||||||
if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", val):
|
if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", val):
|
||||||
val_as_bytes = base64.b64decode(val)
|
val_as_bytes: bytes = base64.b64decode(val)
|
||||||
return hexstr(val_as_bytes)
|
return hexstr(val_as_bytes)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def snake_to_camel(a_string):
|
def snake_to_camel(a_string: str) -> str:
|
||||||
"""convert snake_case to camelCase"""
|
"""convert snake_case to camelCase"""
|
||||||
# split underscore using split
|
# split underscore using split
|
||||||
temp = a_string.split("_")
|
temp = a_string.split("_")
|
||||||
@@ -380,16 +389,16 @@ def snake_to_camel(a_string):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def camel_to_snake(a_string):
|
def camel_to_snake(a_string: str) -> str:
|
||||||
"""convert camelCase to snake_case"""
|
"""convert camelCase to snake_case"""
|
||||||
return "".join(["_" + i.lower() if i.isupper() else i for i in a_string]).lstrip(
|
return "".join(["_" + i.lower() if i.isupper() else i for i in a_string]).lstrip(
|
||||||
"_"
|
"_"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def detect_supported_devices():
|
def detect_supported_devices() -> Set:
|
||||||
"""detect supported devices based on vendor id"""
|
"""detect supported devices based on vendor id"""
|
||||||
system = platform.system()
|
system: str = platform.system()
|
||||||
# print(f'system:{system}')
|
# print(f'system:{system}')
|
||||||
|
|
||||||
possible_devices = set()
|
possible_devices = set()
|
||||||
@@ -447,9 +456,9 @@ def detect_supported_devices():
|
|||||||
return possible_devices
|
return possible_devices
|
||||||
|
|
||||||
|
|
||||||
def detect_windows_needs_driver(sd, print_reason=False):
|
def detect_windows_needs_driver(sd, print_reason=False) -> bool:
|
||||||
"""detect if Windows user needs to install driver for a supported device"""
|
"""detect if Windows user needs to install driver for a supported device"""
|
||||||
need_to_install_driver = False
|
need_to_install_driver: bool = False
|
||||||
|
|
||||||
if sd:
|
if sd:
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
@@ -475,7 +484,7 @@ def detect_windows_needs_driver(sd, print_reason=False):
|
|||||||
return need_to_install_driver
|
return need_to_install_driver
|
||||||
|
|
||||||
|
|
||||||
def eliminate_duplicate_port(ports):
|
def eliminate_duplicate_port(ports: List) -> List:
|
||||||
"""Sometimes we detect 2 serial ports, but we really only need to use one of the ports.
|
"""Sometimes we detect 2 serial ports, but we really only need to use one of the ports.
|
||||||
|
|
||||||
ports is a list of ports
|
ports is a list of ports
|
||||||
@@ -508,9 +517,9 @@ def eliminate_duplicate_port(ports):
|
|||||||
return new_ports
|
return new_ports
|
||||||
|
|
||||||
|
|
||||||
def is_windows11():
|
def is_windows11() -> bool:
|
||||||
"""Detect if Windows 11"""
|
"""Detect if Windows 11"""
|
||||||
is_win11 = False
|
is_win11: bool = False
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
if float(platform.release()) >= 10.0:
|
if float(platform.release()) >= 10.0:
|
||||||
patch = platform.version().split(".")[2]
|
patch = platform.version().split(".")[2]
|
||||||
@@ -524,7 +533,7 @@ def is_windows11():
|
|||||||
return is_win11
|
return is_win11
|
||||||
|
|
||||||
|
|
||||||
def get_unique_vendor_ids():
|
def get_unique_vendor_ids() -> Set[str]:
|
||||||
"""Return a set of unique vendor ids"""
|
"""Return a set of unique vendor ids"""
|
||||||
vids = set()
|
vids = set()
|
||||||
for d in supported_devices:
|
for d in supported_devices:
|
||||||
@@ -533,7 +542,7 @@ def get_unique_vendor_ids():
|
|||||||
return vids
|
return vids
|
||||||
|
|
||||||
|
|
||||||
def get_devices_with_vendor_id(vid):
|
def get_devices_with_vendor_id(vid: str) -> Set: #Set[SupportedDevice]
|
||||||
"""Return a set of unique devices with the vendor id"""
|
"""Return a set of unique devices with the vendor id"""
|
||||||
sd = set()
|
sd = set()
|
||||||
for d in supported_devices:
|
for d in supported_devices:
|
||||||
@@ -542,11 +551,11 @@ def get_devices_with_vendor_id(vid):
|
|||||||
return sd
|
return sd
|
||||||
|
|
||||||
|
|
||||||
def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
|
def active_ports_on_supported_devices(sds, eliminate_duplicates=False) -> Set[str]:
|
||||||
"""Return a set of active ports based on the supplied supported devices"""
|
"""Return a set of active ports based on the supplied supported devices"""
|
||||||
ports = set()
|
ports: Set = set()
|
||||||
baseports = set()
|
baseports: Set = set()
|
||||||
system = platform.system()
|
system: str = platform.system()
|
||||||
|
|
||||||
# figure out what possible base ports there are
|
# figure out what possible base ports there are
|
||||||
for d in sds:
|
for d in sds:
|
||||||
@@ -604,13 +613,13 @@ def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
|
|||||||
for com_port in com_ports:
|
for com_port in com_ports:
|
||||||
ports.add(com_port)
|
ports.add(com_port)
|
||||||
if eliminate_duplicates:
|
if eliminate_duplicates:
|
||||||
ports = eliminate_duplicate_port(list(ports))
|
portlist: List = eliminate_duplicate_port(list(ports))
|
||||||
ports.sort()
|
portlist.sort()
|
||||||
ports = set(ports)
|
ports = set(portlist)
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
|
|
||||||
def detect_windows_port(sd):
|
def detect_windows_port(sd) -> Set[str]: #"sd" is a SupportedDevice from meshtastic.supported_device
|
||||||
"""detect if Windows port"""
|
"""detect if Windows port"""
|
||||||
ports = set()
|
ports = set()
|
||||||
|
|
||||||
@@ -635,20 +644,26 @@ def detect_windows_port(sd):
|
|||||||
return ports
|
return ports
|
||||||
|
|
||||||
|
|
||||||
def check_if_newer_version():
|
def check_if_newer_version() -> Optional[str]:
|
||||||
"""Check pip to see if we are running the latest version."""
|
"""Check pip to see if we are running the latest version."""
|
||||||
pypi_version = None
|
pypi_version: Optional[str] = None
|
||||||
try:
|
try:
|
||||||
url = "https://pypi.org/pypi/meshtastic/json"
|
url: str = "https://pypi.org/pypi/meshtastic/json"
|
||||||
data = requests.get(url, timeout=5).json()
|
data = requests.get(url, timeout=5).json()
|
||||||
pypi_version = data["info"]["version"]
|
pypi_version = data["info"]["version"]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
act_version = get_active_version()
|
act_version = get_active_version()
|
||||||
|
|
||||||
|
if pypi_version is None:
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
parsed_act_version = pkg_version.parse(act_version)
|
parsed_act_version = pkg_version.parse(act_version)
|
||||||
parsed_pypi_version = pkg_version.parse(pypi_version)
|
parsed_pypi_version = pkg_version.parse(pypi_version)
|
||||||
|
#Note: if handed "None" when we can't download the pypi_version,
|
||||||
|
#this gets a TypeError:
|
||||||
|
#"TypeError: expected string or bytes-like object, got 'NoneType'"
|
||||||
|
#Handle that below?
|
||||||
except pkg_version.InvalidVersion:
|
except pkg_version.InvalidVersion:
|
||||||
return pypi_version
|
return pypi_version
|
||||||
|
|
||||||
|
|||||||
2827
poetry.lock
generated
2827
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
Submodule protobufs updated: 0acaec6eff...83c78e26e3
@@ -1,13 +1,13 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "meshtastic"
|
name = "meshtastic"
|
||||||
version = "2.5.0"
|
version = "2.5.4"
|
||||||
description = "Python API & client shell for talking to Meshtastic devices"
|
description = "Python API & client shell for talking to Meshtastic devices"
|
||||||
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9,<3.13" # 3.9 is needed for pandas, bleak requires a max of 3.13 for some reason
|
python = "^3.9,<3.14" # 3.9 is needed for pandas, bleak requires <3.14
|
||||||
pyserial = "^3.5"
|
pyserial = "^3.5"
|
||||||
protobuf = ">=5.26.0"
|
protobuf = ">=5.26.0"
|
||||||
dotmap = "^1.3.30"
|
dotmap = "^1.3.30"
|
||||||
@@ -19,13 +19,14 @@ requests = "^2.31.0"
|
|||||||
pyparsing = "^3.1.2"
|
pyparsing = "^3.1.2"
|
||||||
pyyaml = "^6.0.1"
|
pyyaml = "^6.0.1"
|
||||||
pypubsub = "^4.0.3"
|
pypubsub = "^4.0.3"
|
||||||
bleak = "^0.21.1"
|
bleak = "^0.22.3"
|
||||||
packaging = "^24.0"
|
packaging = "^24.0"
|
||||||
print-color = "^0.4.6"
|
print-color = "^0.4.6"
|
||||||
dash = { version = "^2.17.1", optional = true }
|
dash = { version = "^2.17.1", optional = true }
|
||||||
pytap2 = { version = "^2.3.0", optional = true }
|
pytap2 = { version = "^2.3.0", optional = true }
|
||||||
dash-bootstrap-components = { version = "^1.6.0", optional = true }
|
dash-bootstrap-components = { version = "^1.6.0", optional = true }
|
||||||
pandas = { version = "^2.2.2", optional = true }
|
pandas = { version = "^2.2.2", optional = true }
|
||||||
|
pandas-stubs = { version = "^2.2.2.240603", optional = true }
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
hypothesis = "^6.103.2"
|
hypothesis = "^6.103.2"
|
||||||
@@ -43,7 +44,6 @@ types-requests = "^2.31.0.20240406"
|
|||||||
types-setuptools = "^69.5.0.20240423"
|
types-setuptools = "^69.5.0.20240423"
|
||||||
types-pyyaml = "^6.0.12.20240311"
|
types-pyyaml = "^6.0.12.20240311"
|
||||||
pyarrow-stubs = "^10.0.1.7"
|
pyarrow-stubs = "^10.0.1.7"
|
||||||
pandas-stubs = "^2.2.2.240603"
|
|
||||||
|
|
||||||
[tool.poetry.group.powermon]
|
[tool.poetry.group.powermon]
|
||||||
optional = true
|
optional = true
|
||||||
|
|||||||
Reference in New Issue
Block a user